Skip to content

A toolkit for compiling C/C++ code using devkitppc and injecting it into a Gamecube Executable (dol).

License

Notifications You must be signed in to change notification settings

Minty-Meeo/dol_c_kit

 
 

Repository files navigation

What is DOL C-Kit?

DOL C-Kit is a toolkit for compiling C/C++ code (or assembly) using DevkitPPC to inject into a GameCube/Wii *.dol executable. It has been written in such a way that it can be adapted to many different games. You will need Python 3 and DevKitPPC installed to use it. As well, DOL C-Kit is dependent on pyelftools, JoshuaMK's fork of dolreader, and geckocode-libs.

Optionally, one may use MetroWerks CodeWarrior instead of DevkitPPC for compiling and assembling code. DevkitPPC is still a dependency even if CodeWarrior tools are used, since a GCC linker is needed.

Credit to Yoshi2 for creating the original GC C-Kit. DOL C-Kit couldn't exist without it.

How to use it

DOL C-Kit is a Python module. To install on Windows, run INSTALL.bat as administrator. To install on Linux, run INSTALL.sh as superuser.

The Project Class

from dol_c_kit import Project, Compiler, Assembler, Linker

The Project class automates the tedious parts of compiling, linking, and injecting custom code into a *.dol executable.

By shifting forward the stack, db_stack, and OSArenaLo, space for new data can be allocated. To do this, a patching function modifying a given game's "__init_registers", "OSInit", and "__OSThreadInit" functions must be written. The project's save_dol function passes two parameters to this patching function: a DolFile class, and the base_addr of your project.

Class constructor

  • Project(self, base_addr=None, verbose=False, compiler=Compiler.DevkitPPC, assembler=Assembler.DevkitPPC, linker=Linker.DevkitPPC)
    • base_addr Sets its respective class member.
    • verbose Sets its respective class member.
    • compiler Enumerated value determining which compiler is used for C/C++. Currently, Compiler.DevkitPPC and Compiler.CodeWarrior are available.
    • assembler Enumerated value determining which assembler is used. Currently, Assembler.DevkitPPC and Assembler.CodeWarrior are available.
    • linker Enumerated value determining which linker is used. Currently, only Linker.DevkitPPC is available.

Class members

The Project class has many member variables that may be directly modified:

  • src_dir Path to C code (or assembly) source files. Default is "".
  • obj_dir Path to output *.o files and other files generated by DOL C-Kit to. Default is "".
  • devkitppc_path Change this if DevKitPPC is not installed at its default location.
    • For Windows, this is "C:/devkitPro/devkitPPC/bin/".
    • For other platforms, this is "/opt/devkitpro/devkitPPC/bin/".
  • codewarrior_path Change this if MetroWerks CodeWarrior is not installed at its default location.
    • For Windows, this is "C:/Program Files (x86)/Metrowerks/CodeWarrior/PowerPC_EABI_Tools/Command_Line_Tools/".
    • For other platforms, this is "/".
  • c_flags List of non-crucial flags passed to the compiler for C.
    • For DevkitPPC, the default is ["-w", "-std=c99", "-O1", "-fno-asynchronous-unwind-tables",].
    • For CodeWarrior, the default is ["-proc", "gekko", "-Cpp_exceptions", "off", "-use_lmw_stmw", "on", "-fp", "fmadd", "-schedule", "on",].
  • cpp_flags List of non-crucial flags passed to the compiler for C++.
    • For DevkitPPC, the default is ["-w", "-std=c++98", "-O1", "-fno-asynchronous-unwind-tables", "-fno-rtti",].
    • For CodeWarrior, the default is ["-proc", "gekko", "-Cpp_exceptions", "off", "-use_lmw_stmw", "on", "-fp", "fmadd", "-schedule", "on",].
  • asm_flags List of non-crucial flags passed to the assembler.
    • For DevkitPPC, the default is ["-w",].
    • For CodeWarrior, the default is ["-proc", "gekko",].
  • linker_flags List of non-crucial flags passed to the linker.
    • For DevkitPPC, the default is [].
  • project_name Name used for certain files generated by DOL C-Kit. Default is "project".
  • base_addr The location new data will be put at. This is set by the constructor, but may be modified directly as well.
  • sda_base The value used for the _SDA_BASE_ symbol. This is set by the set_sda_bases method, but may be modified directly as well.
  • sda2_base The value used for the _SDA2_BASE_ symbol. This is set by the set_sda_bases method, but may be modified directly as well.
  • verbose Flag for additional information printing. This is set by the constructor, but may be modified directly as well.

