diff --git a/CMakeLists.txt b/CMakeLists.txt index 7652aa56c2..2f937f7748 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,4 +101,4 @@ add_subdirectory(third-party/spdlog) # windows memory management lib IF (WIN32) add_subdirectory(third-party/mman) -ENDIF () +ENDIF () \ No newline at end of file diff --git a/decompiler/IR/CfgBuilder.cpp b/decompiler/IR/CfgBuilder.cpp index eca4a3d848..6421869998 100644 --- a/decompiler/IR/CfgBuilder.cpp +++ b/decompiler/IR/CfgBuilder.cpp @@ -1009,4 +1009,4 @@ std::shared_ptr build_cfg_ir(Function& function, } catch (std::runtime_error& e) { return nullptr; } -} \ No newline at end of file +} diff --git a/decompiler/config/jak1_ntsc_black_label.jsonc b/decompiler/config/jak1_ntsc_black_label.jsonc index 5404b03c46..e7dbb862f9 100644 --- a/decompiler/config/jak1_ntsc_black_label.jsonc +++ b/decompiler/config/jak1_ntsc_black_label.jsonc @@ -1,48 +1,56 @@ - - { - "game_version":1, - // the order here matters. KERNEL and GAME should go first - "dgo_names":["CGO/KERNEL.CGO", "CGO/GAME.CGO"], - /*, "CGO/ENGINE.CGO" - , "CGO/ART.CGO", "DGO/BEA.DGO", "DGO/CIT.DGO", "CGO/COMMON.CGO", "DGO/DAR.DGO", "DGO/DEM.DGO", - "DGO/FIN.DGO", "DGO/INT.DGO", "DGO/JUB.DGO", "DGO/JUN.DGO", "CGO/JUNGLE.CGO", "CGO/L1.CGO", "DGO/FIC.DGO", - "DGO/LAV.DGO", "DGO/MAI.DGO", "CGO/MAINCAVE.CGO", "DGO/MIS.DGO", "DGO/OGR.DGO", "CGO/RACERP.CGO", "DGO/ROB.DGO", "DGO/ROL.DGO", - "DGO/SNO.DGO", "DGO/SUB.DGO", "DGO/SUN.DGO", "CGO/SUNKEN.CGO", "DGO/SWA.DGO", "DGO/TIT.DGO", "DGO/TRA.DGO", "DGO/VI1.DGO", - "DGO/VI2.DGO", "DGO/VI3.DGO", "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO" - ],*/ + "game_version" : 1, + // the order here matters. KERNEL and GAME should go first + "dgo_names" + : [ "CGO/KERNEL.CGO", "CGO/GAME.CGO" ], + /*, "CGO/ENGINE.CGO" + , "CGO/ART.CGO", "DGO/BEA.DGO", "DGO/CIT.DGO", "CGO/COMMON.CGO", "DGO/DAR.DGO", + "DGO/DEM.DGO", "DGO/FIN.DGO", "DGO/INT.DGO", "DGO/JUB.DGO", "DGO/JUN.DGO", + "CGO/JUNGLE.CGO", "CGO/L1.CGO", "DGO/FIC.DGO", "DGO/LAV.DGO", "DGO/MAI.DGO", + "CGO/MAINCAVE.CGO", "DGO/MIS.DGO", "DGO/OGR.DGO", "CGO/RACERP.CGO", "DGO/ROB.DGO", + "DGO/ROL.DGO", "DGO/SNO.DGO", "DGO/SUB.DGO", "DGO/SUN.DGO", "CGO/SUNKEN.CGO", + "DGO/SWA.DGO", "DGO/TIT.DGO", "DGO/TRA.DGO", "DGO/VI1.DGO", "DGO/VI2.DGO", "DGO/VI3.DGO", + "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO" + ],*/ - "write_disassembly":true, - "write_hex_near_instructions":false, - // if false, skips disassembling object files without functions, as these are usually large and not interesting yet. - "disassemble_objects_without_functions":false, + "write_disassembly" : true, + "write_hex_near_instructions" + : false, + // if false, skips disassembling object files without functions, as these are usually large + // and not interesting yet. + "disassemble_objects_without_functions" : false, - // to write out data of each object file - "write_hexdump":false, - // to write out hexdump on the v3 only, to avoid the huge level data files - "write_hexdump_on_v3_only":true, + // to write out data of each object file + "write_hexdump" + : false, + // to write out hexdump on the v3 only, to avoid the huge level data files + "write_hexdump_on_v3_only" + : true, - // to write out "scripts", which are currently just all the linked lists found - "write_scripts":true, + // to write out "scripts", which are currently just all the linked lists found + "write_scripts" : true, - // Experimental Stuff - "find_basic_blocks":true, + // Experimental Stuff + "find_basic_blocks" : true, - "asm_functions_by_name":[ + "asm_functions_by_name" + : [ // gcommon "min", "max", "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!", // pskernel - "resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception", - "kernel-read", "kernel-read-function", "kernel-write", "kernel-write-function", "kernel-copy-to-kernel-ram", + "resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", + "return-from-exception", "kernel-read", "kernel-read-function", "kernel-write", + "kernel-write-function", "kernel-copy-to-kernel-ram", // this one needs more investigation. nothing looks weird about it but it fails... "camera-change-to", // two back to back arithmetic shifts... - "texture-relocate", + "texture-relocate", - // this one fails due to false compaction where an else case has only a not expression in it. + // this one fails due to false compaction where an else case has only a not expression in + // it. "master-is-hopeful-better?", // fails for unknown reason @@ -51,28 +59,36 @@ // merged right typecase... can probably handle this "cspace-inspect-tree", - // these are all valid, but use short circuiting branches in strange ways. There's probably a few compiler uses that we're not - "(method 21 actor-link-info)","(method 20 actor-link-info)","(method 28 collide-shape-prim-mesh)", "(method 35 collide-shape)", - "debug-menu-item-var-render", "(method 14 level)","add-blue-motion","anim-tester-add-newobj","(method 27 orb-cache-top)", + // these are all valid, but use short circuiting branches in strange ways. There's probably + // a few compiler uses that we're not + "(method 21 actor-link-info)", "(method 20 actor-link-info)", + "(method 28 collide-shape-prim-mesh)", "(method 35 collide-shape)", + "debug-menu-item-var-render", "(method 14 level)", "add-blue-motion", + "anim-tester-add-newobj", "(method 27 orb-cache-top)", // real asm - "cspace<-parented-transformq-joint!", "blerc-a-fragment", "render-boundary-tri", "render-boundary-quad", - "(method 19 collide-shape-prim-sphere)","vector-segment-distance-point!", "exp", "(method 11 collide-mesh-cache)", + "cspace<-parented-transformq-joint!", "blerc-a-fragment", "render-boundary-tri", + "render-boundary-quad", "(method 19 collide-shape-prim-sphere)", + "vector-segment-distance-point!", "exp", "(method 11 collide-mesh-cache)", "(method 13 collide-edge-work)", "ambient-inspect", - "(method 11 cpu-thread)", "atan0", "sincos!", "sincos-rad!", "disasm-dma-list", "vblank-handler", "vif1-handler", - "vif1-handler-debug", "entity-actor-count", "decompress-frame-data-pair-to-accumulator", - "decompress-frame-data-to-accumulator", "normalize-frame-quaternions", "clear-frame-accumulator", - "generic-copy-vtx-dclr-dtex", "generic-no-light-dproc-only", "generic-no-light-proc", "mercneric-bittable-asm", - "generic-tie-decompress", "matrix-axis-sin-cos!", "matrix-axis-sin-cos-vu!", "generic-prepare-dma-single", - "(method 13 collide-shape-prim-sphere)", "(method 14 collide-shape-prim-sphere)", "(method 12 collide-shape-prim-sphere)", - "adgif-shader<-texture-with-update!", "generic-interp-dproc", "sprite-draw-distorters", "draw-bones", "(method 9 collide-mesh-cache)", - "(method 18 collide-shape-prim-sphere)","birth-pickup-at-point", + "(method 11 cpu-thread)", "atan0", "sincos!", "sincos-rad!", "disasm-dma-list", + "vblank-handler", "vif1-handler", "vif1-handler-debug", "entity-actor-count", + "decompress-frame-data-pair-to-accumulator", "decompress-frame-data-to-accumulator", + "normalize-frame-quaternions", "clear-frame-accumulator", "generic-copy-vtx-dclr-dtex", + "generic-no-light-dproc-only", "generic-no-light-proc", "mercneric-bittable-asm", + "generic-tie-decompress", "matrix-axis-sin-cos!", "matrix-axis-sin-cos-vu!", + "generic-prepare-dma-single", "(method 13 collide-shape-prim-sphere)", + "(method 14 collide-shape-prim-sphere)", "(method 12 collide-shape-prim-sphere)", + "adgif-shader<-texture-with-update!", "generic-interp-dproc", "sprite-draw-distorters", + "draw-bones", "(method 9 collide-mesh-cache)", "(method 18 collide-shape-prim-sphere)", + "birth-pickup-at-point", - "collide-do-primitives", "draw-bones-check-longest-edge-asm", - "sp-launch-particles-var", "(method 15 collide-shape-prim-mesh)", "(method 15 collide-shape-prim-sphere)", - "(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", "dma-sync-hang", "generic-no-light-dproc", - "dma-sync-fast", "bsp-camera-asm", - "generic-none-dma-wait", "unpack-comp-rle", "level-remap-texture", "(method 10 collide-edge-hold-list)" - ] -} \ No newline at end of file + "collide-do-primitives", "draw-bones-check-longest-edge-asm", "sp-launch-particles-var", + "(method 15 collide-shape-prim-mesh)", "(method 15 collide-shape-prim-sphere)", + "(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", + "dma-sync-hang", "generic-no-light-dproc", "dma-sync-fast", "bsp-camera-asm", + "generic-none-dma-wait", "unpack-comp-rle", "level-remap-texture", + "(method 10 collide-edge-hold-list)" + ] +} diff --git a/decompiler/util/LispPrint.cpp b/decompiler/util/LispPrint.cpp new file mode 100644 index 0000000000..eea0c0ffa5 --- /dev/null +++ b/decompiler/util/LispPrint.cpp @@ -0,0 +1,525 @@ +#include +#include +#include + +//////// HACK - symbol table now looks up by string, which makes it really stupid and store +// all strings twice. +// should probably just remove it + +/*! + * String interning + */ +std::string* SymbolTable::intern(const std::string& str) { + if (map.find(str) == map.end()) { + auto* new_string = new std::string(str); + map[str] = new_string; + return new_string; + } else { + return map[str]; + } +} + +/*! + * Global interned string table + */ +SymbolTable gSymbolTable; + +SymbolTable::SymbolTable() { + empty_pair = std::make_shared
(); + empty_pair->kind = FormKind::EMPTY_LIST; +} + +SymbolTable::~SymbolTable() { + for (const auto& kv : map) + delete kv.second; +} + +/*! + * Convert a form to a one-line string. + */ +std::string Form::toStringSimple() { + std::string result; + buildStringSimple(result); + return result; +} + +void Form::buildStringSimple(std::string& str) { + std::vector tokens; + toTokenList(tokens); + for (auto& token : tokens) { + switch (token.kind) { + case TokenKind::WHITESPACE: + str.push_back(' '); + break; + case TokenKind::SYMBOL: + str.append(*token.str); + break; + case TokenKind::OPEN_PAREN: + str.push_back('('); + break; + case TokenKind::DOT: + str.push_back('.'); + break; + case TokenKind::CLOSE_PAREN: + str.push_back(')'); + break; + case TokenKind::EMPTY_PAIR: + str.append("()"); + break; + case TokenKind::SPECIAL_SYMBOL: + str.append(*token.str); + break; + default: + throw std::runtime_error("buildStringSimple unknown token kind"); + } + } +} + +void Form::toTokenList(std::vector& tokens) { + switch (kind) { + case FormKind::SYMBOL: + tokens.emplace_back(TokenKind::SYMBOL, symbol); + break; + case FormKind::PAIR: { + tokens.emplace_back(TokenKind::OPEN_PAREN); + Form* toPrint = this; + for (;;) { + if (toPrint->kind == FormKind::PAIR) { + toPrint->pair[0]->toTokenList(tokens); // print CAR + toPrint = toPrint->pair[1].get(); + if (toPrint->kind == FormKind::EMPTY_LIST) { + tokens.emplace_back(TokenKind::CLOSE_PAREN); + return; + } else { + tokens.emplace_back(TokenKind::WHITESPACE); + } + } else { // not a proper list! + tokens.emplace_back(TokenKind::DOT); + tokens.emplace_back(TokenKind::WHITESPACE); + toPrint->toTokenList(tokens); + tokens.emplace_back(TokenKind::CLOSE_PAREN); + return; + } + } + } break; + case FormKind::EMPTY_LIST: + tokens.emplace_back(TokenKind::EMPTY_PAIR); + break; + default: + throw std::runtime_error("unhandled form type in buildSimpleString"); + break; + } +} + +/////////////////// +// Pretty Printer +/////////////////// + +/*! + * Linked list node representing a token in the output (whitespace, paren, newline, etc) + */ +struct PrettyPrinterNode { + FormToken* tok = nullptr; // if we aren't a newline, we will have a token. + int line = -1; // line that token occurs on. undef for newlines + int lineIndent = -1; // indent of line. only valid for first token in the line + int offset = -1; // offset of beginning of token from left margin + int specialIndentDelta = 0; + bool is_line_separator = false; // true if line separator (not a token) + PrettyPrinterNode *next = nullptr, *prev = nullptr; // linked list + PrettyPrinterNode* paren = + nullptr; // pointer to open paren if in parens. open paren points to close and vice versa + explicit PrettyPrinterNode(FormToken& _tok) { tok = &_tok; } + PrettyPrinterNode() = default; +}; + +/*! + * Splice in a line break after the given node, it there isn't one already and if it isn't the last + * node. + */ +static void insertNewlineAfter(PrettyPrinterNode* node, int specialIndentDelta) { + if (node->next && !node->next->is_line_separator) { + auto* nl = new PrettyPrinterNode; + auto* next = node->next; + node->next = nl; + nl->prev = node; + nl->next = next; + next->prev = nl; + nl->is_line_separator = true; + nl->specialIndentDelta = specialIndentDelta; + } +} + +/*! + * Splice in a line break before the given node, if there isn't one already and if it isn't the + * first node. + */ +static void insertNewlineBefore(PrettyPrinterNode* node, int specialIndentDelta) { + if (node->prev && !node->prev->is_line_separator) { + auto* nl = new PrettyPrinterNode; + auto* prev = node->prev; + prev->next = nl; + nl->prev = prev; + nl->next = node; + node->prev = nl; + nl->is_line_separator = true; + nl->specialIndentDelta = specialIndentDelta; + } +} + +/*! + * Break a list across multiple lines. This is the fundamental reducing operation of this algorithm + */ +static void breakList(PrettyPrinterNode* leftParen) { + assert(!leftParen->is_line_separator); + assert(leftParen->tok->kind == TokenKind::OPEN_PAREN); + auto* rp = leftParen->paren; + assert(rp->tok->kind == TokenKind::CLOSE_PAREN); + + for (auto* n = leftParen->next; n && n != rp; n = n->next) { + if (!n->is_line_separator) { + if (n->tok->kind == TokenKind::OPEN_PAREN) { + n = n->paren; + assert(n->tok->kind == TokenKind::CLOSE_PAREN); + insertNewlineAfter(n, 0); + } else if (n->tok->kind != TokenKind::WHITESPACE) { + assert(n->tok->kind != TokenKind::CLOSE_PAREN); + insertNewlineAfter(n, 0); + } + } + } +} + +/*! + * Compute proper line numbers, offsets, and indents for a list of tokens with newlines + * Will add newlines for close parens if needed. + */ +static PrettyPrinterNode* propagatePretty(PrettyPrinterNode* list, int line_length) { + // propagate line numbers + PrettyPrinterNode* rv = nullptr; + int line = list->line; + for (auto* n = list; n; n = n->next) { + if (n->is_line_separator) { + line++; + } else { + n->line = line; + // add the weird newline. + if (n->tok->kind == TokenKind::CLOSE_PAREN) { + if (n->line != n->paren->line) { + if (n->prev && !n->prev->is_line_separator) { + insertNewlineBefore(n, 0); + line++; + } + if (n->next && !n->next->is_line_separator) { + insertNewlineAfter(n, 0); + } + } + } + } + } + + // compute offsets and indents + std::vector indentStack; + indentStack.push_back(0); + int offset = 0; + PrettyPrinterNode* line_start = list; + bool previous_line_sep = false; + for (auto* n = list; n; n = n->next) { + if (n->is_line_separator) { + previous_line_sep = true; + offset = indentStack.back() += n->specialIndentDelta; + } else { + if (previous_line_sep) { + line_start = n; + n->lineIndent = offset; + previous_line_sep = false; + } + + n->offset = offset; + offset += n->tok->toString().length(); + if (offset > line_length && !rv) + rv = line_start; + if (n->tok->kind == TokenKind::OPEN_PAREN) { + if (!n->prev || n->prev->is_line_separator) { + indentStack.push_back(offset + 1); + } else { + indentStack.push_back(offset - 1); + } + } + + if (n->tok->kind == TokenKind::CLOSE_PAREN) { + indentStack.pop_back(); + } + } + } + return rv; +} + +/*! + * Get the token on the start of the next line. nullptr if we're the last line. + */ +static PrettyPrinterNode* getNextLine(PrettyPrinterNode* start) { + assert(!start->is_line_separator); + int line = start->line; + for (;;) { + if (start->is_line_separator || start->line == line) { + if (start->next) + start = start->next; + else + return nullptr; + } else { + break; + } + } + return start; +} + +/*! + * Get the next open paren on the current line (can start in the middle of line, not inclusive of + * start) nullptr if there's no open parens on the rest of this line. + */ +static PrettyPrinterNode* getNextListOnLine(PrettyPrinterNode* start) { + int line = start->line; + assert(!start->is_line_separator); + if (!start->next || start->next->is_line_separator) + return nullptr; + start = start->next; + while (!start->is_line_separator && start->line == line) { + if (start->tok->kind == TokenKind::OPEN_PAREN) + return start; + if (!start->next) + return nullptr; + start = start->next; + } + return nullptr; +} + +/*! + * Get the first open paren on the current line (can start in the middle of line, inclusive of + * start) nullptr if there's no open parens on the rest of this line + */ +static PrettyPrinterNode* getFirstListOnLine(PrettyPrinterNode* start) { + int line = start->line; + assert(!start->is_line_separator); + while (!start->is_line_separator && start->line == line) { + if (start->tok->kind == TokenKind::OPEN_PAREN) + return start; + if (!start->next) + return nullptr; + start = start->next; + } + return nullptr; +} + +/*! + * Get the first token on the first line which exceeds the max length + */ +static PrettyPrinterNode* getFirstBadLine(PrettyPrinterNode* start, int line_length) { + assert(!start->is_line_separator); + int currentLine = start->line; + auto* currentLineNode = start; + for (;;) { + if (start->is_line_separator) { + assert(start->next); + start = start->next; + } else { + if (start->line != currentLine) { + currentLine = start->line; + currentLineNode = start; + } + if (start->offset > line_length) { + return currentLineNode; + } + if (!start->next) { + return nullptr; + } + start = start->next; + } + } +} + +/*! + * Break insertion algorithm. + */ +static void insertBreaksAsNeeded(PrettyPrinterNode* head, int line_length) { + PrettyPrinterNode* last_line_complete = nullptr; + PrettyPrinterNode* line_to_start_line_search = head; + + // loop over lines + for (;;) { + // compute lines as needed + propagatePretty(head, line_length); + + // search for a bad line starting at the last line we fixed + PrettyPrinterNode* candidate_line = getFirstBadLine(line_to_start_line_search, line_length); + // if we got the same line we started on, this means we couldn't fix it. + if (candidate_line == last_line_complete) { + candidate_line = nullptr; // so we say our candidate was bad and try to find another + PrettyPrinterNode* next_line = getNextLine(line_to_start_line_search); + if (next_line) { + candidate_line = getFirstBadLine(next_line, line_length); + } + } + if (!candidate_line) + break; + + // okay, we have a line which needs fixing. + assert(!candidate_line->prev || candidate_line->prev->is_line_separator); + PrettyPrinterNode* form_to_start = getFirstListOnLine(candidate_line); + for (;;) { + if (!form_to_start) { + printf("pretty printer has failed. Fix the bug or increase the the line length.\n"); + assert(false); + } + breakList(form_to_start); + propagatePretty(head, line_length); + if (getFirstBadLine(candidate_line, line_length) != candidate_line) { + break; + } + + form_to_start = getNextListOnLine(form_to_start); + if (!form_to_start) + break; + } + + last_line_complete = candidate_line; + line_to_start_line_search = candidate_line; + } +} + +static void insertSpecialBreaks(PrettyPrinterNode* node) { + for (; node; node = node->next) { + if (!node->is_line_separator && node->tok->kind == TokenKind::SYMBOL) { + std::string& name = *node->tok->str; + if (name == "deftype") { + auto* parent_type_dec = getNextListOnLine(node); + if (parent_type_dec) { + insertNewlineAfter(parent_type_dec->paren, 0); + } + } + } + } +} + +std::string Form::toStringPretty(int indent, int line_length) { + (void)indent; + (void)line_length; + std::vector tokens; + toTokenList(tokens); + assert(!tokens.empty()); + std::string pretty; + + // build linked list of nodes + PrettyPrinterNode* head = new PrettyPrinterNode(tokens[0]); + PrettyPrinterNode* node = head; + head->line = 0; + head->offset = 0; + head->lineIndent = 0; + int offset = head->tok->toString().length(); + for (size_t i = 1; i < tokens.size(); i++) { + node->next = new PrettyPrinterNode(tokens[i]); + node->next->prev = node; + node = node->next; + node->line = 0; + node->offset = offset; + offset += node->tok->toString().length(); + node->lineIndent = 0; + } + + // attach parens. + std::vector parenStack; + parenStack.push_back(nullptr); + for (PrettyPrinterNode* n = head; n; n = n->next) { + if (n->tok->kind == TokenKind::OPEN_PAREN) { + parenStack.push_back(n); + } else if (n->tok->kind == TokenKind::CLOSE_PAREN) { + n->paren = parenStack.back(); + parenStack.back()->paren = n; + parenStack.pop_back(); + } else { + n->paren = parenStack.back(); + } + } + assert(parenStack.size() == 1); + assert(!parenStack.back()); + + insertSpecialBreaks(head); + propagatePretty(head, line_length); + insertBreaksAsNeeded(head, line_length); + + // write to string + bool newline_prev = true; + for (PrettyPrinterNode* n = head; n; n = n->next) { + if (n->is_line_separator) { + pretty.push_back('\n'); + newline_prev = true; + } else { + if (newline_prev) { + pretty.append(n->lineIndent, ' '); + newline_prev = false; + if (n->tok->kind == TokenKind::WHITESPACE) + continue; + } + pretty.append(n->tok->toString()); + } + } + + for (;;) { + if (!head) + break; + auto* next = head->next; + delete head; + head = next; + } + + return pretty; +} + +std::shared_ptr toForm(const std::string& str) { + auto f = std::make_shared(); + f->kind = FormKind::SYMBOL; + f->symbol = gSymbolTable.intern(str); + return f; +} + +std::shared_ptr buildList(std::shared_ptr form) { + auto f = std::make_shared(); + f->kind = FormKind::PAIR; + f->pair[0] = form; + f->pair[1] = gSymbolTable.getEmptyPair(); + return f; +} + +std::shared_ptr buildList(const std::string& str) { + return buildList(toForm(str)); +} + +std::shared_ptr buildList(std::shared_ptr* forms, int count) { + auto f = std::make_shared(); + f->kind = FormKind::PAIR; + f->pair[0] = forms[0]; + if (count - 1) { + f->pair[1] = buildList(forms + 1, count - 1); + } else { + f->pair[1] = gSymbolTable.getEmptyPair(); + } + + return f; +} + +std::shared_ptr buildList(std::vector>& forms) { + if (forms.empty()) { + return gSymbolTable.getEmptyPair(); + } + return buildList(forms.data(), forms.size()); +} + +std::shared_ptr buildList(std::vector& forms) { + if (forms.empty()) { + return gSymbolTable.getEmptyPair(); + } + std::vector> f; + for (auto& x : forms) { + f.push_back(toForm(x)); + } + return buildList(f.data(), f.size()); +} diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index a8adf19f61..e815845b7c 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -90,4 +90,3 @@ ELSE() ENDIF() - diff --git a/game/kernel/asm_funcs.asm b/game/kernel/asm_funcs.asm index 753725c7ac..76a3ba70af 100644 --- a/game/kernel/asm_funcs.asm +++ b/game/kernel/asm_funcs.asm @@ -184,4 +184,4 @@ _call_goal_asm_win32: pop rbx pop rdx - ret + ret \ No newline at end of file diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index fddc0e0872..9e280203ac 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -148,11 +148,11 @@ void InitParms(int argc, const char* const* argv) { * DONE, EXACT */ void InitCD() { - spdlog::info("Initializing CD drive\nThis may take a while...\n"); + spdlog::info("Initializing CD drive. This may take a while..."); sceCdInit(SCECdINIT); sceCdMmode(SCECdDVD); while (sceCdDiskReady(0) == SCECdNotReady) { - spdlog::debug("Drive not ready... insert a disk!\n"); + spdlog::debug("Drive not ready... insert a disk!"); } spdlog::debug("Disk type {}\n", sceCdGetDiskType()); } diff --git a/game/main.cpp b/game/main.cpp index b8dc08cfb5..c5f23b1301 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -11,8 +11,13 @@ int main(int argc, char** argv) { while (true) { spdlog::set_level(spdlog::level::debug); + auto game_logger = spdlog::basic_logger_mt("GOAL Runtime", "logs/runtime.log"); + spdlog::set_default_logger(game_logger); + spdlog::flush_on(spdlog::level::info); + // run the runtime in a loop so we can reset the game and have it restart cleanly - spdlog::info("gk {}.{} OK!\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR); + spdlog::info("OpenGOAL Runtime {}.{}", versions::GOAL_VERSION_MAJOR, + versions::GOAL_VERSION_MINOR); if (exec_runtime(argc, argv) == 2) { return 0; diff --git a/game/overlord/fake_iso.cpp b/game/overlord/fake_iso.cpp index 3dd8671945..db3843a480 100644 --- a/game/overlord/fake_iso.cpp +++ b/game/overlord/fake_iso.cpp @@ -216,7 +216,6 @@ uint32_t FS_GetLength(FileRecord* fr) { * This is an ISO FS API Function */ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) { - // printf("[OVERLORD] FS Open %s\n", fr->name); // Added spdlog::debug("[OVERLORD] FS Open {}", fr->name); LoadStackEntry* selected = nullptr; // find first unused spot on load stack. @@ -231,7 +230,6 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) { return selected; } } - // printf("[OVERLORD ISO CD] Failed to FS_Open %s\n", fr->name); spdlog::warn("[OVERLORD] Failed to FS Open {}", fr->name); ExitIOP(); return nullptr; @@ -243,7 +241,6 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) { * This is an ISO FS API Function */ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) { - // printf("[OVERLORD] FS Open %s\n", fr->name); // Added spdlog::debug("[OVERLORD] FS_OpenWad {}", fr->name); LoadStackEntry* selected = nullptr; for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) { @@ -254,7 +251,6 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) { return selected; } } - // printf("[OVERLORD ISO CD] Failed to FS_OpenWad %s\n", fr->name); spdlog::warn("[OVERLORD] Failed to FS_OpenWad {}", fr->name); ExitIOP(); return nullptr; @@ -265,7 +261,6 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) { * This is an ISO FS API Function */ void FS_Close(LoadStackEntry* fd) { - // printf("[OVERLORD] FS Close %s\n", fd->fr->name); spdlog::debug("[OVERLORD] FS_Close {}", fd->fr->name); // close the FD @@ -285,7 +280,6 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) { int32_t real_size = len; if (len < 0) { // not sure what this is about... - // printf("[OVERLORD ISO CD] negative length warning!\n"); spdlog::warn("[OVERLORD ISO CD] Negative length warning!"); real_size = len + 0x7ff; } diff --git a/game/overlord/iso_cd.cpp b/game/overlord/iso_cd.cpp index c8a8dcd861..62c7e7ac4a 100644 --- a/game/overlord/iso_cd.cpp +++ b/game/overlord/iso_cd.cpp @@ -231,7 +231,7 @@ u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID) { while (lsize > 0) { // ISO low-level read if (!ReadSectorsNow(lsector, 1, buffer)) { - printf("[OVERLORD ISO CD] Failed to read sector in ReadDirectory\n"); + spdlog::info("[OVERLORD ISO CD] Failed to read sector in ReadDirectory!"); return 0; } u8* lbuffer = buffer; @@ -264,7 +264,7 @@ u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID) { } } else { if (sNumFiles == MAX_ISO_FILES) { - printf("[OVERLORD ISO CD] There are too many files on the disc!\n"); + spdlog::info("[OVERLORD ISO CD] There are too many files on the disc!"); return 0; } @@ -283,7 +283,7 @@ u32 ReadDirectory(uint32_t sector, uint32_t size, uint32_t secBufID) { lsize -= 0x800; } } else { - printf("[OVERLORD ISO CD] ReadDirectory ran out of sector buffers!\n"); + spdlog::info("[OVERLORD ISO CD] ReadDirectory ran out of sector buffers!"); } return 1; } @@ -354,7 +354,7 @@ void LoadMusicTweaks(u8* buffer) { FileRecord* fr = FS_FindIN(iso_name); if (!fr || !ReadSectorsNow(fr->location, 1, buffer)) { *(s32*)gMusicTweakInfo = 0; - printf("[OVERLORD ISO CD] Failed to load music tweaks!\n"); + spdlog::warn("[OVERLORD ISO CD] Failed to load music tweaks!"); } else { memcpy(gMusicTweakInfo, buffer, MUSIC_TWEAK_SIZE); } @@ -373,8 +373,8 @@ void LoadDiscID() { MakeISOName(iso_name, "DISK_ID.DIZ"); FileRecord* fr = FS_FindIN(iso_name); if (!fr) { - printf( - "[OVERLORD ISO CD] LoadDiscID failed to find DISK_ID.DIZ, using sector 0x400 instead!\n"); + spdlog::warn( + "[OVERLORD ISO CD] LoadDiscID failed to find DISK_ID.DIZ, using sector 0x400 instead!"); CD_ID_SectorNum = 0x400; } else { CD_ID_SectorNum = fr->location; @@ -385,7 +385,7 @@ void LoadDiscID() { for (uint32_t i = 0; i < SECTOR_SIZE / 4; i++) { CD_ID_SectorSum += CD_ID_Sector[i]; } - printf("[OVERLORD] DISK_ID.DIZ OK 0x%x\n", CD_ID_SectorSum); + spdlog::info("[OVERLORD] DISK_ID.DIZ OK 0x{}\n", CD_ID_SectorSum); } /*! @@ -416,13 +416,13 @@ void SetRealSector() { } else { // it's a duplicated file, and duplicate read is enabled, so get the area 2 sector. _real_sector = _sector + sAreaDiff; - printf("[OVERLORD] Warning, adjusting real sector in SetRealSector\n"); + spdlog::warn("[OVERLORD] Warning, adjusting real sector in SetRealSector"); } // we suspect the game is pirated, load the wrong sector. if (pirated) { _real_sector += 3; - printf("pirated!\n"); // added, so I don't trip this by accident! + spdlog::warn("Pirated!"); } } @@ -466,13 +466,13 @@ int FS_Init(u8* buffer) { // read primary volume descriptor into buffer if (!ReadSectorsNow(0x10, 1, sSecBuffer[0])) { - printf("[OVERLORD ISO CD] Failed to read primary volume descriptor\n"); + spdlog::warn("[OVERLORD ISO CD] Failed to read primary volume descriptor"); return 1; } // check volume descriptor identifier if (memcmp(sSecBuffer[0] + 1, "CD001", 5)) { - printf("[OVERLORD ISO CD] Got the wrong volume descriptor identifier\n"); + spdlog::warn("[OVERLORD ISO CD] Got the wrong volume descriptor identifier"); char* cptr = (char*)sSecBuffer[0] + 1; printf("%c%c%c%c%c\n", cptr[0], cptr[1], cptr[2], cptr[3], cptr[4]); return 1; @@ -482,7 +482,7 @@ int FS_Init(u8* buffer) { uint32_t path_table_sector = ReadU32(sSecBuffer[0] + 0x8c); if (!ReadSectorsNow(path_table_sector, 1, sSecBuffer[0])) { - printf("[OVERLORD ISO CD] Failed to read path table\n"); + spdlog::warn("[OVERLORD ISO CD] Failed to read path"); return 1; } @@ -490,14 +490,14 @@ int FS_Init(u8* buffer) { uint32_t path_table_extent = ReadU32(sSecBuffer[0] + 2); if (!ReadSectorsNow(path_table_extent, 1, sSecBuffer[0])) { - printf("[OVERLORD ISO CD] Failed to read path table extent\n"); + spdlog::warn("[OVERLORD ISO CD] Failed to read path table extent"); } // read root directory add_files = true; uint32_t dir_size = ReadU32(sSecBuffer[0] + 10); if (!ReadDirectory(path_table_extent, dir_size, 0)) { - printf("[OVERLORD ISO CD] Failed to ReadDirectory\n"); + spdlog::warn("[OVERLORD ISO CD] Failed to ReadDirectory"); return 1; } @@ -520,7 +520,7 @@ int FS_Init(u8* buffer) { } return 0; } else { - printf("[OVERLORD ISO CD] Bad Media Type\n"); + spdlog::warn("[OVERLORD ISO CD] Bad Media Type!"); return 1; } } @@ -567,7 +567,7 @@ FileRecord* FS_FindIN(const char* iso_name) { } // we didn't get 1 GB of files, you're a pirate. - printf("pirated!\n"); // i added this so i know if it hangs here + spdlog::warn("Pirated!"); } } @@ -585,7 +585,7 @@ uint32_t FS_GetLength(FileRecord* fr) { * This is an ISO FS API Function */ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) { - printf("[OVERLORD] FS Open %s\n", fr->name); // Added + spdlog::info("[OVERLORD] FS Open {}", fr->name); LoadStackEntry* selected = nullptr; // find first unused spot on load stack. for (uint32_t i = 0; i < MAX_OPEN_FILES; i++) { @@ -599,7 +599,7 @@ LoadStackEntry* FS_Open(FileRecord* fr, int32_t offset) { return selected; } } - printf("[OVERLORD ISO CD] Failed to FS_Open %s\n", fr->name); + spdlog::warn("[OVERLORD ISO CD] Failed to FS_Open {}", fr->name); ExitIOP(); return nullptr; } @@ -620,7 +620,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) { return selected; } } - printf("[OVERLORD ISO CD] Failed to FS_OpenWad %s\n", fr->name); + spdlog::warn("[OVERLORD ISO CD] Failed to use FS_OpenWad {}", fr->name); ExitIOP(); return nullptr; } @@ -630,7 +630,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset) { * This is an ISO FS API Function */ void FS_Close(LoadStackEntry* fd) { - printf("[OVERLORD] FS Close %s\n", fd->fr->name); + spdlog::info("[OVERLORD] FS Close {}", fd->fr->name); if (fd == sReadInfo) { // the file is currently being read, so lets try to finish out the read, if possible. int count = 0; @@ -663,7 +663,7 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) { int32_t real_size = len; if (len < 0) { // not sure what this is about... - printf("[OVERLORD ISO CD] negative length warning!\n"); + spdlog::warn("[OVERLORD ISO CD] Negative length warning!"); real_size = len + 0x7ff; } _sectors = real_size >> 11; @@ -982,4 +982,4 @@ void CD_WaitReturn() { } } while (!CheckDiskID()); gNoCD = 0; -} \ No newline at end of file +} diff --git a/game/overlord/iso_queue.cpp b/game/overlord/iso_queue.cpp index 3f327bd111..63b4b62e8a 100644 --- a/game/overlord/iso_queue.cpp +++ b/game/overlord/iso_queue.cpp @@ -179,11 +179,11 @@ u32 QueueMessage(IsoMessage* cmd, int32_t priority, const char* name) { gPriStack[priority].cmds[gPriStack[priority].n] = cmd; gPriStack[priority].names[gPriStack[priority].n] = name; gPriStack[priority].n++; - printf("[OVERLORD] Queue %d (%d/%d), %s\n", priority, gPriStack[priority].n, PRI_STACK_LENGTH, - gPriStack[priority].names[gPriStack[priority].n - 1].c_str()); + spdlog::debug("[OVERLORD] Queue {} ({}/{}), {}", priority, gPriStack[priority].n, + PRI_STACK_LENGTH, gPriStack[priority].names[gPriStack[priority].n - 1].c_str()); DisplayQueue(); } else { - printf("[OVERLORD ISO QUEUE] Failed to queue!\n"); + spdlog::warn("[OVERLORD ISO QUEUE] Failed to queue!"); cmd->status = CMD_STATUS_FAILED_TO_QUEUE; ReturnMessage(cmd); } @@ -209,7 +209,7 @@ void UnqueueMessage(IsoMessage* cmd) { } } } - printf("[OVERLORD ISO QUEUE] Failed to unqueue!\n"); + spdlog::warn("[OVERLORD ISO QUEUE] Failed to unqueue!"); found: assert(gPriStack[pri].cmds[idx] == cmd); diff --git a/game/runtime.cpp b/game/runtime.cpp index eddd4491e0..8d325c90cc 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -75,7 +75,7 @@ void deci2_runner(SystemThreadInterface& iface) { throw std::runtime_error("DECI2 server init failed"); } - printf("[DECI2] waiting for listener...\n"); + spdlog::debug("[DECI2] Waiting for listener..."); // spdlog::debug("[DECI2] Waiting for listener..."); --> disabled temporarily, some weird race // condition? bool saw_listener = false; @@ -259,7 +259,6 @@ u32 exec_runtime(int argc, char** argv) { // join and exit tm.join(); - // printf("GOAL Runtime Shutdown (code %d)\n", MasterExit); spdlog::info("GOAL Runtime Shutdown (code {})", MasterExit); return MasterExit; }