aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2022-09-16 07:34:44 +0100
committerJuan J. Martinez <jjm@usebox.net>2022-09-16 12:05:09 +0100
commit1ef0d697a62eff28115d6642c850ba4d01ef6a89 (patch)
tree23d7fdb24453a3a1378f81d021567616492bbdbd /tools
parent290c74b70661bcde314f73fde2be888e5aed47e0 (diff)
downloadubox-msx-lib-1ef0d697a62eff28115d6642c850ba4d01ef6a89.tar.gz
ubox-msx-lib-1ef0d697a62eff28115d6642c850ba4d01ef6a89.zip
Added CAS support to the example game
Diffstat (limited to 'tools')
-rwxr-xr-xtools/mkcas.py164
-rwxr-xr-xtools/png2scr.py140
2 files changed, 304 insertions, 0 deletions
diff --git a/tools/mkcas.py b/tools/mkcas.py
new file mode 100755
index 0000000..267270d
--- /dev/null
+++ b/tools/mkcas.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020-2022 by Juan J. Martinez <jjm@usebox.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+__version__ = "1.1"
+
+import os
+from argparse import ArgumentParser
+
+DEF_ADDR = 0x4000
+TYPES = ("binary", "basic", "ascii", "custom-header", "custom")
+TYPE_BLOCK = {
+ "binary": bytes((0xD0 for _ in range(10))),
+ "basic": bytes((0xD3 for _ in range(10))),
+ "ascii": bytes((0xEA for _ in range(10))),
+}
+
+BLOCK_ID = bytes((0x1F, 0xA6, 0xDE, 0xBA, 0xCC, 0x13, 0x7D, 0x74))
+
+
+def write_word(fd, word):
+ fd.write(bytes((word & 0xFF, word >> 8)))
+
+
+def auto_int(value):
+ return int(value, 0)
+
+
+def main():
+
+ parser = ArgumentParser(
+ description="Make a CAS file for the MSX",
+ epilog="Copyright (C) 2020-2022 Juan J Martinez <jjm@usebox.net>",
+ )
+
+ parser.add_argument(
+ "--version", action="version", version="%(prog)s " + __version__
+ )
+ parser.add_argument(
+ "-a",
+ "--add",
+ dest="add",
+ action="store_true",
+ help="append to the existing CAS file instead of creating a new one",
+ )
+ parser.add_argument(
+ "--name",
+ dest="name",
+ default=None,
+ type=str,
+ help="name to use for the file (limit 6 chars, defaults to the file name)",
+ )
+ parser.add_argument(
+ "--addr",
+ dest="addr",
+ default=DEF_ADDR,
+ type=auto_int,
+ help="address to load if binary file (default: 0x%04x)" % DEF_ADDR,
+ )
+ parser.add_argument(
+ "--exec",
+ dest="exec",
+ default=DEF_ADDR,
+ type=auto_int,
+ help="address to exec if binary file (default: 0x%04x)" % DEF_ADDR,
+ )
+
+ parser.add_argument("output", help="target .CAS file")
+ parser.add_argument("type", help="file type", choices=TYPES)
+ parser.add_argument("file", help="input file")
+
+ args = parser.parse_args()
+
+ if args.add:
+ flags = "ab"
+ else:
+ flags = "wb"
+
+ with open(args.output, flags) as out:
+
+ if args.name:
+ name = args.name
+ else:
+ name = os.path.basename(args.file)
+
+ with open(args.file, "rb") as inf:
+ data = inf.read()
+
+ out.write(BLOCK_ID)
+
+ if args.type == "ascii":
+
+ out.write(TYPE_BLOCK[args.type])
+ out.write(name[:6].ljust(6).encode("ascii"))
+
+ for b in range(0, len(data), 256):
+ out.write(BLOCK_ID)
+ out.write(data[b : b + 256])
+
+ padding = 256 - (len(data) % 256)
+ if padding == 256:
+ out.write(BLOCK_ID)
+ out.write(bytes((0x1A for _ in range(padding))))
+
+ elif args.type == "basic":
+
+ out.write(TYPE_BLOCK[args.type])
+ out.write(name[:6].ljust(6).encode("ascii"))
+ out.write(BLOCK_ID)
+ out.write(data)
+
+ elif args.type == "binary":
+
+ addr = args.addr
+ exec_addr = args.exec
+ end_addr = addr + len(data) - 1
+
+ if end_addr > 0xFFFF:
+ parser.error("Binary doesn't fit in memory")
+
+ out.write(TYPE_BLOCK[args.type])
+ out.write(name[:6].ljust(6).encode("ascii"))
+
+ out.write(BLOCK_ID)
+ write_word(out, addr)
+ write_word(out, end_addr)
+ write_word(out, exec_addr)
+
+ out.write(data)
+
+ elif args.type == "custom-header":
+
+ addr = args.addr
+ length = len(data)
+
+ write_word(out, addr)
+ write_word(out, length)
+ out.write(data)
+
+ else:
+ # custom
+ out.write(data)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/png2scr.py b/tools/png2scr.py
new file mode 100755
index 0000000..5d08915
--- /dev/null
+++ b/tools/png2scr.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018-2022 by Juan J. Martinez <jjm@usebox.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+
+__version__ = "1.0"
+
+import sys
+from argparse import ArgumentParser
+from PIL import Image
+from collections import defaultdict
+
+DEF_W = 8
+DEF_H = 8
+
+# TOSHIBA palette
+MSX_COLS = [
+ (255, 0, 255),
+ (0, 0, 0),
+ (102, 204, 102),
+ (136, 238, 136),
+ (68, 68, 221),
+ (119, 119, 255),
+ (187, 85, 85),
+ (119, 221, 221),
+ (221, 102, 102),
+ (255, 119, 119),
+ (204, 204, 85),
+ (238, 238, 136),
+ (85, 170, 85),
+ (187, 85, 187),
+ (204, 204, 204),
+ (238, 238, 238),
+]
+
+
+def main():
+
+ parser = ArgumentParser(
+ description="PNG to SCR file",
+ epilog="Copyright (C) 2018-2022 Juan J Martinez <jjm@usebox.net>",
+ )
+
+ parser.add_argument(
+ "--version", action="version", version="%(prog)s " + __version__
+ )
+
+ parser.add_argument("image", help="image to convert")
+
+ args = parser.parse_args()
+
+ try:
+ image = Image.open(args.image)
+ except IOError:
+ parser.error("failed to open the image")
+
+ if image.mode != "RGB":
+ parser.error("not a RGB image")
+
+ (w, h) = image.size
+
+ if w % 256 or h % 192:
+ parser.error("%s size is not 256x192" % args.image)
+
+ data = image.getdata()
+
+ color_idx = defaultdict(list)
+ color = []
+ out = []
+ ntiles = 0
+ for y in range(0, h, DEF_H):
+ for x in range(0, w, DEF_W):
+ # tile data
+ tile = [
+ data[x + i + ((y + j) * w)] for j in range(DEF_H) for i in range(DEF_W)
+ ]
+
+ # get the attibutes of the tile
+ # FIXME: this may not be right
+ for i in range(0, len(tile), DEF_W):
+ cols = list(sorted(set(tile[i : i + DEF_W])))
+
+ if len(cols) > 2:
+ parser.error(
+ "tile %d (%d, %d) has more than two colors: %r"
+ % (ntiles, x, y, cols)
+ )
+ elif len(cols) == 1:
+ cols.append(MSX_COLS[1])
+
+ for c in cols:
+ if c not in MSX_COLS:
+ parser.error(
+ "tile %d (%d, %d) has a color not in the MSX palette: %r"
+ % (ntiles, x, y, c)
+ )
+
+ # each tile has two coloributes per row
+ color_idx[ntiles * DEF_H + i // DEF_W] = cols
+ color.append((MSX_COLS.index(cols[1]) << 4) | MSX_COLS.index(cols[0]))
+
+ frame = []
+ for i in range(0, len(tile), 8):
+ byte = 0
+ p = 7
+ for k in range(8):
+ # 0 or 1 is determined by the order in the coloributes for that row
+ byte |= (
+ color_idx[ntiles * DEF_H + i // DEF_W].index(tile[i + k]) << p
+ )
+ p -= 1
+
+ frame.append(byte)
+ ntiles += 1
+ out.extend(frame)
+
+ sys.stdout.buffer.write(bytes(out))
+ sys.stdout.buffer.write(bytes(color))
+
+
+if __name__ == "__main__":
+ main()