152 lines
4.1 KiB
Python
152 lines
4.1 KiB
Python
from os import path
|
|
import struct
|
|
import sys
|
|
from itertools import chain
|
|
from dataclasses import dataclass
|
|
from binascii import unhexlify
|
|
|
|
storage_size = 128 * 1024
|
|
buf = bytearray(storage_size)
|
|
|
|
storage_blocks_count = storage_size // 512
|
|
system_block = 0xff
|
|
fat_block = 0xfe
|
|
fat_blocks = 1
|
|
file_information_block = 0xfd
|
|
file_information_blocks = 0xd
|
|
block_size = 512
|
|
|
|
def round_up(n):
|
|
rem = n % block_size
|
|
if rem != 0:
|
|
rem = block_size - rem
|
|
return n + rem
|
|
|
|
def build_chain(length):
|
|
total_blocks = length // block_size
|
|
print("total blocks", length, total_blocks)
|
|
unused_blocks = storage_blocks_count - total_blocks
|
|
#print(total_blocks, unused_blocks)
|
|
for i in range(total_blocks - 1):
|
|
yield i + 1
|
|
yield 0xfffa
|
|
|
|
for i in range(unused_blocks):
|
|
yield 0xfffc
|
|
|
|
def write_chain(start_entry, chain):
|
|
offset = fat_block * block_size + start_entry * 2
|
|
for entry in chain:
|
|
assert offset < (fat_block + fat_blocks) * block_size
|
|
packed = struct.pack("<H", entry)
|
|
buf[offset+0] = packed[0]
|
|
buf[offset+1] = packed[1]
|
|
offset += 2
|
|
|
|
def write_image(vms_path):
|
|
with open(vms_path, 'rb') as f:
|
|
vms_buf = f.read()
|
|
assert len(vms_buf) <= (storage_size // 2), len(vms_buf)
|
|
for i, c in enumerate(vms_buf):
|
|
buf[i] = c
|
|
return round_up(len(vms_buf))
|
|
|
|
def write_file_information_chain():
|
|
block = file_information_block
|
|
for i in range(file_information_blocks - 1):
|
|
chain_ix = file_information_block - (i + 0)
|
|
chain_entry = file_information_block - (i + 1)
|
|
offset = fat_block * block_size + chain_ix * 2
|
|
struct.pack_into("<H", buf, offset, chain_entry)
|
|
struct.pack_into("<H", buf, offset - 2, 0xfffa)
|
|
|
|
@dataclass
|
|
class FileInformation:
|
|
status: int
|
|
copy: int
|
|
start_fat: int
|
|
file_name: bytes
|
|
date: int
|
|
block_size: int
|
|
header: int
|
|
|
|
file_name_length = 12
|
|
|
|
def write_file_information(info):
|
|
offset = file_information_block * block_size
|
|
struct.pack_into("<BBH", buf, offset, info.status, info.copy, info.start_fat)
|
|
offset += 4
|
|
assert len(info.file_name) == file_name_length
|
|
for i, c in enumerate(info.file_name):
|
|
buf[offset + i] = c
|
|
offset += file_name_length
|
|
date = b"\x19\x98\x11'\x030\t\x04"
|
|
for i, c in enumerate(date):
|
|
buf[offset + i] = c
|
|
offset += len(date)
|
|
struct.pack_into("<HHI", buf, offset, info.block_size, info.header, 0)
|
|
|
|
def write_hex(offset, s):
|
|
for i, b in enumerate(unhexlify(s)):
|
|
buf[offset + i] = b
|
|
|
|
def write_num2(offset, n):
|
|
struct.pack_into("<H", buf, offset, n)
|
|
|
|
def write_system_area():
|
|
offset = lambda n: system_block * 512 + n
|
|
|
|
struct.pack_into("<" + ("B" * 16), buf, offset(0), *([0x55] * 16))
|
|
|
|
write_hex(offset(0x10), b"01ffffffff000000000000000000000000000000000000000000000000000000")
|
|
write_hex(offset(0x30), b"1998120604293906")
|
|
write_num2(offset(0x40), 255)
|
|
write_num2(offset(0x44), 255)
|
|
write_num2(offset(0x46), 254)
|
|
write_num2(offset(0x48), 1)
|
|
write_num2(offset(0x4a), 253)
|
|
write_num2(offset(0x4c), 13)
|
|
write_num2(offset(0x50), 200)
|
|
write_num2(offset(0x52), 31)
|
|
write_num2(offset(0x54), 1)
|
|
|
|
def main():
|
|
if len(sys.argv) < 3:
|
|
print(f"usage: {sys.argv[0]} [game.vms] [vmu_image.bin]")
|
|
sys.exit(1)
|
|
|
|
vms_path = sys.argv[1]
|
|
image_size = write_image(vms_path)
|
|
image_chain = list(build_chain(image_size))
|
|
assert len(image_chain) == fat_blocks * block_size // 2
|
|
write_chain(0, image_chain)
|
|
|
|
file_name = path.split(vms_path.upper())[1][:file_name_length].encode('utf-8')
|
|
append = file_name_length - len(file_name)
|
|
if append > 0:
|
|
file_name += b'\x00' * append
|
|
|
|
info = FileInformation(
|
|
status = 0xcc,
|
|
copy = 0x0,
|
|
start_fat = 0x0,
|
|
file_name = file_name,
|
|
date = 0,
|
|
block_size = image_size // block_size,
|
|
header = 1,
|
|
)
|
|
|
|
write_file_information_chain()
|
|
write_file_information(info)
|
|
|
|
offset = fat_block * 512 + (254 * 2)
|
|
struct.pack_into("<HH", buf, offset, 0xfffa, 0xfffa)
|
|
|
|
write_system_area()
|
|
|
|
with open(sys.argv[2], 'wb') as f:
|
|
f.write(buf)
|
|
|
|
|
|
main()
|