id3cobol/id3cobol.cbl

328 lines
11 KiB
COBOL

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.
*> Debug flag to enable/disable debug prints
01 WS-DEBUG-MODE PIC 9 VALUE 1.
*> Default filename that can be overridden with command-line argument
01 WS-FILENAME PIC X(255) VALUE "audio.mp3".
01 WS-EOF-FLAG PIC 9 VALUE 0.
88 WS-EOF VALUE 1.
*> Add a stop flag for zero-length frames
01 STOP-READING-FLAG PIC 9 VALUE 0.
88 STOP-READING VALUE 1.
*> Command line argument handling
01 CMD-ARGS.
05 CMD-ARG-COUNT PIC 9(2) COMP-5.
05 CMD-ARG-VALUES PIC X(255) OCCURS 1 TO 10 DEPENDING ON CMD-ARG-COUNT.
*> ID3v2 Header Structure
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
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.
*> Variables for sync-safe integer conversion
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.
*> Variables for zero-based ORD function
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-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".
*> Process command-line arguments
PERFORM PROCESS-COMMAND-LINE
DISPLAY "Analyzing file: " FUNCTION TRIM(WS-FILENAME)
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)
MOVE 0 TO STOP-READING-FLAG. *> Reset the stop reading flag
*> 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 OR
STOP-READING.
READ-FRAMES.
*> 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
*> Check for all null bytes - this is definitely padding
IF ID3-FRAME-ID = LOW-VALUES
DISPLAY "****************************************"
DISPLAY "Found padding (all nulls) - stopping frame reading"
DISPLAY "****************************************"
MOVE 1 TO STOP-READING-FLAG
EXIT PARAGRAPH
END-IF.
*> 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 zero Frame Length - set stop flag and exit
IF WS-FRAME-SIZE = 0
DISPLAY "****************************************"
DISPLAY "Frame length zero - stopping frame reading"
DISPLAY "****************************************"
MOVE 1 TO STOP-READING-FLAG
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.
*> Process command-line arguments to get the filename
PROCESS-COMMAND-LINE.
*> Get argument count and values
ACCEPT CMD-ARG-COUNT FROM ARGUMENT-NUMBER
*> Check if at least one argument was provided
IF CMD-ARG-COUNT > 0
*> Get the first argument (the filename)
ACCEPT CMD-ARG-VALUES(1) FROM ARGUMENT-VALUE
*> If argument isn't empty, use it as filename
IF CMD-ARG-VALUES(1) NOT = SPACES
MOVE CMD-ARG-VALUES(1) TO WS-FILENAME
END-IF
END-IF.