tsvm mov encoder and decoder

This commit is contained in:
minjaesong
2022-04-12 16:44:14 +09:00
parent 41761289d3
commit 95bfaae1da
8 changed files with 219 additions and 37 deletions

54
assets/disk0/decodemov.js Normal file
View File

@@ -0,0 +1,54 @@
let filename = exec_args[1]
const FBUF_SIZE = 560*448
let status = filesystem.open("A", filename, "R")
if (status) return status
println("Reading...")
let bytes = filesystem.readAllBytes("A")
con.clear()
let readCount = 0
function readBytes(length) {
let ret = new Int8Array(length)
for (let k = 0; k < length; k++) {
ret[k] = bytes[readCount]
readCount += 1
}
return ret
}
function readInt() {
let b = readBytes(4)
return (b[0] & 255) | ((b[1] & 255) << 8) | ((b[2] & 255) << 16) | ((b[3] & 255) << 24)
}
function readShort() {
let b = readBytes(2)
return (b[0] & 255) | ((b[1] & 255) << 8)
}
let magic = readBytes(8)
if (String.fromCharCode.apply(null, magic) != '\x1fTSVMMOV') return 1
let width = readShort()
let height = readShort()
let fps = readShort()
let frameCount = readInt() % 16777216
let fbuf = sys.malloc(FBUF_SIZE)
for (let f = 0; f < frameCount; f++) {
let payloadLen = readInt()
let gzipped = readBytes(payloadLen)
gzip.decompTo(gzipped, fbuf)
dma.ramToFrame(fbuf, 0, FBUF_SIZE)
}
sys.free(fbuf)

64
assets/disk0/encodemov.js Normal file
View File

@@ -0,0 +1,64 @@
const FBUF_SIZE = 560*448
let infile = sys.malloc(120000) // somewhat arbitrary
let imagearea = sys.malloc(FBUF_SIZE*3)
let decodearea = sys.malloc(FBUF_SIZE)
let gzippedImage = sys.malloc(180000) // somewhat arbitrary
let outfilename = exec_args[1]
if (!outfilename) return 1
function appendToOutfile(bytes) {
filesystem.open("A", outfilename, "A")
filesystem.writeBytes("A", bytes)
}
function appendToOutfilePtr(ptr, len) {
filesystem.open("A", outfilename, "A")
dma.ramToCom(ptr, 0, len)
}
// write header to the file
let headerBytes = [
0x1F, 0x54, 0x53, 0x56, 0x4D, 0x4D, 0x4F, 0x56,
0x30, 0x02,
0xC0, 0x01,
0x1E, 0x00,
0x34, 0x00, 0x00, 0x7C
]
filesystem.open("A", outfilename, "W")
filesystem.writeBytes("A", headerBytes)
for (let f = 1; f <=52; f++) {
let fname = `/movtestimg/${(''+f).padStart(3,'0')}.jpg`
filesystem.open("A", fname, "R")
let fileLen = filesystem.getFileLen("A")
dma.comToRam(0, 0, infile, fileLen)
graphics.decodeImageTo(infile, fileLen, imagearea)
print(`Encoding frame ${f}...`)
graphics.imageToDisplayableFormat(imagearea, decodearea, 560, 448, 3, 1)
let gzlen = gzip.compFromTo(decodearea, FBUF_SIZE, gzippedImage)
let frameSize = [
(gzlen >>> 0) & 255,
(gzlen >>> 8) & 255,
(gzlen >>> 16) & 255,
(gzlen >>> 24) & 255
]
appendToOutfile(frameSize)
appendToOutfilePtr(gzippedImage, gzlen)
print(` ${gzlen} bytes\n`)
}
sys.free(infile)
sys.free(imagearea)
sys.free(decodearea)
sys.free(gzippedImage)

View File

