Skip to content

Instantly share code, notes, and snippets.

@garrettjoecox
Last active June 17, 2023 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garrettjoecox/0f807e0e2f654cf19686b291bbd20c23 to your computer and use it in GitHub Desktop.
Save garrettjoecox/0f807e0e2f654cf19686b291bbd20c23 to your computer and use it in GitHub Desktop.
Replaces manual flag get/sets and hardcoded numbers with a uniform functionCall(MACRO)
const fs = require('fs');
const path = require('path');
// Example 1
// Before: gSaveContext.eventChkInf[4] |= 0x8
// After: Flags_SetEventChkInf(EVENTCHKINF_43)
// Example 2
// Before: Flags_GetInfTable(4)
// After: Flags_GetInfTable(INFTABLE_04)
const macroTable = {};
const C_FILES_REGEX = /.+\.c$/;
const SAVE_CONTEXT_REGEX = /gSaveContext\.(eventChkInf|itemGetInf|infTable)\[(0x[\da-f]+|\d+)\] (\|= |& |&= ~)(0x[\da-f]+|\d+)/gi;
const FLAGS_REGEX = /Flags_(?:Get|Set|Unset)(EventChkInf|ItemGetInf|InfTable)\((0x[\da-f]+|\d+)\)/gi;
const UNNECESSARY_PARENS_REGEX = /!\((Flags_(?:Get|Set|Unset)(?:EventChkInf|ItemGetInf|InfTable)\((?:\w+)\))\)/gi;
const ACTION_GET = '& ';
const ACTION_SET = '|= ';
const ACTION_CLEAR = '&= ~';
function convertToHex(value) {
return parseInt(value).toString(16).toUpperCase();
}
function convertBitToHex(value) {
const bit = Math.log2(parseInt(value));
if (bit % 1 !== 0) return "NaN";
return bit.toString(16).toUpperCase();
}
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
// Regex patterns to match and replace
function processFile(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
let updatedContent = content;
updatedContent = content.replace(SAVE_CONTEXT_REGEX, (match, table, index, action, value) => {
if (convertBitToHex(value) === "NaN" || convertToHex(index) === "NaN") {
console.warn(`Invalid value: ${match} @ ${filePath}`);
return match;
}
let key = `${table.toUpperCase()}_${convertToHex(index)}${convertBitToHex(value)}`;
if (!macroTable[key]) {
console.warn(`No macro found for ${key} @ ${filePath}`);
return match;
}
key = macroTable[key];
switch (action) {
case ACTION_GET:
return `Flags_Get${capitalizeFirstLetter(table)}(${key})`;
case ACTION_SET:
return `Flags_Set${capitalizeFirstLetter(table)}(${key})`;
case ACTION_CLEAR:
return `Flags_Unset${capitalizeFirstLetter(table)}(${key})`;
default:
console.warn(`Invalid action: ${action} ${key} @ ${filePath}`);
return match;
}
});
updatedContent = updatedContent.replace(FLAGS_REGEX, (match, table, value) => {
if (convertToHex(value) === "NaN") {
console.warn(`Invalid value: ${match} @ ${filePath}`);
return match;
}
let key = `${table.toUpperCase()}_${convertToHex(value).length === 1 ? '0' : ''}${convertToHex(value)}`;
if (!macroTable[key]) {
console.warn(`No macro found for ${key} @ ${filePath}`);
return match;
}
key = macroTable[key];
return match.replace(/\((0x[\da-f]+|\d+)\)/gi, `(${key})`);
});
updatedContent = updatedContent.replace(UNNECESSARY_PARENS_REGEX, (match, value) => `!${value}`);
fs.writeFileSync(filePath, updatedContent, 'utf8');
}
// Recursive function to process files in a directory
function processFiles(dirPath) {
const files = fs.readdirSync(dirPath);
files.forEach((file) => {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
processFiles(filePath);
} else if (C_FILES_REGEX.test(file)) {
processFile(filePath);
}
});
}
function prepareMacroTable() {
const content = fs.readFileSync('soh/include/z64save.h', 'utf8');
const regexPattern = /#define ((EVENTCHKINF|ITEMGETINF|INFTABLE)_\w+) 0x([\da-f]+)/gi;
let match = regexPattern.exec(content);
while (match !== null) {
const [, macro, table, value] = match;
macroTable[`${table}_${value}`] = macro;
match = regexPattern.exec(content);
}
}
// Start processing files in the directory
prepareMacroTable();
processFiles(__dirname);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment