splat64 0.35.1__tar.gz → 0.36.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. {splat64-0.35.1 → splat64-0.36.0}/CHANGELOG.md +27 -0
  2. {splat64-0.35.1 → splat64-0.36.0}/PKG-INFO +4 -4
  3. {splat64-0.35.1 → splat64-0.36.0}/README.md +1 -1
  4. {splat64-0.35.1 → splat64-0.36.0}/docs/Adding-Symbols.md +26 -0
  5. {splat64-0.35.1 → splat64-0.36.0}/docs/Configuration.md +52 -1
  6. {splat64-0.35.1 → splat64-0.36.0}/docs/Segments.md +3 -1
  7. {splat64-0.35.1 → splat64-0.36.0}/pyproject.toml +2 -2
  8. {splat64-0.35.1 → splat64-0.36.0}/requirements.txt +1 -1
  9. {splat64-0.35.1 → splat64-0.36.0}/src/splat/__init__.py +1 -1
  10. {splat64-0.35.1 → splat64-0.36.0}/src/splat/disassembler/spimdisasm_disassembler.py +1 -1
  11. {splat64-0.35.1 → splat64-0.36.0}/src/splat/scripts/create_config.py +5 -0
  12. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/textbin.py +55 -30
  13. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/linker_entry.py +4 -2
  14. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/i1.py +9 -9
  15. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/file_presets.py +56 -18
  16. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/options.py +26 -0
  17. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/symbols.py +28 -0
  18. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/data/main.bss.s +2 -1
  19. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/data/main.data.s +4 -2
  20. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/data/main.rodata.s +4 -2
  21. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/handwritten.s +2 -1
  22. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/main.s +4 -2
  23. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/nonmatchings/main/D_80000510.s +2 -1
  24. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/nonmatchings/main/func_80000400.s +4 -2
  25. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/nonmatchings/main/func_800004A0.s +2 -1
  26. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/include/labels.inc +2 -1
  27. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/include/macro.inc +7 -1
  28. {splat64-0.35.1 → splat64-0.36.0}/.gitattributes +0 -0
  29. {splat64-0.35.1 → splat64-0.36.0}/.github/workflows/black.yml +0 -0
  30. {splat64-0.35.1 → splat64-0.36.0}/.github/workflows/mypy.yml +0 -0
  31. {splat64-0.35.1 → splat64-0.36.0}/.github/workflows/publish_docs_to_wiki.yml +0 -0
  32. {splat64-0.35.1 → splat64-0.36.0}/.github/workflows/pypi.yml +0 -0
  33. {splat64-0.35.1 → splat64-0.36.0}/.github/workflows/test_lib.yml +0 -0
  34. {splat64-0.35.1 → splat64-0.36.0}/.github/workflows/unit_tests.yml +0 -0
  35. {splat64-0.35.1 → splat64-0.36.0}/.gitignore +0 -0
  36. {splat64-0.35.1 → splat64-0.36.0}/LICENSE +0 -0
  37. {splat64-0.35.1 → splat64-0.36.0}/create_config.py +0 -0
  38. {splat64-0.35.1 → splat64-0.36.0}/docs/Advanced-Reloc.md +0 -0
  39. {splat64-0.35.1 → splat64-0.36.0}/docs/Advanced.md +0 -0
  40. {splat64-0.35.1 → splat64-0.36.0}/docs/Examples.md +0 -0
  41. {splat64-0.35.1 → splat64-0.36.0}/docs/General-Workflow.md +0 -0
  42. {splat64-0.35.1 → splat64-0.36.0}/docs/Home.md +0 -0
  43. {splat64-0.35.1 → splat64-0.36.0}/docs/Quickstart.md +0 -0
  44. {splat64-0.35.1 → splat64-0.36.0}/docs/VramClasses.md +0 -0
  45. {splat64-0.35.1 → splat64-0.36.0}/mypy.ini +0 -0
  46. {splat64-0.35.1 → splat64-0.36.0}/split.py +0 -0
  47. {splat64-0.35.1 → splat64-0.36.0}/src/splat/__main__.py +0 -0
  48. {splat64-0.35.1 → splat64-0.36.0}/src/splat/disassembler/__init__.py +0 -0
  49. {splat64-0.35.1 → splat64-0.36.0}/src/splat/disassembler/disassembler.py +0 -0
  50. {splat64-0.35.1 → splat64-0.36.0}/src/splat/disassembler/disassembler_instance.py +0 -0
  51. {splat64-0.35.1 → splat64-0.36.0}/src/splat/disassembler/disassembler_section.py +0 -0
  52. {splat64-0.35.1 → splat64-0.36.0}/src/splat/disassembler/null_disassembler.py +0 -0
  53. {splat64-0.35.1 → splat64-0.36.0}/src/splat/platforms/__init__.py +0 -0
  54. {splat64-0.35.1 → splat64-0.36.0}/src/splat/platforms/n64.py +0 -0
  55. {splat64-0.35.1 → splat64-0.36.0}/src/splat/platforms/ps2.py +0 -0
  56. {splat64-0.35.1 → splat64-0.36.0}/src/splat/platforms/psp.py +0 -0
  57. {splat64-0.35.1 → splat64-0.36.0}/src/splat/platforms/psx.py +0 -0
  58. {splat64-0.35.1 → splat64-0.36.0}/src/splat/py.typed +0 -0
  59. {splat64-0.35.1 → splat64-0.36.0}/src/splat/scripts/__init__.py +0 -0
  60. {splat64-0.35.1 → splat64-0.36.0}/src/splat/scripts/capy.py +0 -0
  61. {splat64-0.35.1 → splat64-0.36.0}/src/splat/scripts/split.py +0 -0
  62. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/__init__.py +0 -0
  63. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/__init__.py +0 -0
  64. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/asm.py +0 -0
  65. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/asmtu.py +0 -0
  66. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/bin.py +0 -0
  67. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/bss.py +0 -0
  68. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/c.py +0 -0
  69. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/code.py +0 -0
  70. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/codesubsegment.py +0 -0
  71. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/cpp.py +0 -0
  72. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/data.py +0 -0
  73. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/databin.py +0 -0
  74. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/eh_frame.py +0 -0
  75. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/gcc_except_table.py +0 -0
  76. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/group.py +0 -0
  77. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/hasm.py +0 -0
  78. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/header.py +0 -0
  79. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/lib.py +0 -0
  80. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/linker_offset.py +0 -0
  81. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/pad.py +0 -0
  82. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/rdata.py +0 -0
  83. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/rodata.py +0 -0
  84. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/rodatabin.py +0 -0
  85. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/sbss.py +0 -0
  86. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/sdata.py +0 -0
  87. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/common/segment.py +0 -0
  88. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/__init__.py +0 -0
  89. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/ci.py +0 -0
  90. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/ci4.py +0 -0
  91. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/ci8.py +0 -0
  92. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/decompressor.py +0 -0
  93. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/gfx.py +0 -0
  94. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/header.py +0 -0
  95. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/i4.py +0 -0
  96. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/i8.py +0 -0
  97. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/ia16.py +0 -0
  98. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/ia4.py +0 -0
  99. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/ia8.py +0 -0
  100. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/img.py +0 -0
  101. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/ipl3.py +0 -0
  102. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/mio0.py +0 -0
  103. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/palette.py +0 -0
  104. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/rgba16.py +0 -0
  105. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/rgba32.py +0 -0
  106. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/rsp.py +0 -0
  107. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/vtx.py +0 -0
  108. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/n64/yay0.py +0 -0
  109. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/ps2/__init__.py +0 -0
  110. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/ps2/ctor.py +0 -0
  111. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/ps2/lit4.py +0 -0
  112. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/ps2/lit8.py +0 -0
  113. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/ps2/vtables.py +0 -0
  114. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/psp/__init__.py +0 -0
  115. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/psx/__init__.py +0 -0
  116. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/psx/header.py +0 -0
  117. {splat64-0.35.1 → splat64-0.36.0}/src/splat/segtypes/segment.py +0 -0
  118. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/__init__.py +0 -0
  119. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/cache_handler.py +0 -0
  120. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/color.py +0 -0
  121. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/compiler.py +0 -0
  122. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/conf.py +0 -0
  123. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/log.py +0 -0
  124. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/n64/__init__.py +0 -0
  125. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/n64/find_code_length.py +0 -0
  126. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/n64/rominfo.py +0 -0
  127. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/palettes.py +0 -0
  128. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/progress_bar.py +0 -0
  129. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/psx/__init__.py +0 -0
  130. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/psx/psxexeinfo.py +0 -0
  131. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/relocs.py +0 -0
  132. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/statistics.py +0 -0
  133. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/utils.py +0 -0
  134. {splat64-0.35.1 → splat64-0.36.0}/src/splat/util/vram_classes.py +0 -0
  135. {splat64-0.35.1 → splat64-0.36.0}/test/Dockerfile +0 -0
  136. {splat64-0.35.1 → splat64-0.36.0}/test/README.md +0 -0
  137. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/.gitignore +0 -0
  138. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/Makefile +0 -0
  139. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/build/basic_app.bin +0 -0
  140. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/dummy_ipl3.s +0 -0
  141. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/.splache +0 -0
  142. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/asm/header.s +0 -0
  143. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/assets/dummy_ipl3.bin +0 -0
  144. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/basic_app.d +0 -0
  145. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/build/test/basic_app/split/src/main.asmproc.d +0 -0
  146. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/include/include_asm.h +0 -0
  147. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/src/main.c +0 -0
  148. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/undefined_funcs_auto.txt +0 -0
  149. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/expected/undefined_syms_auto.txt +0 -0
  150. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/handwritten.s +0 -0
  151. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/header.s +0 -0
  152. {splat64-0.35.1 → splat64-0.36.0}/test/basic_app/main.c +0 -0
  153. {splat64-0.35.1 → splat64-0.36.0}/test/test_gen_expected.sh +0 -0
  154. {splat64-0.35.1 → splat64-0.36.0}/test.py +0 -0
