diff --git a/tsvm_executable/src/net/torvald/terrarum/utils/JsonFetcher.kt b/tsvm_executable/src/net/torvald/terrarum/utils/JsonFetcher.kt new file mode 100644 index 0000000..5631831 --- /dev/null +++ b/tsvm_executable/src/net/torvald/terrarum/utils/JsonFetcher.kt @@ -0,0 +1,91 @@ +package net.torvald.terrarum.utils + +import com.badlogic.gdx.utils.JsonReader +import com.badlogic.gdx.utils.JsonValue + +/** + * Created by minjaesong on 2016-02-15. + */ +object JsonFetcher { + + private var jsonString: StringBuffer? = null + + @Throws(java.io.IOException::class) + operator fun invoke(jsonFilePath: String): JsonValue { + jsonString = StringBuffer() // reset buffer every time it called + readJsonFileAsString(jsonFilePath) + +// printdbg(this, "Reading JSON $jsonFilePath") + + if (jsonString == null) { + throw Error("[JsonFetcher] jsonString is null!") + } + + return JsonReader().parse(jsonString.toString()) + } + + @Throws(java.io.IOException::class) + operator fun invoke(jsonFile: java.io.File): JsonValue { + jsonString = StringBuffer() // reset buffer every time it called + readJsonFileAsString(jsonFile.canonicalPath) + +// printdbg(this, "Reading JSON ${jsonFile.path}") + + if (jsonString == null) { + throw Error("[JsonFetcher] jsonString is null!") + } + + return JsonReader().parse(jsonString.toString()) + } + + @Throws(java.io.IOException::class) + private fun readJsonFileAsString(path: String) { + java.nio.file.Files.lines(java.nio.file.FileSystems.getDefault().getPath(path)).forEach( + { jsonString!!.append(it) } + ) // JSON does not require line break + + } + + /** + * Iterates [JsonValue] over its siblings. + * + * @param map JsonValue to iterate over + * @param action A `function(`Name of the sibling or a stringified integer if the `map` is an array`, `JsonValue representation of the sibling`)` -> `Unit` + */ + fun forEachSiblings(map: JsonValue, action: (String, JsonValue) -> Unit) { + var counter = 0 + var entry = map.child + while (entry != null) { + action(entry.name ?: "$counter", entry) + entry = entry.next + counter += 1 + } + } + + /** + * Iterates [JsonValue] over its siblings. + * + * @param map JsonValue to iterate over + * @param action A `function(index, `Name of the sibling or a stringified integer if the `map` is an array`, `JsonValue representation of the sibling`)` -> `Unit` + */ + fun forEachSiblingsIndexed(map: JsonValue, action: (Int, String, JsonValue) -> Unit) { + var counter = 0 + var entry = map.child + while (entry != null) { + action(counter, entry.name ?: "$counter", entry) + entry = entry.next + counter += 1 + } + } +} + +/** + * Iterates [JsonValue] over its siblings. + * @param action A function(`Name of the sibling or a stringified integer if the `map` is an array`, `JsonValue representation of the sibling`)` -> `Unit` + */ +fun JsonValue.forEachSiblings(action: (String, JsonValue) -> Unit) = JsonFetcher.forEachSiblings(this, action) +/** + * Iterates [JsonValue] over its siblings. + * @param action A `function(index, `Name of the sibling or a stringified integer if the `map` is an array`, `JsonValue representation of the sibling`)` -> `Unit` + */ +fun JsonValue.forEachSiblingsIndexed(action: (Int, String, JsonValue) -> Unit) = JsonFetcher.forEachSiblingsIndexed(this, action) \ No newline at end of file diff --git a/tsvm_executable/src/net/torvald/tsvm/ProfileSerialiser.kt b/tsvm_executable/src/net/torvald/tsvm/ProfileSerialiser.kt new file mode 100644 index 0000000..b69dbf1 --- /dev/null +++ b/tsvm_executable/src/net/torvald/tsvm/ProfileSerialiser.kt @@ -0,0 +1,32 @@ +package net.torvald.tsvm + +import com.badlogic.gdx.utils.Json +import com.badlogic.gdx.utils.JsonValue +import com.badlogic.gdx.utils.JsonWriter +import java.math.BigInteger + +/** + * Created by minjaesong on 2022-10-27. + */ +object ProfileSerialiser { + + val jsoner = Json(JsonWriter.OutputType.json) + + init { + jsoner.ignoreUnknownFields = true + jsoner.setUsePrototypes(false) + jsoner.setIgnoreDeprecated(false) + + // BigInteger + jsoner.setSerializer(BigInteger::class.java, object : Json.Serializer { + override fun write(json: Json, obj: BigInteger?, knownType: Class<*>?) { + json.writeValue(obj?.toString()) + } + + override fun read(json: Json, jsonData: JsonValue, type: Class<*>?): BigInteger? { + return if (jsonData.isNull) null else BigInteger(jsonData.asString()) + } + }) + // + } +} \ No newline at end of file diff --git a/tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt b/tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt index 9236442..2803669 100644 --- a/tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt +++ b/tsvm_executable/src/net/torvald/tsvm/VMEmuExecutable.kt @@ -2,22 +2,22 @@ package net.torvald.tsvm import com.badlogic.gdx.ApplicationAdapter import com.badlogic.gdx.Gdx +import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch -import com.badlogic.gdx.utils.Json import com.badlogic.gdx.utils.JsonReader import com.badlogic.gdx.utils.JsonValue +import com.badlogic.gdx.utils.JsonWriter import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import net.torvald.terrarum.FlippingSpriteBatch import net.torvald.terrarum.imagefont.TinyAlphNum +import net.torvald.terrarum.utils.JsonFetcher import net.torvald.tsvm.VMEmuExecutableWrapper.Companion.FONT import net.torvald.tsvm.VMEmuExecutableWrapper.Companion.SQTEX import net.torvald.tsvm.peripheral.* -import java.io.File -import java.util.* class VMEmuExecutableWrapper(val windowWidth: Int, val windowHeight: Int, var panelsX: Int, var panelsY: Int, val diskPathRoot: String) : ApplicationAdapter() { @@ -77,6 +77,17 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: var vmRunners = HashMap() // var coroutineJobs = HashMap() // + companion object { + val APPDATADIR = System.getProperty("os.name").toUpperCase().let { + if (it.contains("WIN")) System.getenv("APPDATA") + "/tsvmdevenv" + else if (it.contains("OS X") || it.contains("MACOS")) System.getProperty("user.home") + "/Library/Application Support/tsvmdevenv" + else System.getProperty("user.home") + "/.tsvmdevenv" + } + + val FILE_CONFIG = Gdx.files.absolute("$APPDATADIR/config.json") + val FILE_PROFILES = Gdx.files.absolute("$APPDATADIR/profiles.json") + } + val fullscreenQuad = Mesh( true, 4, 6, VertexAttribute.Position(), @@ -84,6 +95,26 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: VertexAttribute.TexCoords(0) ) + val profiles = HashMap() + + fun writeProfilesToFile(outFile: FileHandle) { + val out = StringBuilder() + out.append('{') + + profiles.forEach { name, jsonValue -> + out.append("\"$name\":{") + out.append(jsonValue.toJson(JsonWriter.OutputType.json)) + out.append("},") + } + + out.deleteCharAt(out.lastIndex).append('}') + + val outstr = out.toString() + println(outstr) + + outFile.writeString(outstr, false) + } + override fun create() { super.create() @@ -98,6 +129,18 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: fbatch.projectionMatrix = camera.combined + // create profiles.json if the file is not there + if (!FILE_PROFILES.exists()) { + FILE_PROFILES.writeString("{${defaultProfile}}", false) + } + // read profiles + JsonFetcher(FILE_PROFILES.file()).let { + JsonFetcher.forEachSiblings(it) { profileName, profileJson -> + profiles[profileName] = profileJson + } + } + + // install the default VM on slot 0 /*val vm = VM("./assets", 8192 shl 10, TheRealWorld(), arrayOf(TsvmBios), 8) vm.getIO().blockTransferPorts[0].attachDevice(TestDiskDrive(vm, 0, File("assets/disk0"))) @@ -326,8 +369,6 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: private val tabPos = (menuTabs + "").mapIndexed { index, _ -> 1 + menuTabs.subList(0, index).sumBy { it.length } + 2 * index } private val tabs = listOf(ProfilesMenu(menuTabW, menuTabH)) private var menuTabSel = 0 - private val profilesPath = "profiles.json" - private val configPath = "config.json" private fun drawMenu(batch: SpriteBatch, x: Float, y: Float) { batch.inUse { @@ -388,9 +429,9 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: "ramsize":8388608, "cardslots":8, "roms":["./assets/bios/tsvmbios.js"], - "com1":{"class":"net.torvald.tsvm.peripheral.TestDiskDrive", "args":[0, "./assets/disk0/"]}, - "com2":{"class":"net.torvald.tsvm.peripheral.HttpModem", "args":[]}, - "card4":{"class":"net.torvald.tsvm.peripheral.RamBank", "args":[256]} + "com1":{"cls":"net.torvald.tsvm.peripheral.TestDiskDrive", "args":[0, "./assets/disk0/"]}, + "com2":{"cls":"net.torvald.tsvm.peripheral.HttpModem", "args":[]}, + "card4":{"cls":"net.torvald.tsvm.peripheral.RamBank", "args":[256]} } """.trimIndent() @@ -415,7 +456,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: // install peripherals listOf("com1", "com2", "com3", "com4").map { json.get(it) }.forEachIndexed { index, jsonValue -> jsonValue?.let { deviceInfo -> - val className = deviceInfo.getString("class") + val className = deviceInfo.getString("cls") val loadedClass = Class.forName(className) @@ -454,7 +495,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: } (2..cardslots).map { it to json.get("card$it") }.forEach { (index, jsonValue) -> jsonValue?.let { deviceInfo -> - val className = deviceInfo.getString("class") + val className = deviceInfo.getString("cls") val loadedClass = Class.forName(className) val argTypes = loadedClass.declaredConstructors[0].parameterTypes