Step 1: Methods to populate the project

  • add_c_file(filepath, flags=(), use_global_flags=True)
    Add a C source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to the compiler for C as flags, and use_global_flags determines if the c_flags member of the Project class are used for this source file.

  • add_cpp_file(filepath, flags=(), use_global_flags=True)
    Add a C++ source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to the compiler for C++ as flags, and use_global_flags determines if the cpp_flags member of the Project class are used for this source file.

  • add_asm_file(filepath, flags=(), use_global_flags=True)
    Add an assembly source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to the assembler as flags, and use_global_flags determines if the asm_flags member of the Project class are used for this source file.

  • add_obj_file(filepath, do_cleanup=False)
    Add an unlinked object file to the project. This object file must be in the obj_dir, not the src_dir. The cleanup method WILL DELETE FILES added by the add_obj_file method if the optional do_cleanup argument is True.

  • add_linker_script_file(filepath)
    Add a linker script file to the project. This is useful for defining symbols.

  • add_gecko_txt_file(filepath)
    Add a textual Gecko Code List to the project. When build_dol is used, codetypes 00, 02, 04, 06, 08, C6, C2, and F2 are permanently patched into the DOL. When build_gecko is used, all given Gecko Codes are copied into a new Gecko Code List.

  • add_gecko_gct_file(filepath)
    Add a binary Gecko Code Table to the project. When build_dol is used, codetypes 00, 02, 04, 06, 08, C6, C2, and F2 are permanently patched into the DOL. When build_gecko is used, all given Gecko Codes are copied into a new Gecko Code List.

  • hook_branch(addr, sym_name, LK=False)
    Declare a branch to a symbol to be written at a given address. Optionally, pass LK=True to declare a branchlink.

  • hook_branchlink(addr, sym_name)
    Declare a branchlink to a symbol to be written at a given address.

  • hook_pointer(addr, sym_name)
    Declare a pointer to a symbol to be written at a given address.

  • hook_string(addr, string, encoding = "ascii", max_strlen = None)
    Declare a string to be written at a given address. Optionally, an encoding and maximum size (in bytes) can be specified.

  • hook_file(addr, filepath, start=0, end=None, max_size = None)
    Declare a file to be written at a given address. Optionally, you may provide a start and end offset to only include a portion of the file, and a maximum size (in bytes) can be specified. Use a negative end offset if you want the offset to be relative to the end of the file. If the file cannot be opened, nothing is written at the given address. This is useful for editing files embedded in the DOL.

  • hook_immediate16(addr, sym_name, modifier)
    Declare a 16-bit immediate to be written at a given address. This is useful for modifying the SIMM, UIMM, and d fields of certain instructions. Valid modifiers include "@h", "@l", "@ha", "@sda", and "@sda2". Make sure to use the set_sda_base method before trying to use the "@sda" or "sda2" modifiers.

  • hook_immediate12(addr, w, i, sym_name, modifier)
    Same thing as add_immediate_16, but for the 12-bit immediate field of Paired-Singles load/store instructions. The w and i fields of the original instruction must also be provided.

  • set_osarena_patcher(function)
    Give your project a game-specific patching function to use to allocate space for new data.

  • set_sda_bases(sda_base, sda2_base)
    Set the _SDA_BASE_ and _SDA2_BASE_ symbols. These values get passed to the linker. They are also important for the @sda and @sda2 modifiers for Immediate16Hooks.

Step 2: Methods to build the project

  • build_dol(in_dol_path, out_dol_path)
    Compile, assemble, and link all source files, hooks, and supported Gecko Codes into a *.dol executable. If no base_addr is specified, the ROM end will automatically be detected and used. A new text section will be allocated to contain the new data. If no text sections are available, a data section will be allocated instead.
    Note: Automatic ROM end detection does not work for DOLs that allocate space for .sbss2.

  • build_gecko(gecko_path)
    Compile, assemble, and link all source files, hooks, and Gecko Codes into a large Gecko Code List. OSArenaLo patchers are not used, and likely never will be worth implementing be due to timing limitations of Gecko Codes. Instead, existing data must be overwritten.

  • save_map(map_path)
    Generate a CodeWarrior-like symbol map from the project. Run this after building but before cleanup.

  • cleanup()
    Delete unimportant files created by DOL C-Kit. This includes unlinked *.o files, and <project_name>.o, <project_name>.bin, and <project_name>.map.

