Instructions are one byte and have a variable number of bytes afterwards.
+Note that this file describes the instruction set AS THE VM INTERPRETS IT,
+e.g. not as you'd write it for the assembler.
+
+Any integers of greater than one byte are not re-ordered to fit endianness.
+If you specify 0001 as a value, the vm will read this as:
+
+ uint16_t val = *((uint16_t*){0x00, 0x01})
+
+Be aware of this.
+
Unless otherwise noted, if an instruction doesn't succeed, it will abort.
nop : 1 byte : Opcode 0x00
16: TWL Section 2
17: TWL Section 3
+ 18: Loader title (see info)
+
find <size> <pattern...> : 2 + size bytes : opcode 0x02
Finds a pattern in memory. On success, operations
will be performed relative to the beginning of the found pattern.
<pattern> : <size> bytes
data to find
-back <count> : 5 bytes : opcode 0x03
+back <count> : 2 bytes : opcode 0x03
Moves back <count> bytes from current position.
- <count> : 4 bytes
+ <count> : 1 byte
How many bytes to rewind.
-fwd <count> : 5 bytes : opcode 0x04
+fwd <count> : 2 bytes : opcode 0x04
Moves forward <count> bytes from current position.
- <count> : 4 bytes
+ <count> : 1 byte
How many bytes to rewind.
set <size> <data...> : 2 + size bytes : opcode 0x05
<data> : <size> bytes
Data to copy.
-test <size> <data...> : 2 bytes : opcode 0x06
+test <size> <data...> : 2 + size bytes : opcode 0x06
Tests if the current location's data is equivalent to <data>.
If equivalent, goes to the next instruction. If not, skips
one operation.
<data> : <size> bytes
Data to bitwise and with relative data.
+
+title <count> <title> : 2 + <count> * 8 bytes : 0x0A
+ Specifies that the following code is applicable only when being applied to
+ a list of titles.
+
+ This allows the creation of generic patches which can be used with multiple
+ titles and share common parts.
+
+ The default state is to apply code on any titleID matches within the header,
+ so unless you have specialized needs you'll almost never need this.
+
+ <count> : 1 byte
+ How many titleIDs to read.
+ <title> : 8 * <count> bytes
+ List of titleIDs as u64.
--- /dev/null
+#!/usr/bin/env python2
+# -*- encoding: utf8 -*-
+
+import os
+import sys
+import re
+import struct
+
+in_file = ""
+out_file = ""
+
+def usage():
+ print("Usage: " + sys.argv[0] + " <input.bas> <output.bco>")
+
+lines = 0
+def syn_err(x):
+ print("error - " + str(line) + " - " + x)
+ exit(2)
+
+def rel_name(x):
+ return {
+ 'native' : "00",
+ 'agb' : "01",
+ 'twl' : "02",
+
+ 'native_p9': "03",
+ 'agb_p9' : "04",
+ 'twl_p9' : "05",
+
+ 'native_s0': "06",
+ 'native_s1': "07",
+ 'native_s2': "08",
+ 'native_s3': "09",
+
+ 'agb_s0' : "0A",
+ 'agb_s1' : "0B",
+ 'agb_s2' : "0C",
+ 'agb_s3' : "0D",
+
+ 'twl_s0' : "0E",
+ 'twl_s1' : "0F",
+ 'twl_s2' : "10",
+ 'twl_s3' : "11",
+ }.get(x, "-1")
+
+name = "NO NAME"
+desc = "NO DESC"
+title = []
+ver = "01"
+flags = []
+uuid = ""
+deps = []
+
+def cat_list(list):
+ retstr = ""
+ for str in list:
+ retstr += str + " "
+ return retstr
+
+def parse_op(token_list):
+ global title
+ global desc
+ global name
+ global ver
+ global flags
+ global uuid
+ global deps
+ s = len(token_list) # Get size.
+ if s == 0:
+ return bytearray() # Empty.
+
+ if token_list[0] == "#": # Comment.
+ if s < 3:
+ return bytearray() # Nope.
+ elif token_list[1] == "$name": # Meta: name
+ name = cat_list(token_list[2:])
+ elif token_list[1] == "$desc": # Description
+ desc = cat_list(token_list[2:])
+ elif token_list[1] == "$title": # Title list
+ title = token_list[2:]
+ elif token_list[1] == "$ver": # Version
+ ver = token_list[2]
+ elif token_list[1] == "$uuid": # UUID
+ uuid = token_list[2]
+ elif token_list[1] == "$flags": # Flags
+ flags = token_list[2:]
+ elif token_list[1] == "$deps": # Flags
+ deps = token_list[2:]
+
+ return bytearray()
+
+
+ if token_list[0] == "nop": # Nop. Expects 0 args.
+ return bytearray.fromhex("00")
+ elif token_list[0] == "rel": # Rel. Expects one argument. Possibly requires mapping.
+ if s != 2:
+ syn_err("expected one argument")
+
+ index = rel_name(token_list[1])
+ if index == "-1":
+ # TODO - Check if an integer was passed.
+ syn_error("invalid argument")
+
+ return bytearray.fromhex("01" + index)
+ elif token_list[0] == "find":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ # We cut corners and calculate stuff manually.
+ return bytearray.fromhex("02") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+ elif token_list[0] == "back":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ return bytearray.fromhex("03" + token_list[1])
+ elif token_list[0] == "fwd":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ return bytearray.fromhex("04" + token_list[1])
+ elif token_list[0] == "set":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ # We cut corners and calculate stuff manually.
+ return bytearray.fromhex("05") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+ elif token_list[0] == "test":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ # We cut corners and calculate stuff manually.
+ return bytearray.fromhex("06") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+ elif token_list[0] == "jmp":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ if len(token_list[1] == 2):
+ token_list[1] = "00" + token_list[1]
+
+ return bytearray.fromhex("07" + token_list[1])
+ elif token_list[0] == "rewind":
+ return bytearray.fromhex("08")
+ elif token_list[0] == "and":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ # We cut corners and calculate stuff manually.
+ return bytearray.fromhex("09") + bytearray([len(token_list[1]) / 2]) + bytearray.fromhex(token_list[1])
+ elif token_list[0] == "title":
+ if s != 2:
+ syn_err("invalid number of arguments")
+
+ return bytearray.fromhex("0A") + bytearray([len(token_list[1]) / 2 / 8]) + bytearray.fromhex(token_list[1])
+
+def pad_zero_r(x, c):
+ while len(x) < c:
+ x = x + bytearray([0])
+ return x
+
+
+def pad_zero_l(x, c):
+ while len(x) < c:
+ x = bytearray([0]) + x
+ return x
+
+def flag_convert(x):
+ flags = 0
+ for f in x:
+ if f == "require":
+ flags &= 0x1
+ if f == "devmode":
+ flags &= 0x2
+ if f == "noabort":
+ flags &= 0x4
+ return pad_zero_r(bytearray([flags]), 4)
+
+try:
+ # Read input and output files.
+ in_file = sys.argv[1]
+ out_file = sys.argv[2]
+except:
+ usage()
+ exit(1)
+
+size = 0
+
+with open(in_file, "r") as ins:
+ with open(out_file, "wb") as writ:
+ bytecode = bytearray()
+
+ for line in ins:
+ lines += 1
+ tokens = re.split("\s+", line.strip("\n")) # Split by whitespace.
+ bytes = parse_op(tokens) # Parse.
+ if bytes:
+ size += len(bytes)
+ bytecode += bytes
+
+ data = bytearray("AIDA")
+ data += bytearray.fromhex(ver)
+ data += pad_zero_r(bytearray(name), 64)
+ data += pad_zero_r(bytearray(desc), 256)
+ data += pad_zero_r(bytearray.fromhex(uuid), 8)
+ data += flag_convert(flags)
+ data += struct.pack('I', len(title))
+ data += struct.pack('I', len(deps))
+ data += struct.pack('I', size)
+ for f in title:
+ data += bytearray.fromhex(f)
+ for f in deps:
+ data += pad_zero_r(bytearray.fromhex(f), 8)
+ data += bytecode
+ writ.write(data)
+
--- /dev/null
+.PHONY: all
+all: $(patsubst %.pco, %.vco, $(wildcard *.pco))
+
+%.vco: %.pco
+ ../host/bytecode_asm.py $< $@
+
+clean:
+ rm -f *.vco
--- /dev/null
+# $name Title Downgrade Fix
+# $desc Removes added checks to prevent downgrade (in 11.0)
+# $ver 01
+# $uuid 00
+# $flags require
+
+# Anti-anti-downgrade fix.
+
+# Relative to process9.
+rel native_p9
+
+# We want to patch the fifth byte of this pattern.
+find 890a814202D2
+fwd 05
+set E0
--- /dev/null
+# $name Block Cart Updates
+# $desc Blocks update checks on cartridges (and also allows region free carts, as a byproduct.)
+# $title 0004013000008002
+# $ver 01
+# $uuid 01
+
+find 0C18E1D8
+set 0B1821C8
+
+find 0C18E1D8
+set 0B1821C8
--- /dev/null
+# $name Block eShop Updates
+# $desc Prevents eShop from checking for system updates.
+# $title 0004013000002C02
+# $ver 01
+# $uuid 02
+
+find 30B5F1B0
+set 002008607047
--- /dev/null
+# $name Block NIM updates
+# $desc Prevents NIM from downloading system updates.
+# $title 0004013000002C02
+# $ver 01
+# $uuid 03
+
+find 25790B99
+set E3A0
--- /dev/null
+# $name ErrDisp devmode
+# $desc Forces ErrDisp into displaying developer info without developer UNITINFO. Doesn't break eShop.
+# $title 0004003000008A02
+# $ver 01
+# $uuid 04
+# $flags devmode
+
+find 1400D0E5DB9A9FED
+set 0000A0E3
+
+find 1400D0E5010010E3
+set 0000A0E3
+find 1400D0E5010010E3
+set 0000A0E3
+find 1400D0E5010010E3
+set 0000A0E3
--- /dev/null
+# $name Fake Friends module version
+# $desc Allows going online without the latest firmware.
+# $title 0004013000003202
+# $ver 01
+# $uuid 05
+# $flags noabort
+
+find E01EFF2FE1010101
+fwd 09
+set 06
--- /dev/null
+# $name ARM11 XN Disable
+# $desc Disables the XN bit on the ARM11 kernel to allow executing code from all memory. May be unsafe.
+# $ver 01
+# $uuid 06
+# $flags devmode
+
+# Relative to NATIVE_FIRM, section index [1]
+rel native_s1
+
+# Find this byte string.
+find 9705000015E40000
+
+# Move backwards until we find what we want.
+# LOOP: (3)
+ back 01
+ test 00016416
+ jmp 0007
+ jmp 0003
+
+# END LOOP: (7)
+ and efff
--- /dev/null
+# $name MSET Version
+# $desc Replaces 'Ver.' with CFW info.
+# $title 0004001000021000 0004001000020000 0004001000022000 0004001000026000 0004001000027000 0004001000028000
+# $ver 01
+# $uuid 07
+
+# u"Ver."
+find 5600650072002e00
+
+# u".hax"
+set 2e006800610078
--- /dev/null
+# $name FIRM Protect
+# $desc Prevents writing FIRM to the NAND during updates.
+# $ver 01
+# $uuid 08
+# $flags require
+
+rel native_p9
+# String: 'exe:'
+find 6578653a
+back 01
+back ff
+find 002801DA
+set 0020C046
--- /dev/null
+# $name Region free HOME
+# $desc Allows launching installed software from any region.
+# $title 0004003000008F02 0004003000008202 0004003000009802 000400300000A102 000400300000A902 000400300000B102
+# $ver 01
+# $uuid 09
+
+find 000055E30110A0E3
+# 16
+back 11
+set 0100A0E31EFF2FE1
--- /dev/null
+# $name RO signature fix
+# $desc Allows usage of unsigned CRO files. This is useful for romhacks.
+# $title 0004013000003702
+# $ver 01
+# $uuid 0a
+
+find 30402DE90250A0E1
+set 0000A0E31EFF2FE1
+
+find 30402DE924D04DE2
+set 0000A0E31EFF2FE1
+
+find F84F2DE90170A0E1
+set 0000A0E31EFF2FE1
--- /dev/null
+# $name SecureInfo_A Signature Fix
+# $desc Allows using unsigned or improperly signed SecureInfo_A files. Useful for region changes.
+# $title 0004013000001702
+# $ver 01
+# $uuid 0b
+
+find 06461048FC
+set 0026
--- /dev/null
+# $name Signature Fix
+# $desc Disables signature checks on content.
+# $ver 01
+# $uuid 0c
+# $flags require
+
+# Signature patch.
+
+# Relative to exefs.
+rel native_p9
+
+# Pattern one.
+find c01c76e7
+set 0020
+
+# Rewind to beginning.
+rewind
+
+# Pattern 2.
+find b5224d0c
+set 00207047
--- /dev/null
+# $name Developer UNITINFO
+# $desc Imitates a panda's configuration. For the average user, loader ErrDisp is enough.
+# $ver 01
+# $uuid 0d
+# $flags devmode
+
+rel native_s2
+find 0110A013
+fwd 03
+set E3
// Structure of a patch file.
struct system_patch
{
- char magic[4]; // "AIDA" for shits and giggles and because we like .hack.
- uint32_t patch_ver; // Version of the patch itself.
- uint32_t load_ver; // Version of the CFW the patch is intended for.
+ char magic[4]; // "AIDA" for shits and giggles and because we like .hack.
+ uint32_t version; // Version of the patch itself.
- char name[64]; // User-readable name for patch in menu.
- char desc[256]; // User-readable description for patch in menu.
- uint64_t patch_id; // Unique ID for patch. Each unique patch should provide
- // a unique ID.
+ char name[64]; // User-readable name for patch in menu.
+ char desc[256]; // User-readable description for patch in menu.
+ uint64_t uuid; // Unique ID for patch. Each unique patch should provide
+ // a unique ID.
- uint64_t tid; // What title this patch is intended for. Certain values are
- // specially handled.
+ uint8_t flags; // Extra flags for patch.
- uint8_t extra_flags; // Extra flags for patch.
+ #define PATCH_FLAG_REQUIRE (1 << 0) // Force enable patch unless 'Unsafe Options' is checked.
+ #define PATCH_FLAG_DEVMODE (1 << 1) // Require 'Developer Options' to be checked.
+ #define PATCH_FLAG_NOABORT (1 << 2) // Don't abort on error.
- uint64_t depends[16]; // What patches need to be applied for this patch to
- // be applied; as unique IDs
+ uint32_t titles; // What title this patch is intended for. Certain values are
+ // specially handled.
- uint32_t patch_size; // Size of the following patch data.
- // uint8_t patch_data[]; // The data for the patch. This is a
- // compiled binary for ARM9/11.
-} __attribute__((packed));
+ uint32_t depends; // How many deps there are.
-struct patch_opcode
-{
+ uint32_t size; // Size of the patch bytecode in bytes.
+
+ // This stuff needs to be read not as part of the struct, but is technically part of it.
+
+ // uint64_t tids[titles] // TitleIDs.
+ // uint64_t deps[depends] // Dependencies as uuid refs.
+ // uint8_t patch_data[size] // Patch data.
} __attribute__((packed));
#endif