commit 788e96bf08416b658864d78fa8d11471d4462271 Author: Iain William Wiseman Date: Mon May 5 14:40:06 2025 +1200 Initial check diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0dff92a --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Ignore build artifacts +*.o +*.so +*.exe +*.int +*.lst +*.idb +*.log +*.bak +*~ +*.i +*.c +*.c.h +*.c.l.h + +# Ignore compiled executables +id3cobol +zero-ord + +# Ignore temporary files +.DS_Store +*.swp +*.tmp +*.temp + +# Ignore system-specific files +*.core + +# Ignore intermediate .i files + +audio.mp3 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0ccc3a0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "COBOL debugger", + "type": "gdb", + "preLaunchTask": "Build All", + "request": "launch", + "cobcargs": ["-free", "-x", "-g","-debug", "-Wall", "-O0"] , + "group": ["id3cobol.cbl"], + "env": { + "COB_LIBRARY_PATH": "${workspaceFolder}", + "COB_PRE_LOAD": "zero-ord", + } + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..910a7c4 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,62 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build zero-ord module", + "type": "shell", + "command": "cobc", + "args": [ + "-free", + "-m", + "-g", + "-debug", + "-Wall", + "-O0", + "zero-ord.cbl" + ], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Build id3cobol", + "type": "shell", + "command": "cobc", + "args": [ + "-free", + "-x", + "-g", + "-debug", + "-Wall", + "-O0", + "id3cobol.cbl" + ], + "group": "build", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Build All", + "dependsOrder": "sequence", + "dependsOn": [ + "Build zero-ord module", + "Build id3cobol" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6b34510 --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +# Makefile for COBOL project cleanup and building +# Created on: May 4, 2025 + +# Compiler settings +COBC = cobc +COBFLAGS = -x -g -debug -Wall +COBFLAGS_DEBUG = -x -g -debug -Wall -O0 + +# Source files +SOURCES = hello.cob hello.cbl hello2.cbl +C_SOURCES = hello.c hello2.c + +# Executables +EXECS = hello hello2 + +# Default target +all: $(EXECS) + +# Build rules +hello: hello.cob + $(COBC) $(COBFLAGS) -o $@ $< + +hello.cbl.exe: hello.cbl + $(COBC) $(COBFLAGS_DEBUG) -o $@ $< + +hello2: hello2.cbl + $(COBC) $(COBFLAGS) -o $@ $< + +# Clean up intermediate files and executables +clean: + rm -f *.o *.so *.exe *.int *.lst *.idb *.c *.h *.i *.c.h + rm -f id3cobol zero-ord + +# Deep clean (includes all build artifacts) +distclean: clean + rm -f *.log *.bak *~ + +# Debug build with optimizations disabled +debug: COBFLAGS = $(COBFLAGS_DEBUG) +debug: all + +# Help target +help: + @echo "Makefile targets:" + @echo " all - Build all executables (default)" + @echo " clean - Remove intermediate files (.i, .c, .c.h, etc.)" + @echo " distclean - Remove intermediate files and executables" + @echo " debug - Build with full debug options and no optimization" + @echo " hello - Build only hello executable" + @echo " hello2 - Build only hello2 executable" + @echo "" + @echo "Examples:" + @echo " make clean - Clean intermediate files" + @echo " make debug - Build with debugging enabled" + +# Prevent conflicts with files named like our targets +.PHONY: all clean distclean debug help \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..196a48c --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# MP3 ID3 Tag Reader + +![GnuCOBOL](https://img.shields.io/badge/GnuCOBOL-3.2.0%2B-blue) + +![Fun COBOL Image](assets/images/cobol.png) + +This project is a COBOL-based MP3 ID3 tag reader that parses and displays metadata from MP3 files. It uses GnuCOBOL for compilation and execution. + +## Software Requirements + +- **GnuCOBOL**: Version 3.2.0 or later + - Ensure GnuCOBOL is installed and accessible via the `cobc` command. +- **Operating System**: Linux (tested on May 5, 2025) +- **Make**: Version 4.3 or later (for building the project) +- **Git**: Version 2.30.0 or later (for version control) + +## Project Structure + +- `id3cobol.cbl`: Main COBOL program for reading and parsing MP3 ID3 tags. +- `zero-ord.cbl`: COBOL module for zero-based ordinal conversion. +- `Makefile`: Build automation file for compiling and cleaning the project. +- `.gitignore`: Specifies files and directories to be ignored by Git. + +## Build Instructions + +1. **Compile the `zero-ord` module**: + ```bash + make zero-ord + ``` + +2. **Compile the `id3cobol` program**: + ```bash + make id3cobol + ``` + +3. **Clean up build artifacts**: + ```bash + make clean + ``` + +4. **Perform a deep clean (removes all artifacts)**: + ```bash + make distclean + ``` + +## Usage + +Run the compiled `id3cobol` program to parse the `audio.mp3` file: +```bash +./id3cobol +``` + +> **Note**: Ensure you have an `audio.mp3` file in the same directory as the program. This file is required for the program to function. + +## Debugging + +To enable debugging, ensure the `WS-DEBUG-MODE` variable in `id3cobol.cbl` is set to `1`. This will display additional debug information during execution. + +## License + +This project is licensed under the MIT License. See the LICENSE file for details. \ No newline at end of file diff --git a/assets/images/cobol.png b/assets/images/cobol.png new file mode 100644 index 0000000..608ee5d Binary files /dev/null and b/assets/images/cobol.png differ diff --git a/id3cobol.cbl b/id3cobol.cbl new file mode 100644 index 0000000..f2cb21f --- /dev/null +++ b/id3cobol.cbl @@ -0,0 +1,298 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. MP3ID3INFO. + + ENVIRONMENT DIVISION. + INPUT-OUTPUT SECTION. + FILE-CONTROL. + SELECT MP3-FILE ASSIGN TO DYNAMIC WS-FILENAME + ORGANIZATION IS BINARY SEQUENTIAL. + + DATA DIVISION. + FILE SECTION. + FD MP3-FILE. + 01 FILE-RECORD PIC X(1). + + WORKING-STORAGE SECTION. + 01 RecordLength PIC 9(5) COMP-5. + *> Debug flag to enable/disable debug prints + 01 WS-DEBUG-MODE PIC 9 VALUE 1. + + 01 WS-FILENAME PIC X(255) VALUE "audio.mp3". + 01 WS-EOF-FLAG PIC 9 VALUE 0. + 88 WS-EOF VALUE 1. + + *> ID3v2 Header Structure - Use simpler types for debugging + 01 ID3-HEADER. + 05 ID3-TAG PIC X(3). *> Should be "ID3" + 05 ID3-VERSION PIC X(2). *> Version (ID3v2.X) + 05 ID3-FLAGS PIC X(1). *> Flags + 05 ID3-SIZE PIC X(4). *> Sync-safe size + + *> Frame Structure - Explicitly define each element for debugger + 01 ID3-FRAME-HEADER. + 05 ID3-FRAME-ID PIC X(4). *> Frame identifier + 05 ID3-FRAME-SIZE PIC X(4). *> Sync-safe frame size + 05 ID3-FRAME-FLAGS PIC X(2). *> Frame flags + + *> Working variables - Use simpler COMP-5 instead of COMP for better debugger support + 01 WS-TAG-SIZE PIC 9(9) COMP-5. + 01 WS-FRAME-SIZE PIC 9(9) COMP-5. + 01 WS-CURRENT-POS PIC 9(9) COMP-5. + 01 WS-REMAINING-BYTES PIC S9(9) COMP-5. + 01 WS-FILE-POS PIC 9(9) COMP-5 VALUE 1. + 01 WS-I PIC 9(9) COMP-5. + + *> Buffer for frame data - Keep fixed size for easier debugging + 01 WS-FRAME-DATA-SIZE PIC 9(5) COMP-5 VALUE 100000. + 01 WS-FRAME-DATA. + 05 WS-FRAME-CHAR PIC X OCCURS 100000 TIMES. + + *> Variables for sync-safe integer conversion + 01 WS-BYTE PIC X OCCURS 4. + 01 WS-BYTE-VAL PIC 9(3) COMP-5 OCCURS 4. + 01 WS-SHIFT-RESULT PIC 9(9) COMP-5 OCCURS 4. + 01 WS-X-RESULT PIC 9(9) COMP-5 OCCURS 4. + 01 WS-Y-RESULT PIC 9(9). + + 01 WS-NUMBER PIC 9(5) VALUE 21154. + 01 WS-DIVIDEND PIC 9(5). + + 01 WS-BYTE-FIELD. + 05 WS-IW-BYTE-0 PIC X(1). + 05 WS-IW-BYTE-1 PIC X(1). + 05 WS-IW-BYTE-2 PIC X(1). + 05 WS-IW-BYTE-3 PIC X(1). + + 01 SYNC-SAFE-INT-IN. + 05 SYNC-SAFE-INT-1 PIC X(1). + 05 SYNC-SAFE-INT-2 PIC X(1). + 05 SYNC-SAFE-INT-3 PIC X(1). + 05 SYNC-SAFE-INT-4 PIC X(1). + + 01 SYNC-SAFE-INT-OUT PIC 9(9) COMP-5. + + *> Debug display variables - explicit single items for easier debugging + 01 DBG-FRAME-SIZE PIC 9(9) COMP-5. + 01 DBG-ASCII-ID PIC X(4). + 01 DBG-LOOP-COUNT PIC 9(5) COMP-5. + + 01 WS-TEMP-CHAR PIC X(1). + 01 WS-ORD-RESULT PIC 9(5) COMP-5. + + 01 WS-BYTES-TO-READ PIC 9(5) COMP-5. + 01 BYTES-BUFFER PIC X(100000). + 01 WS-TEMP-BYTE PIC X(1). + + 01 WS-SHIFT-21 PIC 9(7) COMP-5 VALUE 2097152. + 01 WS-SHIFT-14 PIC 9(7) COMP-5 VALUE 16384. + 01 WS-SHIFT-7 PIC 9(7) COMP-5 VALUE 128. + 01 WS-SHIFT-0 PIC 9(7) COMP-5 VALUE 1. + + 01 WS-VAL PIC 9(7). + 01 WS-NEW-VAL PIC 9(7). + + LINKAGE SECTION. + 01 LS-CHAR PIC X(1). + 01 LS-ORD-RESULT PIC 9(5) COMP-5. + + PROCEDURE DIVISION. + MAIN-PARA. + DISPLAY "Starting MP3-ID3-INFO program". + + PERFORM OPEN-FILE. + + IF WS-EOF-FLAG = 0 + PERFORM READ-ID3-HEADER + IF ID3-TAG = "ID3" + PERFORM PROCESS-ID3-TAG + ELSE + DISPLAY "No ID3v2 tag found!" + END-IF + END-IF. + + PERFORM CLOSE-FILE. + + DISPLAY "Program execution complete". + STOP RUN. + + OPEN-FILE. + DISPLAY "Opening file: " WS-FILENAME. + OPEN INPUT MP3-FILE. + IF WS-EOF-FLAG = 1 + DISPLAY "Error opening file!" + END-IF. + + CLOSE-FILE. + DISPLAY "Closing file". + CLOSE MP3-FILE. + + *> Read ID3 header + READ-ID3-HEADER. + *> Read ID3 header (10 bytes) + MOVE 10 TO WS-BYTES-TO-READ + PERFORM READ-BYTES + MOVE BYTES-BUFFER(1:10) TO ID3-HEADER. + + PROCESS-ID3-TAG. + *> Get the size which is 4-byte sync-safe integer + MOVE ID3-SIZE TO SYNC-SAFE-INT-IN + PERFORM CONVERT-SYNC-SAFE-INT + MOVE SYNC-SAFE-INT-OUT TO WS-TAG-SIZE + + DISPLAY "ID3v2 Version: " + FUNCTION TRIM(FUNCTION HEX-OF(ID3-VERSION(1:1))) "." + FUNCTION TRIM(FUNCTION HEX-OF(ID3-VERSION(2:1))). + + *> Debug display + IF WS-DEBUG-MODE = 1 + DISPLAY "Tag Size: " WS-TAG-SIZE " bytes" + END-IF + + MOVE 10 TO WS-FILE-POS. *> 10 bytes read so far (header) + + *> Debug line with explicit break opportunity + DISPLAY "Starting to read frames at position " WS-FILE-POS. + + PERFORM READ-FRAMES + UNTIL (WS-FILE-POS >= WS-TAG-SIZE + 10) OR WS-EOF. + + READ-FRAMES. + *> Debug counter for frame reading + ADD 1 TO DBG-LOOP-COUNT + + *> Reset values + MOVE LOW-VALUES TO ID3-FRAME-HEADER. + MOVE 0 TO WS-FRAME-SIZE. + + *> Read frame header (10 bytes) + MOVE 10 TO WS-BYTES-TO-READ + PERFORM READ-BYTES + MOVE BYTES-BUFFER(1:10) TO ID3-FRAME-HEADER + + *> Update file position + ADD 10 TO WS-FILE-POS. + DISPLAY "Current position: " WS-FILE-POS. + + *> Get Frame length + MOVE ID3-FRAME-SIZE TO SYNC-SAFE-INT-IN + PERFORM CONVERT-SYNC-SAFE-INT + MOVE SYNC-SAFE-INT-OUT TO WS-FRAME-SIZE + + IF WS-DEBUG-MODE = 1 + DISPLAY "-------------------------------" + DISPLAY "FRAME HEADER: ID=" + ID3-FRAME-ID + ", SIZE=" WS-FRAME-SIZE + END-IF + + *> Check for invalid Frame Length + IF WS-FRAME-SIZE <= 0 + DISPLAY "Invalid WS-FRAME-SIZE" WS-FRAME-SIZE + EXIT PARAGRAPH + END-IF. + + *> Check for buffer overrun + IF WS-FRAME-SIZE > 100000 + DISPLAY "Frame too large to process: " WS-FRAME-SIZE + PERFORM SKIP-FRAME + ELSE + PERFORM READ-FRAME-DATA + END-IF. + + DISPLAY "Current position: " WS-FILE-POS. + + *> Update file position + ADD WS-FRAME-SIZE TO WS-FILE-POS. + + COMPUTE WS-REMAINING-BYTES = (WS-TAG-SIZE + 10) - WS-FILE-POS. + DISPLAY "Next position: " WS-FILE-POS. + DISPLAY "Remaining: " WS-REMAINING-BYTES. + + SKIP-FRAME. + PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > WS-FRAME-SIZE + READ MP3-FILE + AT END MOVE 1 TO WS-EOF-FLAG + EXIT PERFORM + END-READ + END-PERFORM. + + *> Read the Frame data + *> We ignore pictures + READ-FRAME-DATA. + DISPLAY "Reading frame data: " WS-FRAME-SIZE " bytes". + MOVE LOW-VALUES TO WS-FRAME-DATA. + PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > WS-FRAME-SIZE + *> Add bounds check to prevent index errors + IF WS-I >= 1 AND WS-I <= 100000 + READ MP3-FILE INTO WS-TEMP-BYTE + AT END + MOVE 1 TO WS-EOF-FLAG + EXIT PERFORM + END-READ + MOVE WS-TEMP-BYTE TO WS-FRAME-CHAR(WS-I) + ELSE + DISPLAY "WARNING: Index " WS-I " out of bounds (1-100000)" + EXIT PERFORM + END-IF + END-PERFORM. + + *> Display appropriate message based on frame ID + IF ID3-FRAME-ID = "APIC" + DISPLAY "Frame: " ID3-FRAME-ID + " - Data: [Picture]" + ELSE + DISPLAY "Frame: " ID3-FRAME-ID + " - Data: " FUNCTION TRIM(WS-FRAME-DATA) + END-IF. + + *> Convert sync-safe integer to actual size + *> Implementation of syncSafeToInt function using zero-based ORD + *> Input: SYNC-SAFE-INT-IN (4 bytes) + *> Output: SYNC-SAFE-INT-OUT (integer value) + CONVERT-SYNC-SAFE-INT. + MOVE 0 TO WS-VAL. + + *> Extract the numeric value of each byte + PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 4 + MOVE SYNC-SAFE-INT-IN(WS-I:1) TO WS-TEMP-CHAR + MOVE 0 TO WS-ORD-RESULT + CALL "ZERO-BASED-ORD" USING WS-TEMP-CHAR, WS-ORD-RESULT + MOVE WS-ORD-RESULT TO WS-BYTE-VAL(WS-I) + END-PERFORM + + *> Debug display of input bytes + IF WS-DEBUG-MODE = 1 + DISPLAY "Byte 1: " WS-BYTE-VAL(1) + " (hex: " FUNCTION HEX-OF(SYNC-SAFE-INT-IN(1:1)) ")" + DISPLAY "Byte 2: " WS-BYTE-VAL(2) + " (hex: " FUNCTION HEX-OF(SYNC-SAFE-INT-IN(2:1)) ")" + DISPLAY "Byte 3: " WS-BYTE-VAL(3) + " (hex: " FUNCTION HEX-OF(SYNC-SAFE-INT-IN(3:1)) ")" + DISPLAY "Byte 4: " WS-BYTE-VAL(4) + " (hex: " FUNCTION HEX-OF(SYNC-SAFE-INT-IN(4:1)) ")" + END-IF + + PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 4 + MULTIPLY WS-VAL BY 256 GIVING WS-VAL + MOVE WS-BYTE-VAL(WS-I) TO WS-NEW-VAL + ADD WS-NEW-VAL TO WS-VAL + END-PERFORM + + MOVE WS-VAL TO SYNC-SAFE-INT-OUT + + DISPLAY "Converted value: " SYNC-SAFE-INT-OUT. + + *> Input: WS-BYTES-TO-READ = number of bytes to read + *> Output: BYTES-BUFFER filled with data + READ-BYTES SECTION. + MOVE SPACES TO BYTES-BUFFER + PERFORM VARYING WS-I FROM 1 BY 1 UNTIL + WS-I > WS-BYTES-TO-READ + + READ MP3-FILE INTO WS-TEMP-BYTE + AT END + MOVE 1 TO WS-EOF-FLAG + EXIT PERFORM + END-READ + MOVE WS-TEMP-BYTE TO BYTES-BUFFER(WS-I:1) + END-PERFORM. diff --git a/zero-ord.cbl b/zero-ord.cbl new file mode 100644 index 0000000..cb7e619 --- /dev/null +++ b/zero-ord.cbl @@ -0,0 +1,12 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. ZERO-BASED-ORD. + + DATA DIVISION. + LINKAGE SECTION. + 01 LS-CHAR PIC X(1). + 01 LS-ORD-RESULT PIC 9(5) COMP-5. + + PROCEDURE DIVISION USING LS-CHAR, LS-ORD-RESULT. + COMPUTE LS-ORD-RESULT = FUNCTION ORD(LS-CHAR) - 1. + EXIT PROGRAM. + END PROGRAM ZERO-BASED-ORD. \ No newline at end of file