Skip to content

Commit

Permalink
Port and document make_patch.c features needed for pokered
Browse files Browse the repository at this point in the history
  • Loading branch information
Rangi42 committed Mar 26, 2022
1 parent 5206ac6 commit 5a7ddb3
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 26 deletions.
10 changes: 5 additions & 5 deletions docs/vc_patch.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ Patch names may designate an alternate for the label with an at-sign "`@`". This

Commands are interpreted with a series of arguments, separated by whitespace (spaces, tabs, or newlines). Leading and trailing whitespace is ignored; for example, "`{ hex @ 4 }`" is interpreted the same as "`{hex @ 4}`".

Some commands may output a **value series**, which is a series of two-digit hexadecimal bytes separated by spaces, preceded by a decimal count: "<code>a*N*: <i>v1</i> <i>v2</i> [...] <i>vN</i></code>".
Command names have variants to allow reproducing the exact formatting in a `.patch` file. If the command name is all lowercase, the output byte values use lowercase for hexadecimal digits A-F; if it is all uppercase, they use uppercase.

Some command names have variants to allow reproducing the exact formatting in a `.patch` file. If the command name is all lowercase, the output byte values use lowercase for hexadecimal digits A-F; if it is all uppercase, they use uppercase. For commands which output a value series, if the command name ends in an underscore, a space is output after the colon preceding the values; if not, then it is not.
Some commands may output a **value series**, which is a series of two-digit hexadecimal bytes separated by spaces, preceded by a decimal count and a colon "`:`": "<code>a*N*: <i>v1</i> <i>v2</i> [...] <i>vN</i></code>". These commands have additional variants: if the command name ends in a slash "`/`", the count and colon are not output; or else, if it ends in an underscore "`_`", a space is output after the colon; otherwise, the count and colon are output without a space.

**Arguments** evaluate to numeric values. They may be any of the following:

- Literal numbers in decimal (base 10, e.g. "`42`"), hexadecimal (base 16, e.g. "`0x2a`"), or octal (base 8, e.g. "`052`"). They may start with a plus sign "`+`". Numbers should not be negative.
- Comparison operators: "`==`" is 0, "`>`" is 1, "`<`" is 2, "`>=`" is 3, "`<=`" is 4, "`!=`" is 5, and "`||`" is 0x11.
- Symbol names from the two `.sym` files provided to `make_patch` may evaluate as their bank-relative address, or their absolute offset in the ROM, depending on the command. They may also be followed by a plus sign and a literal number that gets added to the value.
- Symbol names from the two `.sym` files provided to `make_patch` may evaluate as their relative address or their absolute offset, depending on the command. (Addresses are relative to the symbol's bank for ROM addresses, or to 0x8000, the start of all RAM, for RAM addresses.) They may also be followed by a plus sign and a literal number that gets added to the value.
- "`@`" evaluates as the address or absolute offset of the current patch/hook label, depending on the command.

Any other characters are output as-is.
Expand Down Expand Up @@ -117,8 +117,8 @@ For example, "`{db 0xEF}`" outputs "`a1:ef`".

### <code>{hex <i>arg</i>[ <i>padding</i>]}</code>

Outputs its first argument as a hexadecimal number. An optional second argument is the minimum length in digits; values shorter than it will be padded with leading zeros. Symbol names or "`@`" are evaluated as their absolute offset.
Outputs its first argument as a hexadecimal number. An optional second argument is the minimum length in digits; values shorter than it will be padded with leading zeros. Symbol names or "`@`" are evaluated as their absolute offset, or as their relative address if the command name ends in a tilde "`~`".

For example, "`{hex 0xabcd 5}`" outputs "`0x0abcd`".
For example, if "`{hex @}`" outputs "`0x6789`", then "`{hex @+1 5}`" outputs "`0x0678a`".

This command has extra variants to reproduce inconsistent output casing: "`Hex`" prints the last three digits in lowercase and the rest uppercase; "`HEx`" prints the last two digits in lowercase and the rest uppercase; "`hEX`" prints the last three digits in uppercase and the rest lowercase; and "`heX`" prints the last two digits in uppercase and the rest lowercase.
53 changes: 35 additions & 18 deletions tools/make_patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ void symbol_append(struct Symbol **symbols, const char *name, int bank, int addr
size_t name_len = strlen(name) + 1;
struct Symbol *symbol = xmalloc(sizeof(*symbol) + name_len);
symbol->address = address;
symbol->offset = bank > 0 && address < 0x8000 ? address + (bank - 1) * 0x4000 : address;
symbol->offset = address < 0x8000
? (bank > 0 ? address + (bank - 1) * 0x4000 : address) // ROM addresses are relative to their bank
: address - 0x8000; // RAM addresses are relative to the start of all RAM
memcpy(symbol->name, name, name_len);
symbol->next = *symbols;
*symbols = symbol;
Expand Down Expand Up @@ -154,13 +156,22 @@ void parse_symbols(const char *filename, struct Symbol **symbols) {
buffer_free(buffer);
}

int strfind(const char *s, const char *list[], int count) {
for (int i = 0; i < count; i++) {
if (!strcmp(s, list[i])) {
return i;
}
}
return -1;
}

#define vstrfind(s, ...) strfind(s, (const char *[]){__VA_ARGS__}, sizeof (const char *[]){__VA_ARGS__} / sizeof(const char *))

int parse_arg_value(const char *arg, bool absolute, const struct Symbol *symbols, const char *patch_name) {
// Comparison operators for "ConditionValueB" evaluate to their particular values
static const char *comparisons[] = {"==", ">", "<", ">=", "<=", "!=", "||"};
for (unsigned int i = 0; i < sizeof(comparisons) / sizeof(*comparisons); i++) {
if (!strcmp(arg, comparisons[i])) {
return i == 6 ? 0x11 : i; // "||" is 0x11
}
int op = vstrfind(arg, "==", ">", "<", ">=", "<=", "!=", "||");
if (op >= 0) {
return op == 6 ? 0x11 : op; // "||" is 0x11
}

// Literal numbers evaluate to themselves
Expand Down Expand Up @@ -213,7 +224,7 @@ void interpret_command(char *command, const struct Symbol *current_hook, const s
}

// Use the arguments
if (!strcmp(command, "patch") || !strcmp(command, "PATCH") || !strcmp(command, "patch_") || !strcmp(command, "PATCH_")) {
if (vstrfind(command, "patch", "PATCH", "patch_", "PATCH_", "patch/", "PATCH/") >= 0) {
if (argc > 2) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command);
}
Expand Down Expand Up @@ -241,7 +252,9 @@ void interpret_command(char *command, const struct Symbol *current_hook, const s
modified = c != getc(orig_rom);
fprintf(output, isupper((unsigned)command[0]) ? "0x%02X" : "0x%02x", c);
} else {
fprintf(output, command[strlen(command) - 1] == '_' ? "a%d: " : "a%d:", length);
if (command[strlen(command) - 1] != '/') {
fprintf(output, command[strlen(command) - 1] == '_' ? "a%d: " : "a%d:", length);
}
for (int i = 0; i < length; i++) {
if (i) {
putc(' ', output);
Expand All @@ -255,11 +268,13 @@ void interpret_command(char *command, const struct Symbol *current_hook, const s
fprintf(stderr, PROGRAM_NAME ": Warning: \"vc_patch %s\" doesn't alter the ROM\n", current_hook->name);
}

} else if (!strcmp(command, "dws") || !strcmp(command, "DWS") || !strcmp(command, "dws_") || !strcmp(command, "DWS_")) {
} else if (vstrfind(command, "dws", "DWS", "dws_", "DWS_", "dws/", "DWS/") >= 0) {
if (argc < 1) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command);
}
fprintf(output, command[strlen(command) - 1] == '_' ? "a%d: " : "a%d:", argc * 2);
if (command[strlen(command) - 1] != '/') {
fprintf(output, command[strlen(command) - 1] == '_' ? "a%d: " : "a%d:", argc * 2);
}
for (int i = 0; i < argc; i++) {
int value = parse_arg_value(argv[i], false, symbols, current_hook->name);
if (value > 0xffff) {
Expand All @@ -271,30 +286,32 @@ void interpret_command(char *command, const struct Symbol *current_hook, const s
fprintf(output, isupper((unsigned)command[0]) ? "%02X %02X": "%02x %02x", value & 0xff, value >> 8);
}

} else if (!strcmp(command, "db") || !strcmp(command, "DB") || !strcmp(command, "db_") || !strcmp(command, "DB_")) {
} else if (vstrfind(command, "db", "DB", "db_", "DB_", "db/", "DB/") >= 0) {
if (argc != 1) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command);
}
int value = parse_arg_value(argv[0], false, symbols, current_hook->name);
if (value > 0xff) {
error_exit("Error: Invalid value for \"%s\" argument: 0x%x\n", command, value);
}
fputs(command[strlen(command) - 1] == '_' ? "a1: " : "a1:", output);
if (command[strlen(command) - 1] != '/') {
fputs(command[strlen(command) - 1] == '_' ? "a1: " : "a1:", output);
}
fprintf(output, isupper((unsigned)command[0]) ? "%02X" : "%02x", value);

} else if (!strcmp(command, "hex") || !strcmp(command, "HEX") || !strcmp(command, "HEx") || !strcmp(command, "Hex") || !strcmp(command, "heX") || !strcmp(command, "hEX")) {
} else if (vstrfind(command, "hex", "HEX", "HEx", "Hex", "heX", "hEX", "hex~", "HEX~", "HEx~", "Hex~", "heX~", "hEX~") >= 0) {
if (argc != 1 && argc != 2) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command);
}
int value = parse_arg_value(argv[0], true, symbols, current_hook->name);
int value = parse_arg_value(argv[0], command[strlen(command) - 1] != '~', symbols, current_hook->name);
int padding = argc > 1 ? parse_number(argv[1], 0) : 2;
if (!strcmp(command, "HEx")) {
if (vstrfind(command, "HEx", "HEx~") >= 0) {
fprintf(output, "0x%0*X%02x", padding - 2, value >> 8, value & 0xff);
} else if (!strcmp(command, "Hex")) {
} else if (vstrfind(command, "Hex", "Hex~") >= 0) {
fprintf(output, "0x%0*X%03x", padding - 3, value >> 12, value & 0xfff);
} else if (!strcmp(command, "heX")) {
} else if (vstrfind(command, "heX", "heX~") >= 0) {
fprintf(output, "0x%0*x%02X", padding - 2, value >> 8, value & 0xff);
} else if (!strcmp(command, "hEX")) {
} else if (vstrfind(command, "hEX", "hEX~") >= 0) {
fprintf(output, "0x%0*x%03X", padding - 3, value >> 12, value & 0xfff);
} else {
fprintf(output, isupper((unsigned)command[0]) ? "0x%0*X" : "0x%0*x", padding, value);
Expand Down
6 changes: 3 additions & 3 deletions vc/pokecrystal11.patch.template
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ Fixcode={patch}
Mode = 6
Type = 0
Address = {hex @}
MemAddress={hex hJoyPressed}
MemAddress={hex~ hJoyPressed}
Fixcode={db NO_INPUT}
ConditionType = 0
ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuSelection wMenuCursorY hJoyPressed hJoyPressed hJoyPressed hJoyPressed}
Expand All @@ -278,7 +278,7 @@ ConditionValueC = {dws_ 0xdd 0xd3 A_BUTTON 0x
Mode = 6
Type = 0
Address = {hex @}
MemAddress={hex hJoyPressed}
MemAddress={hex~ hJoyPressed}
Fixcode={db NO_INPUT}
ConditionType = 0
ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuCursorY wMapGroup wMapNumber wYCoord wXCoord hJoyPressed hJoyPressed hJoyPressed hJoyPressed}
Expand Down Expand Up @@ -321,7 +321,7 @@ Fixcode={patch}
Mode = 6
Type = 0
Address = {hex @}
MemAddress={hex hJoyPressed}
MemAddress={hex~ hJoyPressed}
Fixcode={db NO_INPUT}
ConditionType = 0
ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wDexArrowCursorPosIndex hJoyPressed hJoyPressed hJoyPressed hJoyPressed}
Expand Down

0 comments on commit 5a7ddb3

Please sign in to comment.