Port and document make_patch.c features needed for pokered

This commit is contained in:
Rangi 2022-03-26 17:13:50 -04:00
parent 5206ac6836
commit 5a7ddb3272
3 changed files with 43 additions and 26 deletions

View File

@ -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}`". 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: **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. - 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. - 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. - "`@`" evaluates as the address or absolute offset of the current patch/hook label, depending on the command.
Any other characters are output as-is. Any other characters are output as-is.
@ -117,8 +117,8 @@ For example, "`{db 0xEF}`" outputs "`a1:ef`".
### <code>{hex <i>arg</i>[ <i>padding</i>]}</code> ### <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. 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.

View File

@ -50,7 +50,9 @@ void symbol_append(struct Symbol **symbols, const char *name, int bank, int addr
size_t name_len = strlen(name) + 1; size_t name_len = strlen(name) + 1;
struct Symbol *symbol = xmalloc(sizeof(*symbol) + name_len); struct Symbol *symbol = xmalloc(sizeof(*symbol) + name_len);
symbol->address = address; 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); memcpy(symbol->name, name, name_len);
symbol->next = *symbols; symbol->next = *symbols;
*symbols = symbol; *symbols = symbol;
@ -154,13 +156,22 @@ void parse_symbols(const char *filename, struct Symbol **symbols) {
buffer_free(buffer); 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) { 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 // Comparison operators for "ConditionValueB" evaluate to their particular values
static const char *comparisons[] = {"==", ">", "<", ">=", "<=", "!=", "||"}; int op = vstrfind(arg, "==", ">", "<", ">=", "<=", "!=", "||");
for (unsigned int i = 0; i < sizeof(comparisons) / sizeof(*comparisons); i++) { if (op >= 0) {
if (!strcmp(arg, comparisons[i])) { return op == 6 ? 0x11 : op; // "||" is 0x11
return i == 6 ? 0x11 : i; // "||" is 0x11
}
} }
// Literal numbers evaluate to themselves // Literal numbers evaluate to themselves
@ -213,7 +224,7 @@ void interpret_command(char *command, const struct Symbol *current_hook, const s
} }
// Use the arguments // 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) { if (argc > 2) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command); error_exit("Error: Invalid arguments for command: \"%s\"\n", command);
} }
@ -241,7 +252,9 @@ void interpret_command(char *command, const struct Symbol *current_hook, const s
modified = c != getc(orig_rom); modified = c != getc(orig_rom);
fprintf(output, isupper((unsigned)command[0]) ? "0x%02X" : "0x%02x", c); fprintf(output, isupper((unsigned)command[0]) ? "0x%02X" : "0x%02x", c);
} else { } 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++) { for (int i = 0; i < length; i++) {
if (i) { if (i) {
putc(' ', output); putc(' ', output);
@ -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); 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) { if (argc < 1) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command); 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++) { for (int i = 0; i < argc; i++) {
int value = parse_arg_value(argv[i], false, symbols, current_hook->name); int value = parse_arg_value(argv[i], false, symbols, current_hook->name);
if (value > 0xffff) { if (value > 0xffff) {
@ -271,7 +286,7 @@ 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); 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) { if (argc != 1) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command); error_exit("Error: Invalid arguments for command: \"%s\"\n", command);
} }
@ -279,22 +294,24 @@ void interpret_command(char *command, const struct Symbol *current_hook, const s
if (value > 0xff) { if (value > 0xff) {
error_exit("Error: Invalid value for \"%s\" argument: 0x%x\n", command, value); 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); 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) { if (argc != 1 && argc != 2) {
error_exit("Error: Invalid arguments for command: \"%s\"\n", command); 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; 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); 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); 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); 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); fprintf(output, "0x%0*x%03X", padding - 3, value >> 12, value & 0xfff);
} else { } else {
fprintf(output, isupper((unsigned)command[0]) ? "0x%0*X" : "0x%0*x", padding, value); fprintf(output, isupper((unsigned)command[0]) ? "0x%0*X" : "0x%0*x", padding, value);

View File

@ -263,7 +263,7 @@ Fixcode={patch}
Mode = 6 Mode = 6
Type = 0 Type = 0
Address = {hex @} Address = {hex @}
MemAddress={hex hJoyPressed} MemAddress={hex~ hJoyPressed}
Fixcode={db NO_INPUT} Fixcode={db NO_INPUT}
ConditionType = 0 ConditionType = 0
ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuSelection wMenuCursorY hJoyPressed hJoyPressed hJoyPressed hJoyPressed} ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuSelection wMenuCursorY hJoyPressed hJoyPressed hJoyPressed hJoyPressed}
@ -278,7 +278,7 @@ ConditionValueC = {dws_ 0xdd 0xd3 A_BUTTON 0x
Mode = 6 Mode = 6
Type = 0 Type = 0
Address = {hex @} Address = {hex @}
MemAddress={hex hJoyPressed} MemAddress={hex~ hJoyPressed}
Fixcode={db NO_INPUT} Fixcode={db NO_INPUT}
ConditionType = 0 ConditionType = 0
ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuCursorY wMapGroup wMapNumber wYCoord wXCoord hJoyPressed hJoyPressed hJoyPressed hJoyPressed} ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wMenuCursorY wMapGroup wMapNumber wYCoord wXCoord hJoyPressed hJoyPressed hJoyPressed hJoyPressed}
@ -321,7 +321,7 @@ Fixcode={patch}
Mode = 6 Mode = 6
Type = 0 Type = 0
Address = {hex @} Address = {hex @}
MemAddress={hex hJoyPressed} MemAddress={hex~ hJoyPressed}
Fixcode={db NO_INPUT} Fixcode={db NO_INPUT}
ConditionType = 0 ConditionType = 0
ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wDexArrowCursorPosIndex hJoyPressed hJoyPressed hJoyPressed hJoyPressed} ConditionValueA = {dws_ wWindowStackPointer wWindowStackPointer+1 wMenuJoypad wMenuSelection wDexArrowCursorPosIndex hJoyPressed hJoyPressed hJoyPressed hJoyPressed}