@@ -141,7 +141,7 @@ filesystem.write = (driveLetter, string) => {
filesystem._flush(port[0]); filesystem._close(port[0]); filesystem._flush(port[0]); filesystem._close(port[0]);
}; };
filesystem.writeBytes = (driveLetter, bytes) => { filesystem.writeBytes = (driveLetter, bytes) => {
var string = String.fromCharCode(...bytes); var string = String.fromCharCode.apply(null, bytes); // no spreading: has length limit
filesystem.write(driveLetter, string); filesystem.write(driveLetter, string);
}; };
filesystem.isDirectory = (driveLetter) => { filesystem.isDirectory = (driveLetter) => {

View File

@@ -269,4 +269,32 @@ SETPAL 5 (15 2 8 15)
SETBG (15 2 8 15) SETBG (15 2 8 15)
D0·00·F2 8F (0xF28F: RGBA colour) D0·00·F2 8F (0xF28F: RGBA colour)
END (pseudocommand of WAITFOR) END (pseudocommand of WAITFOR)
80·FF FF FF 80·FF FF FF
--------------------------------------------------------------------------------
TSVM MOV file format
Endianness: Little
\x1F T S V M M O V
[METADATA]
[FRAME0]
[FRAME1]
[FRAME2]
...
where:
METADATA -
uint16 WIDTH
uint16 HEIGHT
uint16 FPS (0: play as fast as can)
uint24 NUMBER OF FRAMES
\x7C
FRAME -
uint32 SIZE OF FRAMEDATA
* FRAMEDATA COMPRESSED IN GZIP

View File

@@ -6,41 +6,70 @@ import java.io.ByteArrayOutputStream
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
object CompressorDelegate { class CompressorDelegate(val vm: VM) {
/*fun comp(ba: ByteArray): ByteArray { fun comp(str: String) = Companion.comp(str)
val bin = ByteArrayInputStream(ba) fun comp(ba: ByteArray) = Companion.comp(ba)
val bout = ByteArrayOutputStream(256)
Lzma.compress(bin, bout) fun compFromTo(input: Int, len: Int, output: Int): Int {
return bout.toByteArray() val inbytes = ByteArray(len) { vm.peek(input.toLong() + it)!! }
comp(inbytes).let {
it.forEachIndexed { index, byte ->
vm.poke(output.toLong() + index, byte)
}
return it.size
}
} }
fun decomp(ba: ByteArray): ByteArray { fun compTo(ba: ByteArray, output: Int): Int {
val bin = ByteArrayInputStream(ba) comp(ba).let {
val bout = ByteArrayOutputStream(256) it.forEachIndexed { index, byte ->
Lzma.decompress(bin, bout) vm.poke(output.toLong() + index, byte)
return bout.toByteArray() }
}*/ return it.size
}
fun comp(str: String) = comp(str.toByteArray(VM.CHARSET))
fun comp(ba: ByteArray): ByteArray {
val baos = ByteArrayOutputStream()
val gz = GZIPOutputStream(baos)
gz.write(ba); gz.flush(); gz.finish()
baos.flush(); baos.close()
return baos.toByteArray()
} }
fun decomp(str: String) = decomp(str.toByteArray(VM.CHARSET))
fun decomp(ba: ByteArray): ByteArray { fun decomp(str: String) = Companion.decomp(str)
val bais = ByteArrayInputStream(ba) fun decomp(ba: ByteArray) = Companion.decomp(ba)
val gz = GZIPInputStream(bais)
val ret = gz.readBytes() fun decompTo(str: String, pointer: Int) {
gz.close(); bais.close() val bytes = decomp(str)
return ret bytes.forEachIndexed { index, byte ->
vm.poke(pointer.toLong() + index, byte)
}
} }
val GZIP_HEADER = byteArrayOf(31,-117,8) // .gz in DEFLATE fun decompTo(ba: ByteArray, pointer: Int) {
val bytes = decomp(ba)
bytes.forEachIndexed { index, byte ->
vm.poke(pointer.toLong() + index, byte)
}
}
companion object {
val GZIP_HEADER = byteArrayOf(31, -117, 8) // .gz in DEFLATE
fun comp(str: String) = comp(str.toByteArray(VM.CHARSET))
fun comp(ba: ByteArray): ByteArray {
val baos = ByteArrayOutputStream()
val gz = GZIPOutputStream(baos)
gz.write(ba); gz.flush(); gz.finish()
baos.flush(); baos.close()
return baos.toByteArray()
}
fun decomp(str: String) = decomp(str.toByteArray(VM.CHARSET))
fun decomp(ba: ByteArray): ByteArray {
val bais = ByteArrayInputStream(ba)
val gz = GZIPInputStream(bais)
val ret = gz.readBytes()
gz.close(); bais.close()
return ret
}
}
} }