How to work with mangled symbols (C++)

In C++, there is the concept of mangled symbol names. For example, the function signature int foo::bar(MyClass arg1) becomes the symbol _ZN3foo3barE7MyClass. DOL C-Kit provides faculties to make working with mangled symbols easy.

The LDPlusPlus class

from dol_c_kit import LDPlusPlus, ABI

Just like a usual linker script to give dol-side symbols a value for C and ASM, one is needed for C++ as well. LDPlusPlus makes this easier by mangling function signatures in bulk for you.

Class constructor

  • LDPlusPlus(abi)
    • abi The desired ABI (an enum value). At the moment, ABI.Itanium and ABI.Macintosh are available.

Methods

  • assign(prototype, value)
    Assign a value to a symbol in the linker script.

  • provide(prototype, value)
    Provide a value for a symbol in the linker script.

  • save(filepath)
    Save the linker script to a given filepath.

mangle (and itanium_mangle)

from dol_c_kit import mangle, ABI, itanium_mangle, macintosh_mangle

To mangle an individual signature, use the mangle function (or itanium_mangle/macintosh_mangle function). This is useful for hooks which take a symbol name as an argument.

  • mangle(prototype, abi)
    Returns a mangled symbol from a given signature prototype in a given ABI (an enum value). At the moment, ABI.Itanium and ABI.Macintosh are available.

  • itanium_mangle(prototype)
    Returns a mangled symbol from a given signature prototype in the Itanium ABI.

  • macintosh_mangle(prototype)
    Returns a mangled symbol from a given signature prototype in the Classic Macintosh ABI.

Limitations and workarounds of the mangler

Writing a C++ mangler that has no concept of user defined types, or really any context at all, was a struggle that came with a few compromises.

  • Function pointer types are not supported.
  • Certain kinds of template instancing are not supported.
  • Typedefs are not supported (yet).
  • Implicit integral long and long long is not supported. Always type out long int and long long int, respectively.
  • To mangle certain edge cases, special tokens starting with "$$" are used. These examples are in the Itanium ABI:
    • $$vtable is used for vtable signatures. e.g. ClassA::$$vtable will mangle to _ZTV6ClassA.
    • $$rtti is used for typeinfo signatures. e.g. ClassA::$$rtti will mangle to _ZTI6ClassA.
    • $$vtt_structure is used for vtable structure signatures. e.g. ClassA::$$vtt_structure will mangle to _ZTT6ClassA.
    • $$rtti_name is used for typeinfo names. e.g. ClassA::$$rtti_name will mangle to _ZTS6ClassA.
    • $$ctor is used for (the assumed default) object constructors. e.g. ClassA::$$ctor will mangle to _ZN6ClassAC1Ev.
    • $$ctor1 is used for complete object constructors. e.g. ClassA::$$ctor1 will mangle to _ZN6ClassAC1Ev.
    • $$ctor2 is used for base object constructors. e.g. ClassA::$$ctor2 will mangle to _ZN6ClassAC2Ev.
    • $$ctor3 is used for complete object allocating. e.g. ClassA::$$ctor3 will mangle to _ZN6ClassAC3Ev.
    • $$dtor is used for (the assumed default) object destructors. e.g. ClassA::$$dtor will mangle to _ZN6ClassAD1Ev.
    • $$dtor0 is used for deleting destructors. e.g. ClassA::$$dtor0 will mangle to _ZN6ClassAD0Ev.
    • $$dtor1 is used for complete object destructors. e.g. ClassA::$$dtor1 will mangle to _ZN6ClassAD1Ev.
    • $$dtor2 is used for base object destructors. e.g. ClassA::$$dtor2 will mangle to _ZN6ClassAD2Ev.
    • $$unary is used for operator overloads that are ambigious without context.
      • operator+ $$unary will produce the "positive" operator override rather than the "add" operator override.
      • operator- $$unary will produce the "negative" operator override rather than the "subtract" operator override.
      • operator& $$unary will produce the "reference" operator override rather than the "bitwise AND" operator override.
      • operator* $$unary will produce the "dereference" operator override rather than the "multiply" operator override.

There are likely other cases that the mangler won't cover, but for 99% of signatures, it should work.

About

A toolkit for compiling C/C++ code using devkitppc and injecting it into a Gamecube Executable (dol).

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 99.8%
  • Other 0.2%