From 6222e9d8bd7b39d2ed02ddc4b4493fe77fa4ffd3 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 3 Oct 2025 23:28:23 +0900 Subject: [PATCH] revived unicode print function --- assets/disk0/tvdos/TVDOS.SYS | 11 ++- assets/disk0/tvdos/bin/playtav.js | 11 ++- assets/disk0/tvdos/i18n/korean.js | 41 ---------- assets/disk0/tvdos/include/playgui.mjs | 22 +++++- terranmon.txt | 1 + video_encoder/encoder_tav.c | 102 +++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 48 deletions(-) diff --git a/assets/disk0/tvdos/TVDOS.SYS b/assets/disk0/tvdos/TVDOS.SYS index 2297c99..fa9cc28 100644 --- a/assets/disk0/tvdos/TVDOS.SYS +++ b/assets/disk0/tvdos/TVDOS.SYS @@ -1366,12 +1366,12 @@ unicode.getUniprint = (c) => { return unicode.uniprint[k] }} -print = function(str) { +unicode.print = (str) => { if ((typeof str === 'string' || str instanceof String) && str.length > 0) { + let cp = unicode.utf8toCodepoints(str) cp.forEach(c => { let q = unicode.getUniprint(c) - if (q == undefined || !q[0](c)) { con.addch(4) con.curs_right() @@ -1381,6 +1381,13 @@ print = function(str) { } }) } + else { + sys.print(str) + } +} + +unicode.println = (str) => { + unicode.print(str+'\n\n') } Object.freeze(unicode); diff --git a/assets/disk0/tvdos/bin/playtav.js b/assets/disk0/tvdos/bin/playtav.js index 94054b8..c796b82 100644 --- a/assets/disk0/tvdos/bin/playtav.js +++ b/assets/disk0/tvdos/bin/playtav.js @@ -103,6 +103,7 @@ graphics.setPalette(0, 0, 0, 0, 9) function processSubtitlePacket(packetSize) { + // Read subtitle packet data according to SSF format // uint24 index + uint8 opcode + variable arguments @@ -168,11 +169,14 @@ function processSubtitlePacket(packetSize) { // Font upload - read payload length and font data if (remainingBytes >= 3) { // uint16 length + at least 1 byte data let payloadLen = seqread.readShort() + + serial.println(`Uploading ${(opcode == SSF_OP_UPLOAD_LOW_FONT) ? 'low' : 'high'} font rom (${payloadLen} bytes)`) + if (remainingBytes >= payloadLen + 2) { let fontData = seqread.readBytes(payloadLen) // upload font data - for (let i = 0; i < Math.min(payloadLen, 1920); i++) sys.poke(-1300607 - i, sys.peek(fontData + i)) + for (let i = 0; i < Math.min(payloadLen, 1920); i++) sys.poke(-133121 - i, sys.peek(fontData + i)) sys.poke(-1299460, (opcode == SSF_OP_UPLOAD_LOW_FONT) ? 18 : 19) sys.free(fontData) @@ -571,6 +575,8 @@ try { // Read packet header var packetType = seqread.readOneByte() +// serial.println(`Packet ${packetType} at offset ${seqread.getReadCount() - 1}`) + // Try to read next TAV file header if (packetType == TAV_FILE_HEADER_FIRST) { let nextHeader = tryReadNextTAVHeader() @@ -766,6 +772,9 @@ finally { } else { console.log(`Playback failed with error ${errorlevel}`) } + + sys.poke(-1299460, 20) + sys.poke(-1299460, 21) } graphics.setPalette(0, 0, 0, 0, 0) diff --git a/assets/disk0/tvdos/i18n/korean.js b/assets/disk0/tvdos/i18n/korean.js index b3a91c2..7433ac6 100644 --- a/assets/disk0/tvdos/i18n/korean.js +++ b/assets/disk0/tvdos/i18n/korean.js @@ -1,37 +1,3 @@ -let status = 0 -let workarea = sys.malloc(1920) - -// install LOCHRROM -let hangulRomL = files.open("A:/tvdos/i18n/hang_lo.chr") -if (!hangulRomL.exists) { - printerrln("hang_lo.chr not found") - sys.free(workarea) - return status -} -//dma.comToRam(filesystem._toPorts("A")[0], 0, workarea, 1920) -hangulRomL.pread(workarea, 1920, 0) -for (let i = 0; i < 1920; i++) sys.poke(-1300607 - i, sys.peek(workarea + i)) -sys.poke(-1299460, 18) - - -// install HICHRROM -let hangulRomH = files.open("A:/tvdos/i18n/hang_hi.chr") -if (!hangulRomH.exists) { - printerrln("hang_hi.chr not found") - sys.free(workarea) - sys.poke(-1299460, 20) // clean up the crap - return status -} -//dma.comToRam(filesystem._toPorts("A")[0], 0, workarea, 1920) -hangulRomH.pread(workarea, 1920, 0) -for (let i = 0; i < 1920; i++) sys.poke(-1300607 - i, sys.peek(workarea + i)) -sys.poke(-1299460, 19) - - - -sys.free(workarea) - -graphics.setHalfrowMode(true) /* * A character is defined as one of: * 1. [I,x] (Initial only) @@ -222,12 +188,5 @@ if (unicode.uniprint) { } } ]) - - println("조합한글 커널모듈이 로드되었습니다.") - return 0 -} -else { - println("Failed to load Assembly Hangul kernel module: incompatible DOS version") - return 1 } diff --git a/assets/disk0/tvdos/include/playgui.mjs b/assets/disk0/tvdos/include/playgui.mjs index a67b39f..978bda0 100644 --- a/assets/disk0/tvdos/include/playgui.mjs +++ b/assets/disk0/tvdos/include/playgui.mjs @@ -57,6 +57,15 @@ function displayFormattedLine(line) { let i = 0 let inBoldOrItalic = false + let buffer = "" // Accumulate characters for batch printing + + // Helper function to flush the buffer + function flushBuffer() { + if (buffer.length > 0) { + unicode.print(buffer) + buffer = "" + } + } // insert initial padding block con.color_pair(0, 255) @@ -68,27 +77,32 @@ function displayFormattedLine(line) { // Check for opening tags if (line.substring(i, i + 3).toLowerCase() === '' || line.substring(i, i + 3).toLowerCase() === '') { + flushBuffer() // Flush before color change con.color_pair(254, 0) // Switch to white for formatted text inBoldOrItalic = true i += 3 } else if (i < line.length - 3 && (line.substring(i, i + 4).toLowerCase() === '' || line.substring(i, i + 4).toLowerCase() === '')) { + flushBuffer() // Flush before color change con.color_pair(231, 0) // Switch back to yellow for normal text inBoldOrItalic = false i += 4 } else { - // Not a formatting tag, print the character - print(line[i]) + // Not a formatting tag, add to buffer + buffer += line[i] i++ } } else { - // Regular character, print it - print(line[i]) + // Regular character, add to buffer + buffer += line[i] i++ } } + // Flush any remaining buffered text + flushBuffer() + // insert final padding block con.color_pair(0, 255) con.prnch(0xDD) diff --git a/terranmon.txt b/terranmon.txt index 859525c..8c2e2d1 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -810,6 +810,7 @@ When SSF is interleaved with MP2 audio, the payload must be inserted in-between ## Packet Structure uint8 0x30 (packet type) + uint32 Packet Size * SSF Payload (see below) ## SSF Packet Structure diff --git a/video_encoder/encoder_tav.c b/video_encoder/encoder_tav.c index 3feec1d..3748ac5 100644 --- a/video_encoder/encoder_tav.c +++ b/video_encoder/encoder_tav.c @@ -222,6 +222,8 @@ typedef struct tav_encoder_s { char *input_file; char *output_file; char *subtitle_file; + char *fontrom_lo_file; + char *fontrom_hi_file; FILE *output_fp; FILE *mp2_file; FILE *ffmpeg_video_pipe; @@ -602,6 +604,8 @@ static void show_usage(const char *program_name) { printf(" -a, --arate N MP2 audio bitrate in kbps (overrides quality-based audio rate)\n"); printf(" Valid values: 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384\n"); printf(" -S, --subtitles FILE SubRip (.srt) or SAMI (.smi) subtitle file\n"); + printf(" --fontrom-lo FILE Low font ROM file (max 1920 bytes) for internationalized subtitles\n"); + printf(" --fontrom-hi FILE High font ROM file (max 1920 bytes) for internationalized subtitles\n"); printf(" -v, --verbose Verbose output\n"); printf(" -t, --test Test mode: generate solid colour frames\n"); printf(" --lossless Lossless mode: use 5/3 reversible wavelet\n"); @@ -2165,6 +2169,82 @@ static void rgba_to_colour_space_frame(tav_encoder_t *enc, const uint8_t *rgba, free(temp_rgb); } } +// Write font ROM upload packet (SSF format) +static int write_fontrom_packet(FILE *fp, const char *filename, uint8_t opcode) { + if (!filename || !fp) return 0; + + FILE *rom_file = fopen(filename, "rb"); + if (!rom_file) { + fprintf(stderr, "Warning: Could not open font ROM file: %s\n", filename); + return -1; + } + + // Get file size + fseek(rom_file, 0, SEEK_END); + long file_size = ftell(rom_file); + fseek(rom_file, 0, SEEK_SET); + + if (file_size > 1920) { + fprintf(stderr, "Warning: Font ROM file too large (max 1920 bytes): %s\n", filename); + fclose(rom_file); + return -1; + } + + // Read font data + uint8_t *font_data = malloc(file_size); + if (!font_data) { + fprintf(stderr, "Error: Could not allocate memory for font ROM\n"); + fclose(rom_file); + return -1; + } + + size_t bytes_read = fread(font_data, 1, file_size, rom_file); + fclose(rom_file); + + if (bytes_read != file_size) { + fprintf(stderr, "Warning: Could not read entire font ROM file: %s\n", filename); + free(font_data); + return -1; + } + + // Write SSF packet + // Packet type: 0x30 (subtitle/SSF) + fputc(0x30, fp); + + // Calculate packet size: 3 (index) + 1 (opcode) + 2 (length) + file_size + 1 (terminator) + uint32_t packet_size = 3 + 1 + 2 + file_size + 1; + + // Write packet size (uint32, little-endian) + fputc(packet_size & 0xFF, fp); + fputc((packet_size >> 8) & 0xFF, fp); + fputc((packet_size >> 16) & 0xFF, fp); + fputc((packet_size >> 24) & 0xFF, fp); + + // SSF payload: + // uint24 index (3 bytes) - use 0 for font ROM uploads + fputc(0, fp); + fputc(0, fp); + fputc(0, fp); + + // uint8 opcode (0x80 = low font ROM, 0x81 = high font ROM) + fputc(opcode, fp); + + // uint16 payload length (little-endian) + uint16_t payload_len = (uint16_t)file_size; + fputc(payload_len & 0xFF, fp); + fputc((payload_len >> 8) & 0xFF, fp); + + // Font data + fwrite(font_data, 1, file_size, fp); + + // Terminator + fputc(0x00, fp); + + free(font_data); + + printf("Font ROM uploaded: %s (%ld bytes, opcode 0x%02X)\n", filename, file_size, opcode); + return 0; +} // Write TAV file header static int write_tav_header(tav_encoder_t *enc) { @@ -3175,6 +3255,8 @@ int main(int argc, char *argv[]) { {"no-perceptual-tuning", no_argument, 0, 1007}, {"encode-limit", required_argument, 0, 1008}, {"dump-frame", required_argument, 0, 1009}, + {"fontrom-lo", required_argument, 0, 1011}, + {"fontrom-hi", required_argument, 0, 1012}, {"help", no_argument, 0, '?'}, {0, 0, 0, 0} }; @@ -3300,6 +3382,12 @@ int main(int argc, char *argv[]) { case 1009: // --dump-frame debugDumpFrameTarget = atoi(optarg); break; + case 1011: // --fontrom-lo + enc->fontrom_lo_file = strdup(optarg); + break; + case 1012: // --fontrom-hi + enc->fontrom_hi_file = strdup(optarg); + break; case 'a': { int bitrate = atoi(optarg); @@ -3444,6 +3532,18 @@ int main(int argc, char *argv[]) { return 1; } + // Write font ROM packets if provided + if (enc->fontrom_lo_file) { + if (write_fontrom_packet(enc->output_fp, enc->fontrom_lo_file, 0x80) != 0) { + fprintf(stderr, "Warning: Failed to write low font ROM, continuing without it\n"); + } + } + if (enc->fontrom_hi_file) { + if (write_fontrom_packet(enc->output_fp, enc->fontrom_hi_file, 0x81) != 0) { + fprintf(stderr, "Warning: Failed to write high font ROM, continuing without it\n"); + } + } + gettimeofday(&enc->start_time, NULL); if (enc->output_fps != enc->fps) { @@ -3688,6 +3788,8 @@ static void cleanup_encoder(tav_encoder_t *enc) { free(enc->input_file); free(enc->output_file); free(enc->subtitle_file); + free(enc->fontrom_lo_file); + free(enc->fontrom_hi_file); free(enc->frame_rgb[0]); free(enc->frame_rgb[1]); free(enc->current_frame_y);