mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
pread/pwrite/ipf encoder/ipf decoder
This commit is contained in:
@@ -261,8 +261,20 @@ _TVDOS.DRV.FS.SERIAL.getFileLen = (fd) => {
|
||||
}
|
||||
return Number(com.pullMessage(port[0]))
|
||||
}
|
||||
// TODO pread replaces DMA.comToRam
|
||||
// TODO pwrite replaces DMA.ramToCom
|
||||
_TVDOS.DRV.FS.SERIAL.pread = (fd, ptr, count, offset) => {
|
||||
if (129 == _TVDOS.DRV.FS.SERIAL._openr(fd)) throw Error(`No such file: ${fd.fullPath}`)
|
||||
|
||||
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
||||
com.sendMessage(port[0], "READ")
|
||||
let response = com.getStatusCode(port[0])
|
||||
if (135 == response) {
|
||||
throw Error("File not opened")
|
||||
}
|
||||
if (response < 0 || response >= 128) {
|
||||
throw Error("Reading a file failed with "+response)
|
||||
}
|
||||
dma.comToRam(port[0], offset, ptr, count)
|
||||
}
|
||||
_TVDOS.DRV.FS.SERIAL.sread = (fd) => {
|
||||
if (129 == _TVDOS.DRV.FS.SERIAL._openr(fd)) throw Error(`No such file: ${fd.fullPath}`)
|
||||
|
||||
@@ -277,6 +289,15 @@ _TVDOS.DRV.FS.SERIAL.sread = (fd) => {
|
||||
}
|
||||
return com.pullMessage(port[0])
|
||||
}
|
||||
_TVDOS.DRV.FS.SERIAL.pwrite = (fd, ptr, count, offset) => {
|
||||
if (offset) throw Error(`Write offset not supported on Serial device such as disk drives`)
|
||||
|
||||
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Writing a file failed with "+rrrr)
|
||||
|
||||
let port = _TVDOS.DRV.FS.SERIAL._toPorts(fd.driveLetter)
|
||||
dma.ramToCom(ptr, port[0], count)
|
||||
|
||||
}
|
||||
_TVDOS.DRV.FS.SERIAL.swrite = (fd, string) => {
|
||||
let rrrr = _TVDOS.DRV.FS.SERIAL._openw(fd); if (rrrr != 0) throw Error("Writing a file failed with "+rrrr)
|
||||
|
||||
@@ -399,6 +420,7 @@ _TVDOS.DRV.FS.DEVRND.pread = (fd, ptr, count, offset) => {
|
||||
for (let i = 0; i < count; i++) {
|
||||
sys.poke(ptr + i, bytes[offset + i])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_TVDOS.DRV.FS.DEVRND.pwrite = (fd, ptr, count, offset) => {}
|
||||
@@ -570,8 +592,8 @@ Object.freeze(_TVDOS.DRV.FS.DEVFBIPF)
|
||||
|
||||
// Legacy Serial filesystem, !!pending for removal!!
|
||||
|
||||
/*
|
||||
const filesystem = {};
|
||||
|
||||
/*const filesystem = {};
|
||||
|
||||
filesystem._toPorts = (driveLetter) => {
|
||||
if (driveLetter.toUpperCase === undefined) {
|
||||
@@ -692,8 +714,8 @@ filesystem.remove = (driveLetter) => {
|
||||
var response = com.getStatusCode(port[0]);
|
||||
return (response === 0);
|
||||
};
|
||||
Object.freeze(filesystem);
|
||||
*/
|
||||
Object.freeze(filesystem);*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
47
assets/disk0/tvdos/bin/decodeipf.js
Normal file
47
assets/disk0/tvdos/bin/decodeipf.js
Normal file
@@ -0,0 +1,47 @@
|
||||
if (exec_args[1] == undefined) {
|
||||
println("decodeipf <input.ipf>")
|
||||
return 1
|
||||
}
|
||||
|
||||
let infile = files.open(_G.shell.resolvePathInput(exec_args[1]).full)
|
||||
|
||||
// read input file
|
||||
let infilePtr = sys.malloc(infile.size)
|
||||
infile.pread(infilePtr, infile.size, 0)
|
||||
|
||||
// check if magic number matches
|
||||
|
||||
const MAGIC = [0x1F, 0x54, 0x53, 0x56, 0x4D, 0x69, 0x50, 0x46]
|
||||
let magicMatching = true
|
||||
|
||||
MAGIC.forEach((b,i) => {
|
||||
let testb = sys.peek(infilePtr + i) & 255 // for some reason this must be located here
|
||||
if (testb != b) {
|
||||
magicMatching = false
|
||||
}
|
||||
})
|
||||
if (!magicMatching) {
|
||||
println("Not an iPF file (MAGIC mismatch)")
|
||||
return 1
|
||||
}
|
||||
|
||||
// decode input image
|
||||
let width = sys.peek(infilePtr+8) | (sys.peek(infilePtr+9) << 8)
|
||||
let height = sys.peek(infilePtr+10) | (sys.peek(infilePtr+11) << 8)
|
||||
let hasAlpha = (sys.peek(infilePtr+12) != 0)
|
||||
let ipfType = sys.peek(infilePtr+13)
|
||||
let imgLen = sys.peek(infilePtr+24) | (sys.peek(infilePtr+25) << 8) | (sys.peek(infilePtr+26) << 16) | (sys.peek(infilePtr+27) << 24)
|
||||
let decodefun = undefined
|
||||
if (ipfType == 1) decodefun = graphics.decodeIpf1
|
||||
if (ipfType == 2) decodefun = graphics.decodeIpf2
|
||||
if (decodefun === undefined) throw Error(`Unknown IPF format: ${ipfType}`)
|
||||
|
||||
let ipfbuf = sys.malloc(imgLen)
|
||||
gzip.decompFromTo(infilePtr + 28, infile.size - 28, ipfbuf) // should return FBUF_SIZE
|
||||
sys.free(infilePtr)
|
||||
|
||||
graphics.setGraphicsMode(4)
|
||||
graphics.clearText(); graphics.clearPixels(0); graphics.clearPixels2(0)
|
||||
decodefun(ipfbuf, -1048577, -1310721, width, height, hasAlpha)
|
||||
|
||||
sys.free(ipfbuf)
|
||||
57
assets/disk0/tvdos/bin/encodeipf.js
Normal file
57
assets/disk0/tvdos/bin/encodeipf.js
Normal file
@@ -0,0 +1,57 @@
|
||||
if (exec_args[3] == undefined) {
|
||||
println("encodeipf <1/2> <input picture> <output filename> [/noalpha]")
|
||||
return 1
|
||||
}
|
||||
|
||||
let noalpha = exec_args[4] != undefined && exec_args[4].toLowerCase() == "/noalpha"
|
||||
|
||||
let infile = files.open(_G.shell.resolvePathInput(exec_args[2]).full)
|
||||
let outfile = files.open(_G.shell.resolvePathInput(exec_args[3]).full)
|
||||
|
||||
let ipfType = exec_args[1]|0
|
||||
let encodefun = undefined
|
||||
if (1 == exec_args[1]) encodefun = graphics.encodeIpf1
|
||||
if (2 == exec_args[1]) encodefun = graphics.encodeIpf2
|
||||
if (encodefun === undefined) throw Error(`Unknown IPF format: ${exec_args[1]}`)
|
||||
|
||||
// read input file
|
||||
let infilePtr = sys.malloc(infile.size)
|
||||
infile.pread(infilePtr, infile.size, 0)
|
||||
|
||||
// decode input image
|
||||
const [imgw, imgh, channels, imageDataPtr] = graphics.decodeImage(infilePtr, infile.size) // stored as [R | G | B | (A)]
|
||||
sys.free(infilePtr)
|
||||
let hasAlpha = (4 == channels) && !noalpha
|
||||
|
||||
// encode image
|
||||
let ipfBlockCount = Math.ceil(imgw / 4.0) * Math.ceil(imgh / 4.0)
|
||||
let ipfSizePerBlock = 12 + 4*(ipfType - 1) + 8*hasAlpha
|
||||
let ipfRawSize = ipfSizePerBlock * ipfBlockCount
|
||||
let ipfarea = sys.malloc(ipfRawSize)
|
||||
let gzippedImage = sys.malloc(28 + ipfRawSize+8) // ipf file header + somewhat arbitrary number. Get the actual count using 28+gzlen
|
||||
encodefun(imageDataPtr, ipfarea, imgw, imgh, channels, hasAlpha, 0)
|
||||
let gzlen = gzip.compFromTo(ipfarea, ipfRawSize, gzippedImage + 28)
|
||||
|
||||
|
||||
sys.free(ipfarea)
|
||||
|
||||
// write to the output bin
|
||||
//// write header
|
||||
;[
|
||||
0x1F, 0x54, 0x53, 0x56, 0x4D, 0x69, 0x50, 0x46, // magic
|
||||
imgw & 255, (imgw >> 8) & 255, // width
|
||||
imgh & 255, (imgh >> 8) & 255, // height
|
||||
0+hasAlpha, // has alpha
|
||||
ipfType, // ipf type
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
|
||||
(gzlen >>> 0) & 255, // uncompressed size
|
||||
(gzlen >>> 8) & 255,
|
||||
(gzlen >>> 16) & 255,
|
||||
(gzlen >>> 24) & 255
|
||||
].forEach((b,i) => sys.poke(gzippedImage + i, b))
|
||||
|
||||
outfile.mkFile()
|
||||
outfile.pwrite(gzippedImage, 28 + gzlen, 0)
|
||||
//dma.ramToCom(writeBuf, 0, writeCount)
|
||||
sys.free(gzippedImage)
|
||||
println(`Wrote ${28 + gzlen} bytes to the file`)
|
||||
BIN
assets/disk0/ycocg.ipf1
Normal file
BIN
assets/disk0/ycocg.ipf1
Normal file
Binary file not shown.
BIN
assets/disk0/ycocg.ipf2
Normal file
BIN
assets/disk0/ycocg.ipf2
Normal file
Binary file not shown.
BIN
assets/disk0/ycocg2.ipf1
Normal file
BIN
assets/disk0/ycocg2.ipf1
Normal file
Binary file not shown.
BIN
assets/disk0/ycocg2.ipf2
Normal file
BIN
assets/disk0/ycocg2.ipf2
Normal file
Binary file not shown.
@@ -413,13 +413,13 @@ TYPE 255 FRAME -
|
||||
|
||||
TSVM Interchangeable Picture Format (aka iPF Type 1/2)
|
||||
|
||||
Image is divided into 4x4 blocks and each block is serialised, then the entire file is gzipped
|
||||
Image is divided into 4x4 blocks and each block is serialised, then the entire iPF blocks are gzipped
|
||||
|
||||
|
||||
# File Structure
|
||||
\x1F T S V M i P F
|
||||
[HEADER]
|
||||
[Blocks.gz] or [Blocks] // gzipping is optional and only done for videos
|
||||
[Blocks.gz]
|
||||
|
||||
- Header
|
||||
uint16 WIDTH
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.io.ByteArrayOutputStream
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
class CompressorDelegate(val vm: VM) {
|
||||
class CompressorDelegate(private val vm: VM) {
|
||||
|
||||
fun comp(str: String) = Companion.comp(str)
|
||||
fun comp(ba: ByteArray) = Companion.comp(ba)
|
||||
|
||||
@@ -6,7 +6,7 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
|
||||
/**
|
||||
* Created by minjaesong on 2021-10-15.
|
||||
*/
|
||||
class DMADelegate(val vm: VM) {
|
||||
class DMADelegate(private val vm: VM) {
|
||||
|
||||
private val READ = "READ".toByteArray(VM.CHARSET)
|
||||
private val FLUSH = "FLUSH".toByteArray(VM.CHARSET)
|
||||
|
||||
@@ -8,7 +8,7 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
|
||||
import net.torvald.tsvm.peripheral.fmod
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class GraphicsJSR223Delegate(val vm: VM) {
|
||||
class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
|
||||
private fun getFirstGPU(): GraphicsAdapter? {
|
||||
return vm.findPeribyType(VM.PERITYPE_GPU_AND_TERM)?.peripheral as? GraphicsAdapter
|
||||
|
||||
@@ -137,7 +137,7 @@ object SerialHelper {
|
||||
data class DeviceStatus(val code: Int, val message: String)
|
||||
}
|
||||
|
||||
class SerialHelperDelegate(val vm: VM) {
|
||||
class SerialHelperDelegate(private val vm: VM) {
|
||||
fun sendMessage(portNo: Int, message: String) = SerialHelper.sendMessage(vm, portNo, message.toByteArray(VM.CHARSET))
|
||||
fun pullMessage(portNo: Int) = SerialHelper.pullMessage(vm, portNo).toString(VM.CHARSET)
|
||||
fun sendMessageGetBytes(portNo: Int, message: String) = SerialHelper.sendMessageGetBytes(vm, portNo, message.toByteArray(VM.CHARSET)).toString(VM.CHARSET)
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.nio.charset.Charset
|
||||
/**
|
||||
* Pass the instance of the class to the ScriptEngine's binding, preferably under the namespace of "vm"
|
||||
*/
|
||||
class VMJSR223Delegate(val vm: VM) {
|
||||
class VMJSR223Delegate(private val vm: VM) {
|
||||
|
||||
fun poke(addr: Int, value: Int) = vm.poke(addr.toLong(), value.toByte())
|
||||
fun peek(addr: Int) = vm.peek(addr.toLong())!!.toInt().and(255)
|
||||
@@ -186,13 +186,13 @@ class VMJSR223Delegate(val vm: VM) {
|
||||
}
|
||||
}
|
||||
|
||||
class VMSerialDebugger(val vm: VM) {
|
||||
class VMSerialDebugger(private val vm: VM) {
|
||||
fun print(s: Any?) = System.out.print("$s")
|
||||
fun println(s: Any?) = System.out.println("$s")
|
||||
fun printerr(s: Any?) = System.err.println("$s")
|
||||
}
|
||||
|
||||
class Parallel(val vm: VM) {
|
||||
class Parallel(private val vm: VM) {
|
||||
fun spawnNewContext(): VMRunner {
|
||||
return VMRunnerFactory(vm.assetsDir, vm, "js")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user