mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
183 lines
5.1 KiB
C
183 lines
5.1 KiB
C
/**
|
|
* UCF Payload Writer for TAV Files
|
|
* Creates a 4KB UCF cue file for concatenated TAV files
|
|
* Usage: ./create_ucf_payload input.tav output.ucf
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#define UCF_SIZE 4096
|
|
#define TAV_OFFSET_BIAS UCF_SIZE
|
|
#define TAV_MAGIC "\x1FTSVMTA" // Matches both TAV and TAP
|
|
|
|
typedef struct {
|
|
uint8_t magic[8];
|
|
uint8_t version;
|
|
uint16_t width;
|
|
uint16_t height;
|
|
uint8_t fps;
|
|
uint32_t total_frames;
|
|
// ... rest of header fields
|
|
} __attribute__((packed)) TAVHeader;
|
|
|
|
// Write UCF header
|
|
static void write_ucf_header(FILE *out, uint16_t num_cues) {
|
|
uint8_t magic[8] = {0x1F, 'T', 'S', 'V', 'M', 'U', 'C', 'F'};
|
|
uint8_t version = 1;
|
|
uint32_t cue_file_size = UCF_SIZE;
|
|
uint8_t reserved = 0;
|
|
|
|
fwrite(magic, 1, 8, out);
|
|
fwrite(&version, 1, 1, out);
|
|
fwrite(&num_cues, 2, 1, out);
|
|
fwrite(&cue_file_size, 4, 1, out);
|
|
fwrite(&reserved, 1, 1, out);
|
|
}
|
|
|
|
// Write UCF cue element (internal addressing, human+machine interactable)
|
|
static void write_cue_element(FILE *out, uint64_t offset, uint16_t track_num) {
|
|
uint8_t addressing_mode = 0x21; // 0x20 (human) | 0x01 (machine) | 0x02 (internal)
|
|
char name[16];
|
|
snprintf(name, sizeof(name), "Track %d", track_num);
|
|
uint16_t name_len = strlen(name);
|
|
|
|
// Offset with 4KB bias
|
|
uint64_t biased_offset = offset + TAV_OFFSET_BIAS;
|
|
|
|
fwrite(&addressing_mode, 1, 1, out);
|
|
fwrite(&name_len, 2, 1, out);
|
|
fwrite(name, 1, name_len, out);
|
|
|
|
// Write 48-bit (6-byte) offset
|
|
fwrite(&biased_offset, 6, 1, out);
|
|
}
|
|
|
|
// Find all TAV headers in the file
|
|
static int find_tav_headers(FILE *in, uint64_t **offsets_out) {
|
|
uint64_t *offsets = NULL;
|
|
int count = 0;
|
|
int capacity = 16;
|
|
|
|
offsets = malloc(capacity * sizeof(uint64_t));
|
|
if (!offsets) {
|
|
fprintf(stderr, "Error: Memory allocation failed\n");
|
|
return -1;
|
|
}
|
|
|
|
// Seek to beginning
|
|
fseek(in, 0, SEEK_SET);
|
|
|
|
uint64_t pos = 0;
|
|
uint8_t magic[8];
|
|
|
|
while (fread(magic, 1, 8, in) == 8) {
|
|
// Check for TAV magic signature
|
|
if (memcmp(magic, TAV_MAGIC, 7) == 0 && (magic[7] == 'V' || magic[7] == 'P')) {
|
|
// Found TAV header
|
|
if (count >= capacity) {
|
|
capacity *= 2;
|
|
uint64_t *new_offsets = realloc(offsets, capacity * sizeof(uint64_t));
|
|
if (!new_offsets) {
|
|
fprintf(stderr, "Error: Memory reallocation failed\n");
|
|
free(offsets);
|
|
return -1;
|
|
}
|
|
offsets = new_offsets;
|
|
}
|
|
|
|
offsets[count++] = pos;
|
|
printf("Found TAV header at offset: 0x%lX (%lu)\n", pos, pos);
|
|
|
|
// Skip past this header (32 bytes total)
|
|
fseek(in, pos + 32, SEEK_SET);
|
|
pos += 32;
|
|
} else {
|
|
// Move forward by 1 byte for next search
|
|
fseek(in, pos + 1, SEEK_SET);
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
*offsets_out = offsets;
|
|
return count;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc != 3) {
|
|
fprintf(stderr, "Usage: %s <input.tav> <output.ucf>\n", argv[0]);
|
|
fprintf(stderr, "Creates a 4KB UCF payload for concatenated TAV file\n");
|
|
return 1;
|
|
}
|
|
|
|
const char *input_path = argv[1];
|
|
const char *output_path = argv[2];
|
|
|
|
// Open input file
|
|
FILE *in = fopen(input_path, "rb");
|
|
if (!in) {
|
|
fprintf(stderr, "Error: Cannot open input file '%s'\n", input_path);
|
|
return 1;
|
|
}
|
|
|
|
// Find all TAV headers
|
|
uint64_t *offsets = NULL;
|
|
int num_tracks = find_tav_headers(in, &offsets);
|
|
fclose(in);
|
|
|
|
if (num_tracks < 0) {
|
|
fprintf(stderr, "Error: Failed to scan input file\n");
|
|
return 1;
|
|
}
|
|
|
|
if (num_tracks == 0) {
|
|
fprintf(stderr, "Error: No TAV headers found in input file\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("\nFound %d TAV header(s)\n", num_tracks);
|
|
|
|
// Create output UCF file
|
|
FILE *out = fopen(output_path, "wb");
|
|
if (!out) {
|
|
fprintf(stderr, "Error: Cannot create output file '%s'\n", output_path);
|
|
free(offsets);
|
|
return 1;
|
|
}
|
|
|
|
// Write UCF header
|
|
write_ucf_header(out, num_tracks);
|
|
|
|
// Write cue elements
|
|
for (int i = 0; i < num_tracks; i++) {
|
|
write_cue_element(out, offsets[i], i + 1);
|
|
printf("Written cue element: Track %d at offset 0x%lX (biased: 0x%lX)\n",
|
|
i + 1, offsets[i], offsets[i] + TAV_OFFSET_BIAS);
|
|
}
|
|
|
|
// Get current file position
|
|
long current_pos = ftell(out);
|
|
|
|
// Fill remaining space with zeros to reach 4KB
|
|
if (current_pos < UCF_SIZE) {
|
|
size_t remaining = UCF_SIZE - current_pos;
|
|
uint8_t *zeros = calloc(remaining, 1);
|
|
if (zeros) {
|
|
fwrite(zeros, 1, remaining, out);
|
|
free(zeros);
|
|
}
|
|
}
|
|
|
|
fclose(out);
|
|
free(offsets);
|
|
|
|
printf("\nUCF payload created successfully: %s\n", output_path);
|
|
printf("File size: %d bytes (4KB)\n", UCF_SIZE);
|
|
printf("\nTo create seekable TAV file, prepend this UCF to your TAV file:\n");
|
|
printf(" cat %s input.tav > output_seekable.tav\n", output_path);
|
|
|
|
return 0;
|
|
}
|