@@ -1,5 +1,32 @@
1
1
  # splat Release Notes
2
2
 
3
+ ### 0.36.0
4
+
5
+ * New symbol attribute: `align`.
6
+ * Emit an alignment directive for the given symbol during disassembly.
7
+ * The given alignment must be positive, be a power of two and The symbol's address must already be aligned to the given custom alignment, otherwise splat will emit an error and halt.
8
+ * New yaml option: `ld_gp_expression`:
9
+ * Allows setting a custom expression for the `_gp` symbol in the generated linker script, making this symbol to properly shift around instead of hardcoding it to the given `gp_value`.
10
+ * New yaml option: `include_asm_macro_style`:
11
+ * Allows configuring the style used by the generated `INCLUDE_ASM` macro. It currently allows two possible values:
12
+ * `default`: Uses the default definition for the macro. This is the default.
13
+ * `maspsx_hack`: Changes the definition of the generated `INCLUDE_ASM` to be compatible with the one expected by `maspsx` when using the [reordering workaround hack](https://github.com/mkst/maspsx?tab=readme-ov-file#include_asm-reordering-workaround-hack). This value is only relevant for psx projects.
14
+ * `incbin` segments now provide a default symbol if the user hasn't defined a symbol at that given address.
15
+ * This name is automatically escaped to be a valid symbol name.
16
+ * The name of this symbol is based on the segment's name, with a `__` prefix and a suffix depending on the type of incbin used.
17
+ * This naming scheme may change at any time.
18
+ * `incbin` segments now emit a nonmatching marker.
19
+ * `spimdisasm` 1.36.1 or above is now required.
20
+
21
+ ### 0.35.2
22
+
23
+ * Miscellaneous updates to generated macro labels.
24
+ * `jlabel`s in `labels.inc` are now explicitly marked as local.
25
+ * `jlabel`s in `macro.inc` no longer are marked as `@function` type.
26
+ * `macro.inc` now has include guards.
27
+ * `labels.inc` does not have them because no old assembler seem to properly support them.
28
+ * Fix splat not respecting when `generate_asm_macros_files` was being turned off.
29
+
3
30
  ### 0.35.1
4
31
 
5
32
  * Fixes issues in the generated `macro.inc` and `labels.inc` files on psx projects.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splat64
3
- Version: 0.35.1
3
+ Version: 0.36.0
4
4
  Summary: A binary splitting tool to assist with decompilation and modding projects
5
5
  Project-URL: Repository, https://github.com/ethteck/splat
6
6
  Project-URL: Issues, https://github.com/ethteck/splat/issues
@@ -42,7 +42,7 @@ Requires-Dist: mypy; extra == 'dev'
42
42
  Requires-Dist: n64img>=0.3.3; extra == 'dev'
43
43
  Requires-Dist: pygfxd; extra == 'dev'
44
44
  Requires-Dist: rabbitizer<2.0.0,>=1.12.0; extra == 'dev'
45
- Requires-Dist: spimdisasm<2.0.0,>=1.32.0; extra == 'dev'
45
+ Requires-Dist: spimdisasm<2.0.0,>=1.36.1; extra == 'dev'
46
46
  Requires-Dist: types-colorama; extra == 'dev'
47
47
  Requires-Dist: types-pyyaml; extra == 'dev'
48
48
  Provides-Extra: mips
@@ -50,7 +50,7 @@ Requires-Dist: crunch64<1.0.0,>=0.5.1; extra == 'mips'
50
50
  Requires-Dist: n64img>=0.3.3; extra == 'mips'
51
51
  Requires-Dist: pygfxd; extra == 'mips'
52
52
  Requires-Dist: rabbitizer<2.0.0,>=1.12.0; extra == 'mips'
53
- Requires-Dist: spimdisasm<2.0.0,>=1.32.0; extra == 'mips'
53
+ Requires-Dist: spimdisasm<2.0.0,>=1.36.1; extra == 'mips'
54
54
  Description-Content-Type: text/markdown
55
55
 
56
56
  # splat
@@ -76,7 +76,7 @@ The brackets corresponds to the optional dependencies to install while installin
76
76
  If you use a `requirements.txt` file in your repository, then you can add this library with the following line:
77
77
 
78
78
  ```txt
79
- splat64[mips]>=0.35.1,<1.0.0
79
+ splat64[mips]>=0.36.0,<1.0.0
80
80
  ```
81
81
 
82
82
  ### Optional dependencies
@@ -21,7 +21,7 @@ The brackets corresponds to the optional dependencies to install while installin
21
21
  If you use a `requirements.txt` file in your repository, then you can add this library with the following line:
22
22
 
23
23
  ```txt
24
- splat64[mips]>=0.35.1,<1.0.0
24
+ splat64[mips]>=0.36.0,<1.0.0
25
25
  ```
26
26
 
27
27
  ### Optional dependencies
@@ -209,3 +209,29 @@ __opPCc__Q23std34_RefCountedPtr<c,Q23std9_Array<c>>CFv = 0x00202850; // filename
209
209
  ```
210
210
 
211
211
  Gets written to `func_00202850.s`
212
+
213
+ ### `align`
214
+
215
+ Emit an alignment directive for the given symbol during disassembly.
216
+
217
+ Even though an explicit alignment directive is not necessary in most situations, it may be desirable to have one on shiftable builds of partially matched projects. This allows ensuring the symbol's required alignment even when it is still being disassembled.
218
+
219
+ Some situations that may require an explicit alignment directive include textures in N64 (0x8 alignment) or symbols sent to the IOP on PS2 (0x40 alignment).
220
+
221
+ The symbol's address must already be aligned to the given custom alignment, or the value will be discarded.
222
+
223
+ This value must be a power of two, otherwise it will be discarded.
224
+
225
+ **Example**
226
+
227
+ ```ini
228
+ rgb_texture = 0x82013118; // align:0x8
229
+ ```
230
+
231
+ Produces a disassembly similar to the following:
232
+
233
+ ```mips
234
+ .align 3
235
+ dlabel rgb_texture
236
+ # ...
237
+ ```
@@ -138,6 +138,15 @@ Some files may not be generated depending on the selected platform and compiler,
138
138
 
139
139
  Defaults to `True`.
140
140
 
141
+ ### include_asm_macro_style
142
+
143
+ Allows configuring the style used by the generated `INCLUDE_ASM` macro. It currently allows two possible values:
144
+
145
+ - `default`: Uses the default definition for the macro. This is the default.
146
+ - `maspsx_hack`: Changes the definition of the generated `INCLUDE_ASM` to be compatible with the one expected by `maspsx` when using the [reordering workaround hack](https://github.com/mkst/maspsx?tab=readme-ov-file#include_asm-reordering-workaround-hack).
147
+
148
+ Defaults to `default`.
149
+
141
150
  ### o_as_suffix
142
151
 
143
152
  Used to determine the file extension of the built files that will be listed on the linker script.
@@ -155,6 +164,8 @@ Defaults to `False`.
155
164
 
156
165
  The value of the `$gp` register to correctly calculate offset to `%gp_rel` relocs.
157
166
 
167
+ It is strongly encouraged to provide a [`ld_gp_expression`](#ld_gp_expression) once the sections of the binary are properly understood.
168
+ Not setting this expression makes splat always emit a hardcoded value for `_gp` in the linker script, preventing proper shiftability.
158
169
 
159
170
  ### check_consecutive_segment_types
160
171
 
@@ -579,7 +590,7 @@ Defaults to `True`, meaning `bss` sections will be put on `NOLOAD` segments.
579
590
 
580
591
  Specify that segments should be aligned before starting them.
581
592
 
582
- This option specifies the desired alignment value, or `null` if no aligment should be imposed on the segment start.
593
+ This option specifies the desired alignment value, or `null` if no alignment should be imposed on the segment start.
583
594
 
584
595
  This behavior can be customized per segment too. See [ld_align_segment_start](Segments.md#ld_align_segment_start) on the Segments section.
585
596
 
@@ -615,6 +626,46 @@ Sets the default option for the `bss_contains_common` attribute of all segments.
615
626
 
616
627
  Defaults to `False`.
617
628
 
629
+ ### ld_gp_expression
630
+
631
+ Provides an expression for the `_gp` symbol to be emitted in the generated linker script.
632
+
633
+ Most projects start by only setting a [`gp_value`](#gp_value) on their yamls. This is fine while matching the project, but it is bad for shiftable builds, becuase it prevents the `_gp` symbol to shift around as needed.
634
+
635
+ This expression is used as-is in the generated linker script, so care must be taken to provide an expression that makes sense for shiftable builds and also matches the original gp value on matching builds.
636
+
637
+ Usually this expression is relative to the start of an "small section" (`.sdata`, `.srodata`, `.sbss`, etc.) plus an optional offset (usually `0x7FF0` or `0x8000`).
638
+
639
+ The recommended approach to know what to set here is to try to understand where the first small section is, take the difference between the [`gp_value`](#gp_value) and the address of the small section to know what the offset is and then use the linker symbol for the start of that small section.
640
+
641
+ For example, say your gp value is `0x00397FF0` and you found the first small section on the rom is a `.sdata` section at vram address `0x00390000` (note *vram* address, not rom address) which is part of the top-level segment `main`. The difference between those two addresses is `0x7FF0`, so that's your offset. Given this information the expression you want for gp is `main_SDATA_START + 0x7FF0`; where `main` is the top-level segment, `SDATA` comes from the `.sdata` section and the `+ 0x7FF0` is your calculated offset. If you did this process right, then the matching build should still match after setting the gp expression.
642
+
643
+ Note you should **not** remove the `gp_value` from your yaml when you set `ld_gp_expression`. `gp_value` is still used for the disassembly; without it you wouldn't get symbolized gp-accesses.
644
+
645
+ #### Usage
646
+
647
+ Not using `ld_gp_expression` makes splat hardcode the value:
648
+
649
+ ```yaml
650
+ gp_value: 0x0039DD70
651
+ ```
652
+
653
+ ```txt
654
+ _gp = 0x0039DD70;
655
+ ```
656
+
657
+ When a `ld_gp_expression` is given then splat is able to emit a non-hardcoded expression for `_gp` without interfering with the disassembly process:
658
+
659
+ ```yaml
660
+ gp_value: 0x0039DD70
661
+ ld_gp_expression: main_SDATA_START + 0x7FF0
662
+ ```
663
+
664
+ ```txt
665
+ _gp = main_SDATA_START + 0x7FF0;
666
+ ```
667
+
668
+
618
669
  ## C file options
619
670
 
620
671
  ### create_c_files
@@ -318,6 +318,8 @@ Generating assembly files enables better customization of these binaries, like a
318
318
 
319
319
  If a known symbol (via a symbol_addrs file) matches the vram of a incbin segment then it will be emitted accordingly at the top. If the symbol contains a [`name_end`](Adding-Symbols.md#name_end) property then it will be emitted after the `.incbin` (useful for Nintendo64's RSP ucodes).
320
320
 
321
+ If no symbol is provided then splat will emit a symbol based on the segment's name with a `__` prefix and a suffix depending on the type of incbin used. This autogenerated naming scheme may change at any time.
322
+
321
323
  Curretly there are 3 types of incbins, `textbin`, `databin` and `rodatabin`, which are intended for binary blobs of `.text`, `.data` and `.rodata` sections.
322
324
 
323
325
  If a `textbin` section has a corresponding `databin` and/or `rodatabin` section with the same name then those will be included in the same generated assembly file.
@@ -542,7 +544,7 @@ Defaults to the value of the global option.
542
544
 
543
545
  Specify the current segment should be aligned before starting it.
544
546
 
545
- This option specifies the desired alignment value, or `null` if no aligment should be imposed on the segment start.
547
+ This option specifies the desired alignment value, or `null` if no alignment should be imposed on the segment start.
546
548
 
547
549
  If not set, then the global configuration is used. See [ld_align_segment_start](Configuration.md#ld_align_segment_start) on the Configuration section.
548
550
 
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "splat64"
3
3
  # Should be synced with src/splat/__init__.py
4
- version = "0.35.1"
4
+ version = "0.36.0"
5
5
  description = "A binary splitting tool to assist with decompilation and modding projects"
6
6
  readme = "README.md"
7
7
  license = {file = "LICENSE"}
@@ -20,7 +20,7 @@ dependencies = [
20
20
 
21
21
  [project.optional-dependencies]
22
22
  mips = [
23
- "spimdisasm>=1.32.0,<2.0.0", # This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py
23
+ "spimdisasm>=1.36.1,<2.0.0", # This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py
24
24
  "rabbitizer>=1.12.0,<2.0.0",
25
25
  "pygfxd",
26
26
  "n64img>=0.3.3",
@@ -4,7 +4,7 @@ tqdm
4
4
  intervaltree
5
5
  colorama
6
6
  # This value should be keep in sync with the version listed on disassembler/spimdisasm_disassembler.py and pyproject.toml
7
- spimdisasm>=1.36.0
7
+ spimdisasm>=1.36.1
8
8
  rabbitizer>=1.10.0
9
9
  pygfxd
10
10
  n64img>=0.1.4
@@ -1,7 +1,7 @@
1
1
  __package_name__ = __name__
2
2
 
3
3
  # Should be synced with pyproject.toml
4
- __version__ = "0.35.1"
4
+ __version__ = "0.36.0"
5
5
  __author__ = "ethteck"
6
6
 
7
7
  from . import util as util
@@ -7,7 +7,7 @@ from typing import Set
7
7
 
8
8
  class SpimdisasmDisassembler(disassembler.Disassembler):
9
9
  # This value should be kept in sync with the version listed on requirements.txt and pyproject.toml
10
- SPIMDISASM_MIN = (1, 36, 0)
10
+ SPIMDISASM_MIN = (1, 36, 1)
11
11
 
12
12
  def configure(self):
13
13
  # Configure spimdisasm
@@ -286,6 +286,7 @@ options:
286
286
 
287
287
  find_file_boundaries: False
288
288
  gp_value: 0x{exe.initial_gp:08X}
289
+ # ld_gp_expression: main_SCOMMON_START + 0x7FF0
289
290
 
290
291
  o_as_suffix: True
291
292
  use_legacy_include_asm: False
@@ -309,6 +310,10 @@ options:
309
310
  data_string_encoding: ASCII
310
311
  rodata_string_guesser_level: 2
311
312
  data_string_guesser_level: 2
313
+
314
+ # Uncomment this line if you need to use the maspsx reorder workaround hack
315
+ # https://github.com/mkst/maspsx?tab=readme-ov-file#include_asm-reordering-workaround-hack
316
+ # include_asm_macro_style: maspsx_hack
312
317
  """
313
318
 
314
319
  segments = f"""\
@@ -1,4 +1,5 @@
1
1
  from pathlib import Path
2
+ import re
2
3
  from typing import Optional, TextIO
3
4
 
4
5
  from ...util import log, options
@@ -75,45 +76,66 @@ class CommonSegTextbin(CommonSegment):
75
76
 
76
77
  f.write(f"{self.get_section_asm_line()}\n\n")
77
78
 
79
+ sym_name = None
80
+ sym_name_end = None
81
+ sym_size_matches = None
82
+
78
83
  # Check if there's a symbol at this address
79
- sym = None
80
84
  vram = self.rom_to_ram(self.rom_start)
81
85
  if vram is not None:
82
86
  sym = self.get_symbol(vram, in_segment=True)
83
-
84
- if sym is not None:
85
- f.write(f"{asm_label} {sym.name}\n")
86
- if asm_label == ".globl":
87
- if self.is_text():
88
- f.write(f".ent {sym.name}\n")
89
- f.write(f"{sym.name}:\n")
90
- sym.defined = True
87
+ if sym is not None:
88
+ sym.defined = True
89
+ sym_name = sym.name
90
+ sym_name_end = sym.given_name_end
91
+ if (
92
+ sym.given_size is None
93
+ or sym.given_size == self.rom_end - self.rom_start
94
+ ):
95
+ sym_size_matches = self.rom_end - self.rom_start
96
+
97
+ if sym_name is None:
98
+ # Normalize stuff like slashes and such.
99
+ n = regex_sym_name_normalizer.sub("_", self.name)
100
+ if self.is_text():
101
+ suffix = "textbin"
102
+ elif self.is_data():
103
+ suffix = "databin"
104
+ elif self.is_rodata():
105
+ suffix = "rodatabin"
106
+ else:
107
+ suffix = "incbin"
108
+ sym_name = f"__{n}_{suffix}"
109
+
110
+ if options.opts.asm_nonmatching_label_macro != "":
111
+ siz = f", 0x{sym_size_matches:X}" if sym_size_matches is not None else ""
112
+ f.write(f"{options.opts.asm_nonmatching_label_macro} {sym_name}{siz}\n\n")
113
+
114
+ f.write(f"{asm_label} {sym_name}\n")
115
+ if asm_label == ".globl":
116
+ if self.is_text():
117
+ f.write(f".ent {sym_name}\n")
118
+ f.write(f"{sym_name}:\n")
91
119
 
92
120
  f.write(f'.incbin "{binpath.as_posix()}"\n')
93
121
 
94
- if sym is not None:
95
- if options.opts.asm_emit_size_directive:
96
- f.write(f".size {sym.name}, . - {sym.name}\n")
122
+ if options.opts.asm_emit_size_directive:
123
+ f.write(f".size {sym_name}, . - {sym_name}\n")
97
124
 
98
- if self.is_text() and options.opts.asm_end_label != "":
99
- f.write(f"{options.opts.asm_end_label} {sym.name}\n")
100
- elif self.is_data() and options.opts.asm_data_end_label != "":
101
- f.write(f"{options.opts.asm_data_end_label} {sym.name}\n")
125
+ if self.is_text() and options.opts.asm_end_label != "":
126
+ f.write(f"{options.opts.asm_end_label} {sym_name}\n")
127
+ elif options.opts.asm_data_end_label != "":
128
+ f.write(f"{options.opts.asm_data_end_label} {sym_name}\n")
102
129
 
103
- if sym.given_name_end is not None:
104
- if (
105
- sym.given_size is None
106
- or sym.given_size == self.rom_end - self.rom_start
107
- ):
108
- f.write(f"{asm_label} {sym.given_name_end}\n")
109
- if asm_label == ".globl":
110
- f.write(f"{sym.given_name_end}:\n")
111
- if self.is_text() and options.opts.asm_end_label != "":
112
- f.write(f"{options.opts.asm_end_label} {sym.given_name_end}\n")
113
- elif self.is_data() and options.opts.asm_data_end_label != "":
114
- f.write(
115
- f"{options.opts.asm_data_end_label} {sym.given_name_end}\n"
116
- )
130
+ if sym_name_end is not None and sym_size_matches is not None:
131
+ f.write(f"{asm_label} {sym_name_end}\n")
132
+ if asm_label == ".globl":
133
+ f.write(f"{sym_name_end}:\n")
134
+
135
+ if self.is_text() and options.opts.asm_end_label != "":
136
+ f.write(f"{options.opts.asm_end_label} {sym_name_end}\n")
137
+ elif options.opts.asm_data_end_label != "":
138
+ f.write(f"{options.opts.asm_data_end_label} {sym_name_end}\n")
117
139
 
118
140
  def split(self, rom_bytes):
119
141
  if self.rom_end is None:
@@ -159,3 +181,6 @@ class CommonSegTextbin(CommonSegment):
159
181
  return (
160
182
  self.extract and self.should_scan()
161
183
  ) # only split if the segment was scanned first
184
+
185
+
186
+ regex_sym_name_normalizer = re.compile(r"[^0-9a-zA-Z_]")
@@ -204,8 +204,10 @@ class LinkerWriter:
204
204
  if not self.is_partial:
205
205
  self._writeln(f"__romPos = {options.opts.ld_rom_start};")
206
206
 
207
- if options.opts.gp is not None:
208
- self._writeln("_gp = " + f"0x{options.opts.gp:X};")
207
+ if options.opts.ld_gp_expression is not None:
208
+ self._writeln(f"_gp = {options.opts.ld_gp_expression};")
209
+ elif options.opts.gp is not None:
210
+ self._writeln(f"_gp = 0x{options.opts.gp:X};")
209
211
 
210
212
  # Write a series of statements which compute a symbol that represents the highest address among a list of segments' end addresses
211
213
  def write_max_vram_end_sym(self, symbol: str, overlays: List[Segment]):
@@ -1,9 +1,9 @@
1
- import n64img.image
2
-
3
- from .img import N64SegImg
4
-
5
-
6
- class N64SegI1(N64SegImg):
7
- def __init__(self, *args, **kwargs):
8
- kwargs["img_cls"] = n64img.image.I1
9
- super().__init__(*args, **kwargs)
1
+ import n64img.image
2
+
3
+ from .img import N64SegImg
4
+
5
+
6
+ class N64SegI1(N64SegImg):
7
+ def __init__(self, *args, **kwargs):
8
+ kwargs["img_cls"] = n64img.image.I1
9
+ super().__init__(*args, **kwargs)
@@ -14,6 +14,9 @@ from . import options, log
14
14
 
15
15
 
16
16
  def write_all_files():
17
+ if not options.opts.generate_asm_macros_files:
18
+ return
19
+
17
20
  write_include_asm_h()
18
21
  write_assembly_inc_files()
19
22
 
@@ -30,13 +33,23 @@ def write_include_asm_h():
30
33
  # These compilers do not use the `INCLUDE_ASM` macro.
31
34
  return
32
35
 
33
- file_data = """\
34
- #ifndef INCLUDE_ASM_H
35
- #define INCLUDE_ASM_H
36
-
37
- #if !defined(M2CTX) && !defined(PERMUTER)
38
-
39
- #ifndef INCLUDE_ASM
36
+ if options.opts.include_asm_macro_style == "maspsx_hack":
37
+ include_asm_macro = """\
38
+ #define INCLUDE_ASM(FOLDER, NAME) \\
39
+ void __maspsx_include_asm_hack_##NAME() { \\
40
+ __asm__( \\
41
+ ".text # maspsx-keep \\n" \\
42
+ " .align 2 # maspsx-keep\\n" \\
43
+ " .set noat # maspsx-keep\\n" \\
44
+ " .set noreorder # maspsx-keep\\n" \\
45
+ " .include \\"" FOLDER "/" #NAME ".s\\" # maspsx-keep\\n" \\
46
+ " .set reorder # maspsx-keep\\n" \\
47
+ " .set at # maspsx-keep\\n" \\
48
+ ); \\
49
+ }
50
+ """
51
+ else: # default
52
+ include_asm_macro = """\
40
53
  #define INCLUDE_ASM(FOLDER, NAME) \\
41
54
  __asm__( \\
42
55
  ".section .text\\n" \\
@@ -46,14 +59,28 @@ def write_include_asm_h():
46
59
  " .set reorder\\n" \\
47
60
  " .set at\\n" \\
48
61
  )
49
- #endif
50
- #ifndef INCLUDE_RODATA
62
+ """
63
+
64
+ include_rodata_macro = """\
51
65
  #define INCLUDE_RODATA(FOLDER, NAME) \\
52
66
  __asm__( \\
53
67
  ".section .rodata\\n" \\
54
68
  " .include \\"" FOLDER "/" #NAME ".s\\"\\n" \\
55
69
  ".section .text" \\
56
70
  )
71
+ """
72
+
73
+ file_data = f"""\
74
+ #ifndef INCLUDE_ASM_H
75
+ #define INCLUDE_ASM_H
76
+
77
+ #if !defined(M2CTX) && !defined(PERMUTER)
78
+
79
+ #ifndef INCLUDE_ASM
80
+ {include_asm_macro}\
81
+ #endif
82
+ #ifndef INCLUDE_RODATA
83
+ {include_rodata_macro}\
57
84
  #endif
58
85
  __asm__(".include \\"include/labels.inc\\"\\n");
59
86
 
@@ -126,14 +153,14 @@ def write_assembly_inc_files():
126
153
  # A label referenced by a jumptable.
127
154
  .macro {options.opts.asm_jtbl_label_macro} label, visibility=global
128
155
  .\\visibility \\label
129
- .type \\label, @function
130
156
  \\label:
131
157
  .endm
132
158
  """
133
159
  if options.opts.migrate_rodata_to_functions:
134
160
  jlabel_macro_labelsinc = f"""
135
161
  # A label referenced by a jumptable.
136
- .macro {options.opts.asm_jtbl_label_macro} label, visibility=global
162
+ .macro {options.opts.asm_jtbl_label_macro} label, visibility=local
163
+ .\\visibility \\label
137
164
  \\label:
138
165
  .endm
139
166
  """
@@ -176,12 +203,6 @@ def write_assembly_inc_files():
176
203
  .endm
177
204
  """
178
205
 
179
- labels_inc = f"""\
180
- {func_macros}
181
- {jlabel_macro_labelsinc}
182
- {data_macros}
183
- {nm_macros}\
184
- """
185
206
  macros_inc = f"""\
186
207
  {func_macros}
187
208
  {jlabel_macro_macroinc}
@@ -195,7 +216,15 @@ def write_assembly_inc_files():
195
216
 
196
217
  if options.opts.platform == "psx":
197
218
  preamble += '\n.include "gte_macros.inc"\n'
198
- _write("include/labels.inc", f"{preamble}\n{labels_inc}")
219
+
220
+ labels_inc = f"""\
221
+ {preamble}
222
+ {func_macros}
223
+ {jlabel_macro_labelsinc}
224
+ {data_macros}
225
+ {nm_macros}\
226
+ """
227
+ _write("include/labels.inc", labels_inc)
199
228
 
200
229
  if options.opts.platform in {"n64", "psx"}:
201
230
  gas = macros_inc
@@ -294,6 +323,15 @@ def write_assembly_inc_files():
294
323
  preamble = (
295
324
  "# This file is used by modern gas.\n# Defines the expected assembly macros\n"
296
325
  )
326
+ gas = f"""\
327
+ # Evaluate this file only once in case it's included more than once
328
+ .ifndef _MACRO_INC_GUARD
329
+ .internal _MACRO_INC_GUARD
330
+ .set _MACRO_INC_GUARD, 1
331
+
332
+ {gas}
333
+ .endif
334
+ """
297
335
  _write("include/macro.inc", f"{preamble}\n{gas}")
298
336
 
299
337
 
@@ -37,7 +37,12 @@ class SplatOpts:
37
37
  generated_s_preamble: str
38
38
  # Determines any extra content to be added in the generated macro.inc file
39
39
  generated_macro_inc_content: Optional[str]
40
+ # Determines if files related to assembly macros should be regenerated by splat
40
41
  generate_asm_macros_files: bool
42
+ # Changes the definition of the generated `INCLUDE_ASM`.
43
+ # default: The default one.
44
+ # maspsx_hack: Use the maspsx hack workaround definition https://github.com/mkst/maspsx?tab=readme-ov-file#include_asm-reordering-workaround-hack
45
+ include_asm_macro_style: Literal["default", "maspsx_hack"]
41
46
  # Determines whether to use .o as the suffix for all binary files?... TODO document
42
47
  use_o_as_suffix: bool
43
48
  # the value of the $gp register to correctly calculate offset to %gp_rel relocs
@@ -150,6 +155,8 @@ class SplatOpts:
150
155
  ld_generate_symbol_per_data_segment: bool
151
156
  # Sets the default option for the `bss_contains_common` attribute of all segments.
152
157
  ld_bss_contains_common: bool
158
+ # Specify an expression to be used for the `_gp` symbol in the generated linker script instead of a hardcoded value.
159
+ ld_gp_expression: Optional[str]
153
160
 
154
161
  ################################################################################
155
162
  # C file options
@@ -400,6 +407,21 @@ def _parse_yaml(
400
407
  else:
401
408
  raise ValueError(f"Invalid endianness: {endianness}")
402
409
 
410
+ def parse_include_asm_macro_style() -> Literal["default", "maspsx_hack"]:
411
+ include_asm_macro_style = p.parse_opt_within(
412
+ "include_asm_macro_style",
413
+ str,
414
+ ["default", "maspsx_hack"],
415
+ "default",
416
+ )
417
+
418
+ if include_asm_macro_style == "default":
419
+ return "default"
420
+ elif include_asm_macro_style == "maspsx_hack":
421
+ return "maspsx_hack"
422
+ else:
423
+ raise ValueError(f"Invalid endianness: {include_asm_macro_style}")
424
+
403
425
  default_ld_bss_is_noload = True
404
426
  if platform == "psx":
405
427
  default_ld_bss_is_noload = False
@@ -431,6 +453,7 @@ def _parse_yaml(
431
453
  "generated_macro_inc_content", str
432
454
  ),
433
455
  generate_asm_macros_files=p.parse_opt("generate_asm_macros_files", bool, True),
456
+ include_asm_macro_style=parse_include_asm_macro_style(),
434
457
  use_o_as_suffix=p.parse_opt("o_as_suffix", bool, False),
435
458
  gp=p.parse_optional_opt("gp_value", int),
436
459
  check_consecutive_segment_types=p.parse_opt(
@@ -507,6 +530,9 @@ def _parse_yaml(
507
530
  "ld_generate_symbol_per_data_segment", bool, False
508
531
  ),
509
532
  ld_bss_contains_common=p.parse_opt("ld_bss_contains_common", bool, False),
533
+ ld_gp_expression=p.parse_optional_opt_with_default(
534
+ "ld_gp_expression", str, None
535
+ ),
510
536
  create_c_files=p.parse_opt("create_c_files", bool, True),
511
537
  auto_decompile_empty_functions=p.parse_opt(
512
538
  "auto_decompile_empty_functions", bool, True