View File

@@ -65,7 +65,7 @@ object VMRunnerFactory {
bind.putMember("sys", VMJSR223Delegate(vm)) // TODO use delegator class to access peripheral (do not expose VM itself) bind.putMember("sys", VMJSR223Delegate(vm)) // TODO use delegator class to access peripheral (do not expose VM itself)
bind.putMember("graphics", GraphicsJSR223Delegate(vm)) bind.putMember("graphics", GraphicsJSR223Delegate(vm))
bind.putMember("serial", VMSerialDebugger(vm)) bind.putMember("serial", VMSerialDebugger(vm))
bind.putMember("gzip", CompressorDelegate) bind.putMember("gzip", CompressorDelegate(vm))
bind.putMember("base64", Base64Delegate) bind.putMember("base64", Base64Delegate)
bind.putMember("com", SerialHelperDelegate(vm)) bind.putMember("com", SerialHelperDelegate(vm))
bind.putMember("dma", DMADelegate(vm)) bind.putMember("dma", DMADelegate(vm))

View File

@@ -68,6 +68,7 @@ class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath:
private var file = File(rootPath.toURI()) private var file = File(rootPath.toURI())
//private var readModeLength = -1 // always 4096 //private var readModeLength = -1 // always 4096
private var writeMode = false private var writeMode = false
private var appendMode = false
private var writeModeLength = -1 private var writeModeLength = -1
private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please
@@ -127,7 +128,7 @@ class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath:
* Disk drive must create desired side effects in accordance with the input message. * Disk drive must create desired side effects in accordance with the input message.
*/ */
override fun writeoutImpl(inputData: ByteArray) { override fun writeoutImpl(inputData: ByteArray) {
if (writeMode) { if (writeMode || appendMode) {
//println("[DiskDrive] writeout with inputdata length of ${inputData.size}") //println("[DiskDrive] writeout with inputdata length of ${inputData.size}")
//println("[DiskDriveMsg] ${inputData.toString(Charsets.UTF_8)}") //println("[DiskDriveMsg] ${inputData.toString(Charsets.UTF_8)}")
@@ -137,9 +138,14 @@ class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath:
writeBufferUsage += inputData.size writeBufferUsage += inputData.size
if (writeBufferUsage >= writeModeLength) { if (writeBufferUsage >= writeModeLength) {
writeMode = false
// commit to the disk // commit to the disk
file.writeBytes(writeBuffer) if (appendMode)
file.appendBytes(writeBuffer)
else if (writeMode)
file.writeBytes(writeBuffer)
writeMode = false
appendMode = false
} }
} }
else { else {
@@ -385,7 +391,8 @@ class TestDiskDrive(private val vm: VM, private val driveNum: Int, theRootPath:
statusCode = STATE_CODE_OPERATION_NOT_PERMITTED statusCode = STATE_CODE_OPERATION_NOT_PERMITTED
return return
} }
writeMode = true if (fileOpenMode == 1) { writeMode = true; appendMode = false }
else if (fileOpenMode == 2) { writeMode = false; appendMode = true }
writeModeLength = inputString.substring(5, inputString.length).toInt() writeModeLength = inputString.substring(5, inputString.length).toInt()
writeBuffer = ByteArray(writeModeLength) writeBuffer = ByteArray(writeModeLength)
writeBufferUsage = 0 writeBufferUsage = 0

View File

@@ -1,7 +1,7 @@
package net.torvald.tsvm.peripheral package net.torvald.tsvm.peripheral
import net.torvald.tsvm.CompressorDelegate import net.torvald.tsvm.CompressorDelegate
import net.torvald.tsvm.CompressorDelegate.GZIP_HEADER import net.torvald.tsvm.CompressorDelegate.Companion.GZIP_HEADER
import net.torvald.tsvm.VM import net.torvald.tsvm.VM
import java.io.File import java.io.File