mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-10 06:54:04 +09:00
UCF reading and writing
This commit is contained in:
@@ -7,7 +7,11 @@
|
||||
const WIDTH = 560
|
||||
const HEIGHT = 448
|
||||
const TAV_MAGIC = [0x1F, 0x54, 0x53, 0x56, 0x4D, 0x54, 0x41, 0x56] // "\x1FTSVM TAV"
|
||||
const UCF_MAGIC = [0x1F, 0x54, 0x53, 0x56, 0x4D, 0x55, 0x43, 0x46] // "\x1FTSVM UCF"
|
||||
const TAV_VERSION = 1 // Initial DWT version
|
||||
const UCF_VERSION = 1
|
||||
const ADDRESSING_EXTERNAL = 0x01
|
||||
const ADDRESSING_INTERNAL = 0x02
|
||||
const SND_BASE_ADDR = audio.getBaseAddr()
|
||||
const pcm = require("pcm")
|
||||
const MP2_FRAME_SIZE = [144,216,252,288,360,432,504,576,720,864,1008,1152,1440,1728]
|
||||
@@ -400,9 +404,13 @@ let stopPlay = false
|
||||
let akku = FRAME_TIME
|
||||
let akku2 = 0.0
|
||||
let currentFileIndex = 1 // Track which file we're playing in concatenated stream
|
||||
let totalFilesProcessed = 0
|
||||
let decoderDbgInfo = {}
|
||||
|
||||
let blockDataPtr = sys.malloc(2377744)
|
||||
|
||||
let cueElements = []
|
||||
|
||||
// Function to try reading next TAV file header at current position
|
||||
function tryReadNextTAVHeader() {
|
||||
// Save current position
|
||||
@@ -422,13 +430,23 @@ function tryReadNextTAVHeader() {
|
||||
|
||||
// Check if it matches TAV magic
|
||||
let isValidTAV = true
|
||||
let isValidUCF = true
|
||||
for (let i = 0; i < newMagic.length; i++) {
|
||||
if (newMagic[i] !== TAV_MAGIC[i+1]) {
|
||||
isValidTAV = false
|
||||
serial.printerr("Header mismatch: got "+newMagic.join())
|
||||
break
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < newMagic.length; i++) {
|
||||
if (newMagic[i] !== UCF_MAGIC[i+1]) {
|
||||
isValidUCF = false
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidTAV && !isValidUCF) {
|
||||
serial.printerr("Header mismatch: got "+newMagic.join())
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
if (isValidTAV) {
|
||||
serial.println("Got next video file")
|
||||
@@ -463,6 +481,67 @@ function tryReadNextTAVHeader() {
|
||||
|
||||
return newHeader
|
||||
}
|
||||
else if (isValidUCF) {
|
||||
serial.println("Got Universal Cue Format")
|
||||
|
||||
// TODO read and store the cue, then proceed to read next TAV packet (should be 0x1F)
|
||||
let version = seqread.readOneByte()
|
||||
if (version !== UCF_VERSION) {
|
||||
serial.println(`Error: Unsupported UCF version: ${version} (expected ${UCF_VERSION})`)
|
||||
return 2
|
||||
}
|
||||
|
||||
let numElements = seqread.readShort()
|
||||
let cueSize = seqread.readInt()
|
||||
seqread.skip(1)
|
||||
|
||||
serial.println(`UCF Version: ${version}, Elements: ${numElements}`)
|
||||
|
||||
// Parse cue elements
|
||||
for (let i = 0; i < numElements; i++) {
|
||||
let element = {}
|
||||
|
||||
element.addressingModeAndIntent = seqread.readOneByte()
|
||||
element.addressingMode = element.addressingModeAndIntent & 15
|
||||
let nameLength = seqread.readShort()
|
||||
element.name = seqread.readString(nameLength)
|
||||
|
||||
if (element.addressingMode === ADDRESSING_EXTERNAL) {
|
||||
let pathLength = seqread.readShort()
|
||||
element.path = seqread.readString(pathLength)
|
||||
serial.println(`Element ${i + 1}: ${element.name} -> ${element.path} (external)`)
|
||||
} else if (element.addressingMode === ADDRESSING_INTERNAL) {
|
||||
// Read 48-bit offset (6 bytes, little endian)
|
||||
let offsetBytes = []
|
||||
for (let j = 0; j < 6; j++) {
|
||||
offsetBytes.push(seqread.readOneByte())
|
||||
}
|
||||
|
||||
element.offset = 0
|
||||
for (let j = 0; j < 6; j++) {
|
||||
element.offset |= (offsetBytes[j] << (j * 8))
|
||||
}
|
||||
|
||||
serial.println(`Element ${i + 1}: ${element.name} -> offset ${element.offset} (internal)`)
|
||||
} else {
|
||||
serial.println(`Error: Unknown addressing mode: ${element.addressingMode}`)
|
||||
return 5
|
||||
}
|
||||
|
||||
cueElements.push(element)
|
||||
}
|
||||
|
||||
// skip zeros
|
||||
let readCount = seqread.getReadCount()
|
||||
serial.println(`Skip to first video (${readCount} -> ${cueSize})`)
|
||||
seqread.skip(cueSize - readCount + 1)
|
||||
currentFileIndex -= 1
|
||||
return tryReadNextTAVHeader()
|
||||
}
|
||||
else {
|
||||
serial.printerr("File not TAV/UCF. Magic: " + newMagic.join())
|
||||
return 7
|
||||
}
|
||||
} catch (e) {
|
||||
serial.printerr(e)
|
||||
|
||||
@@ -477,8 +556,6 @@ function tryReadNextTAVHeader() {
|
||||
// Playback loop - properly adapted from TEV with multi-file support
|
||||
try {
|
||||
let t1 = sys.nanoTime()
|
||||
let totalFilesProcessed = 0
|
||||
let decoderDbgInfo = {}
|
||||
|
||||
while (!stopPlay && seqread.getReadCount() < FILE_LENGTH) {
|
||||
|
||||
@@ -519,8 +596,10 @@ try {
|
||||
// Continue with new file
|
||||
packetType = seqread.readOneByte()
|
||||
}
|
||||
else
|
||||
else {
|
||||
serial.printerr("Header read failed: " + JSON.stringify(nextHeader))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (packetType === TAV_PACKET_SYNC || packetType == TAV_PACKET_SYNC_NTSC) {
|
||||
|
||||
@@ -12,7 +12,7 @@ if (!exec_args[1]) {
|
||||
const interactive = exec_args[2] && exec_args[2].toLowerCase() == "-i"
|
||||
const fullFilePath = _G.shell.resolvePathInput(exec_args[1])
|
||||
|
||||
if (!files.exists(fullFilePath.full)) {
|
||||
if (!files.open(fullFilePath.full).exists) {
|
||||
serial.println(`Error: File not found: ${fullFilePath.full}`)
|
||||
return 2
|
||||
}
|
||||
@@ -147,7 +147,8 @@ let cueElements = []
|
||||
for (let i = 0; i < numElements; i++) {
|
||||
let element = {}
|
||||
|
||||
element.addressingMode = reader.readOneByte()
|
||||
element.addressingModeAndIntent = reader.readOneByte()
|
||||
element.addressingMode = element.addressingModeAndIntent & 15
|
||||
let nameLength = reader.readShort()
|
||||
element.name = reader.readString(nameLength)
|
||||
|
||||
@@ -234,7 +235,7 @@ for (let i = 0; i < cueElements.length; i++) {
|
||||
targetPath = elementPath
|
||||
}
|
||||
|
||||
if (!files.exists(targetPath)) {
|
||||
if (!files.open(targetPath).exists) {
|
||||
serial.println(`Warning: External file not found: ${targetPath}`)
|
||||
continue
|
||||
}
|
||||
@@ -307,7 +308,7 @@ for (let i = 0; i < cueElements.length; i++) {
|
||||
exec_args[1] = targetPath
|
||||
if (playerFile) {
|
||||
let playerPath = `A:\\tvdos\\bin\\${playerFile}.js`
|
||||
if (files.exists(playerPath)) {
|
||||
if (files.open(playerPath).exists) {
|
||||
eval(files.readText(playerPath))
|
||||
} else {
|
||||
serial.println(`Warning: Player not found: ${playerFile}`)
|
||||
@@ -334,7 +335,7 @@ for (let i = 0; i < cueElements.length; i++) {
|
||||
|
||||
// Execute the appropriate player
|
||||
let playerPath = `A:\\tvdos\\bin\\${playerFile}.js`
|
||||
if (!files.exists(playerPath)) {
|
||||
if (!files.open(playerPath).exists) {
|
||||
serial.println(`Warning: Player script not found: ${playerPath}`)
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user