Are there any existing .bam manipulation macros or functions? It might sound a bit crazy, but I am looking into adding and removing existing frames to sequences as well as adding and removing sequences themselves.
My method is as follows:
(1) generate an array of sequences in $BAM_V10_CYCLES_FRAMES as $BAM_V10_CYCLES_FRAMES("0") for sequence0, and so on until $BAM_V10_CYCLES_FRAMES("n") for sequence n.
(2) The entries within the array are strings in the form of frame numbers separated by commas i.e. if $BAM_V10_CYCLES_FRAMES("0") is set to "0,1,2" then that means the first sequence uses frames 0, 1, and 2.
(3) If I wanted to manipulate the frames within the sequences, I would have to modify the strings within the 1d array. i.e. by INNER_PATCH_SAVE and REPLACE_TEXTUALLY.
So I've got 2 macros (not functions). One to generate $BAM_V10_CYCLES_FRAMES, and the other to apply $BAM_V10_CYCLES_FRAMES back into the .bam file.
All works fine until I add or delete frames in a sequence. I haven't even tried adding new sequences yet. I get corrupted .bams whenever I try changing the size of the frame lookup table and all the offsets of the frame data, which is the only thing occouring after the frame lookup table.
I've tried saving a copy of the .bam with the extra frame added in DLTCEP to see how they compare in a hex editor, but the offsets for the frame data seem to be updated correctly, as well as the cycle->frame table. So I'm really, really stumped.
I'll post the code here:
//\\ Get cycle and frame data of a .bam file
// cycles are stored as strings within $BAM_V10_CYCLES_FRAMES
// as $BAM_V10_CYCLES_FRAMES("0") for cycle 0 and so on until $BAM_V10_CYCLES_FRAMES("n") for cycle n
// strings within the array are stored in the form of frame0,frame1,...,framen
DEFINE_PATCH_MACRO "GET_BAM_V10_CYCLES_FRAMES" BEGIN
PATCH_IF ("%SOURCE_FILE%" STRING_MATCHES_REGEXP "^.+\.bam" = 0 OR "%SOURCE_FILE%" STRING_MATCHES_REGEXP "^.+\.bamc" = 0) BEGIN
READ_ASCII 0x0 sg (4) //Signature
PATCH_IF (~%sg%~ STRING_EQUAL_CASE ~BAMC~ = 1) BEGIN
READ_LONG 0x8 dl //Uncompressed data length
DECOMPRESS_REPLACE_FILE 0xc (SOURCE_SIZE - 0xc) dl
END ELSE BEGIN
PATCH_PRINT ~%SOURCE_FILE% is not a compressed BAM.~
END
// frames and cycles number
READ_SHORT 0x0008 frames_num
READ_BYTE 0x000a cycles_num
// Get frames offset
READ_LONG 0x000c frames_off
// Get cycles offset
SET cycles_off = (LONG_AT (0x000c) + frames_num * 12)
// Get frame lookup offset (frames in cycles)
READ_LONG 0x0014 framelookup_off // 00,01,02,03
// Get frame data offset
SET framedata_off = (LONG_AT (frames_off + 0x8) BAND (BNOT BIT31)) // disable compressed bit
// Reset array
PATCH_CLEAR_ARRAY BAM_V10_CYCLES_FRAMES
// Build array of cycles with number of frames in cycle for each cycle entry
FOR (cycle_index = 0; cycle_index < cycles_num; cycle_index += 1) BEGIN // Generate cycles & number of frames
READ_SHORT (cycles_off + cycle_index * 4) $BAM_V10_CYCLES_FRAMES("%cycle_index%") // look at number of frames for current cycle
END
// Replace number of frames in each cycle entry with string containing frames
// in the form of "framenumber:frame0,frame1,frame2,..."
SET cycle_index = 0
SET current_frame_in_cycle = 0
SPRINT cycles_frames ""
FOR (at_framelookup_off = framelookup_off; at_framelookup_off < framedata_off; at_framelookup_off += 2) BEGIN
READ_SHORT at_framelookup_off current_frame // get current frame from frame lookup table
// Reset counter of current frame in cycle if index jumps a cycle
PATCH_IF current_frame_in_cycle > $BAM_V10_CYCLES_FRAMES("%cycle_index%") - 1 BEGIN
SET current_frame_in_cycle = 0 // reset current frame count
INNER_PATCH_SAVE cycles_frames "%cycles_frames%" BEGIN // Remove comma in beginning of string
REPLACE_TEXTUALLY "^," ""
END
SPRINT $BAM_V10_CYCLES_FRAMES("%cycle_index%") "%cycles_frames%" // store frames string for previous cycle
SPRINT cycles_frames "" // reset frames string for next cycle
SET cycle_index += 1 // increment cycle index
END
SPRINT cycles_frames "%cycles_frames%,%current_frame%" // add current frame to frames string
SET current_frame_in_cycle += 1
END
// Store frames of last cycle
INNER_PATCH_SAVE cycles_frames "%cycles_frames%" BEGIN // Remove comma in beginning of string
REPLACE_TEXTUALLY "^," ""
END
SPRINT $BAM_V10_CYCLES_FRAMES("%cycle_index%") "%cycles_frames%"
END
END
//\\ Apply cycle and frame data of a .bam file
// Writes cycle information into the .bam file from array $BAM_V10_CYCLES_FRAMES
DEFINE_PATCH_MACRO "APPLY_BAM_V10_CYCLES_FRAMES" BEGIN
PATCH_IF ("%SOURCE_FILE%" STRING_MATCHES_REGEXP "^.+\.bam" = 0 OR "%SOURCE_FILE%" STRING_MATCHES_REGEXP "^.+\.bamc" = 0) BEGIN
// get frames and cycles number
READ_SHORT 0x0008 frames_num
READ_BYTE 0x000a old_cycles_num
// Get frames offset
READ_LONG 0x000c old_frames_off
// Get cycles offset
SET old_cycles_off = (LONG_AT (0x000c) + frames_num * 12)
// Get palette offset
READ_LONG 0x0010 old_palette_off
// Get frame data offset of first frame
SET old_framedata_off = (LONG_AT (frames_off + 0x8) BAND (BNOT BIT31)) // disable compressed bit
READ_LONG 0x0014 old_framelookup_off // 00,01,02,03
//\\ Check & remove invalid frame references first
PATCH_PHP_EACH "BAM_V10_CYCLES_FRAMES" AS array_index => entry BEGIN
INNER_PATCH_SAVE entry "%entry%" BEGIN
REPLACE_EVALUATE "\([^,; %TAB%]+\)" BEGIN
PATCH_IF NOT IS_AN_INT MATCH1 OR MATCH1 >= frames_num OR MATCH1 < 0 BEGIN
SPRINT MATCH1 "-1" // mark as invalid
END
END "%MATCH1%"
// Remove invalid frames from string
REPLACE_TEXTUALLY "-1," ""
REPLACE_TEXTUALLY ",-1" ""
REPLACE_TEXTUALLY "-1" ""
END
SPRINT $BAM_V10_CYCLES_FRAMES("%array_index%") "%entry%" // Reapply fixed changes
END
//\\ Write data
//\\ Generate cycles table & frame lookup table
SPRINT cycles_table ""
SPRINT frame_lookup_table ""
SET tot_entry_num = 0 // reset count
PATCH_PHP_EACH "BAM_V10_CYCLES_FRAMES" AS array_index => entry BEGIN
SET cycle_entry_num = 0 // reset count in current cycle
// Generate cycles table
INNER_PATCH_SAVE cycles_table "%cycles_table%" BEGIN
INSERT_BYTES (array_index * 4) (4)
WRITE_SHORT (array_index * 4 + 0x2) tot_entry_num
END
// Generate frame lookup table
INNER_PATCH "%entry%" BEGIN
REPLACE_EVALUATE "\([0-9]+\)" BEGIN
INNER_PATCH_SAVE frame_lookup_table "%frame_lookup_table%" BEGIN
INSERT_BYTES (tot_entry_num * 2) (2)
WRITE_SHORT (tot_entry_num * 2) MATCH1
END
SET tot_entry_num += 1
SET cycle_entry_num += 1
END ""
END
// Generate cycles table
INNER_PATCH_SAVE cycles_table "%cycles_table%" BEGIN
WRITE_SHORT (array_index * 4 + 0x0) cycle_entry_num
END
END
// Get cycles number
/*SET new_cycles_num = array_index + 1
SET new_cycles_diff = (new_cycles_num - old_cycles_num)
// If insertion or deletion occours
PATCH_IF new_cycles_num != old_cycles_num BEGIN
WRITE_BYTE 0xa new_cycles_num // update number of cycles
// Update offsets of all frame data in frame entries list
FOR (current_frame = 0; current_frame < tot_entry_num; current_frame += 1) BEGIN
// increment/decrement offset to frame data of current frame while ignoring BIT31 (compressed/uncompressed)
WRITE_LONG (LONG_AT (old_frames_off) + 0x12 * current_frame + 0x8) ((THIS BAND BNOT BIT31) + (new_cycles_diff) * 4) BOR (THIS BOR BIT31)
END
// Update offset of palette table
WRITE_LONG 0x10 (THIS + ((new_cycles_num - old_cycles_num) * 4)) // increment palette lookup table offset
// Update offset of frame lookup table
WRITE_LONG 0x14 (THIS + ((new_cycles_num - old_cycles_num) * 4)) // increment frame lookup table offset
END
// do actual insertion & deletion
PATCH_IF new_cycles_num > old_cycles_num BEGIN
INSERT_BYTES old_cycles_off ((new_cycles_num - old_cycles_num) * 4)
END ELSE
PATCH_IF new_cycles_num < old_cycles_num BEGIN
DELETE_BYTES old_cycles_off ((old_cycles_num - new_cycles_num) * 4)
END*/
// Get new size of frame lookup table
READ_LONG 0x0014 old_framelookup_off // 00,01,02,03
SET old_frame_lookup_size = (old_framedata_off - old_framelookup_off)
SET new_frame_lookup_size = STRING_LENGTH "%frame_lookup_table%"
SET new_frame_lookup_diff = (new_frame_lookup_size - old_frame_lookup_size)
// Increment offsets of frame data in frame entries list
PATCH_IF new_frame_lookup_size != old_frame_lookup_size BEGIN
// Update offsets of all frame data in frame entries list
FOR (current_frame = 0; current_frame < frames_num; current_frame += 1) BEGIN
// increment/decrement offset to frame data of current frame while ignoring BIT31 (compressed/uncompressed)
WRITE_LONG (old_frames_off + 12 * current_frame + 0x8) ((THIS BAND BNOT BIT31) + new_frame_lookup_diff) BOR (THIS BOR BIT31)
END
END
PATCH_IF new_frame_lookup_size > old_frame_lookup_size BEGIN
INSERT_BYTES old_framelookup_off ((new_frame_lookup_size - old_frame_lookup_size) * 2)
END ELSE
PATCH_IF new_frame_lookup_size < old_frame_lookup_size BEGIN
DELETE_BYTES old_framelookup_off ((old_frame_lookup_size - new_frame_lookup_size) * 2)
END
// Write new frame lookup data & update cycles info
WRITE_ASCIIE old_cycles_off "%cycles_table%"
WRITE_ASCIIE old_framelookup_off "%frame_lookup_table%"
END
END
COPY_EXISTING "MMUMSP.bam" "override"
LAUNCH_PATCH_MACRO "GET_BAM_V10_CYCLES_FRAMES"
SPRINT la $BAM_V10_CYCLES_FRAMES("0")
INNER_PATCH_SAVE la "%la%" BEGIN
REPLACE_TEXTUALLY "0,1,2" "0,0,1,2"
END
SPRINT $BAM_V10_CYCLES_FRAMES("0") "%la%"
LAUNCH_PATCH_MACRO "APPLY_BAM_V10_CYCLES_FRAMES"
BUT_ONLY