splat64 0.32.3__tar.gz → 0.33.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.33.0/.gitattributes +2 -0
  2. {splat64-0.32.3 → splat64-0.33.0}/.github/workflows/mypy.yml +1 -0
  3. splat64-0.33.0/.github/workflows/unit_tests.yml +60 -0
  4. {splat64-0.32.3 → splat64-0.33.0}/CHANGELOG.md +15 -2
  5. {splat64-0.32.3 → splat64-0.33.0}/PKG-INFO +2 -2
  6. {splat64-0.32.3 → splat64-0.33.0}/README.md +1 -1
  7. {splat64-0.32.3 → splat64-0.33.0}/docs/Configuration.md +74 -4
  8. {splat64-0.32.3 → splat64-0.33.0}/pyproject.toml +1 -1
  9. {splat64-0.32.3 → splat64-0.33.0}/requirements.txt +1 -1
  10. {splat64-0.32.3 → splat64-0.33.0}/src/splat/__init__.py +1 -1
  11. {splat64-0.32.3 → splat64-0.33.0}/src/splat/disassembler/spimdisasm_disassembler.py +1 -1
  12. {splat64-0.32.3 → splat64-0.33.0}/src/splat/scripts/split.py +20 -55
  13. splat64-0.33.0/src/splat/segtypes/common/asm.py +29 -0
  14. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/asmtu.py +3 -3
  15. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/c.py +20 -9
  16. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/codesubsegment.py +55 -2
  17. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/data.py +8 -20
  18. splat64-0.33.0/src/splat/segtypes/common/hasm.py +27 -0
  19. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/textbin.py +7 -1
  20. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/linker_entry.py +5 -5
  21. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/compiler.py +1 -0
  22. splat64-0.33.0/src/splat/util/conf.py +96 -0
  23. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/options.py +17 -4
  24. splat64-0.33.0/test/basic_app/.gitignore +4 -0
  25. splat64-0.33.0/test/basic_app/build/basic_app.bin +0 -0
  26. splat64-0.33.0/test/basic_app/expected/.splache +0 -0
  27. splat64-0.33.0/test/basic_app/expected/asm/data/main.rodata.s +22 -0
  28. splat64-0.33.0/test/basic_app/expected/asm/main.s +84 -0
  29. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/asm/nonmatchings/main/func_80000400.s +0 -3
  30. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/asm/nonmatchings/main/func_800004A0.s +1 -4
  31. splat64-0.33.0/test/basic_app/expected/basic_app.d +21 -0
  32. splat64-0.33.0/test/basic_app/expected/build/test/basic_app/split/src/main.asmproc.d +7 -0
  33. {splat64-0.32.3 → splat64-0.33.0}/test/test_gen_expected.sh +1 -1
  34. {splat64-0.32.3 → splat64-0.33.0}/test.py +20 -20
  35. splat64-0.32.3/.github/workflows/unit_tests.yml +0 -27
  36. splat64-0.32.3/src/splat/segtypes/common/asm.py +0 -59
  37. splat64-0.32.3/src/splat/segtypes/common/hasm.py +0 -35
  38. splat64-0.32.3/test/basic_app/.gitignore +0 -3
  39. splat64-0.32.3/test/basic_app/expected/.splache +0 -0
  40. {splat64-0.32.3 → splat64-0.33.0}/.github/workflows/black.yml +0 -0
  41. {splat64-0.32.3 → splat64-0.33.0}/.github/workflows/publish_docs_to_wiki.yml +0 -0
  42. {splat64-0.32.3 → splat64-0.33.0}/.github/workflows/pypi.yml +0 -0
  43. {splat64-0.32.3 → splat64-0.33.0}/.github/workflows/test_lib.yml +0 -0
  44. {splat64-0.32.3 → splat64-0.33.0}/.gitignore +0 -0
  45. {splat64-0.32.3 → splat64-0.33.0}/LICENSE +0 -0
  46. {splat64-0.32.3 → splat64-0.33.0}/create_config.py +0 -0
  47. {splat64-0.32.3 → splat64-0.33.0}/docs/Adding-Symbols.md +0 -0
  48. {splat64-0.32.3 → splat64-0.33.0}/docs/Advanced.md +0 -0
  49. {splat64-0.32.3 → splat64-0.33.0}/docs/Examples.md +0 -0
  50. {splat64-0.32.3 → splat64-0.33.0}/docs/General-Workflow.md +0 -0
  51. {splat64-0.32.3 → splat64-0.33.0}/docs/Home.md +0 -0
  52. {splat64-0.32.3 → splat64-0.33.0}/docs/Quickstart.md +0 -0
  53. {splat64-0.32.3 → splat64-0.33.0}/docs/Segments.md +0 -0
  54. {splat64-0.32.3 → splat64-0.33.0}/docs/VramClasses.md +0 -0
  55. {splat64-0.32.3 → splat64-0.33.0}/mypy.ini +0 -0
  56. {splat64-0.32.3 → splat64-0.33.0}/split.py +0 -0
  57. {splat64-0.32.3 → splat64-0.33.0}/src/splat/__main__.py +0 -0
  58. {splat64-0.32.3 → splat64-0.33.0}/src/splat/disassembler/__init__.py +0 -0
  59. {splat64-0.32.3 → splat64-0.33.0}/src/splat/disassembler/disassembler.py +0 -0
  60. {splat64-0.32.3 → splat64-0.33.0}/src/splat/disassembler/disassembler_instance.py +0 -0
  61. {splat64-0.32.3 → splat64-0.33.0}/src/splat/disassembler/disassembler_section.py +0 -0
  62. {splat64-0.32.3 → splat64-0.33.0}/src/splat/disassembler/null_disassembler.py +0 -0
  63. {splat64-0.32.3 → splat64-0.33.0}/src/splat/platforms/__init__.py +0 -0
  64. {splat64-0.32.3 → splat64-0.33.0}/src/splat/platforms/n64.py +0 -0
  65. {splat64-0.32.3 → splat64-0.33.0}/src/splat/platforms/ps2.py +0 -0
  66. {splat64-0.32.3 → splat64-0.33.0}/src/splat/platforms/psp.py +0 -0
  67. {splat64-0.32.3 → splat64-0.33.0}/src/splat/platforms/psx.py +0 -0
  68. {splat64-0.32.3 → splat64-0.33.0}/src/splat/py.typed +0 -0
  69. {splat64-0.32.3 → splat64-0.33.0}/src/splat/scripts/__init__.py +0 -0
  70. {splat64-0.32.3 → splat64-0.33.0}/src/splat/scripts/capy.py +0 -0
  71. {splat64-0.32.3 → splat64-0.33.0}/src/splat/scripts/create_config.py +0 -0
  72. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/__init__.py +0 -0
  73. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/__init__.py +0 -0
  74. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/bin.py +0 -0
  75. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/bss.py +0 -0
  76. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/code.py +0 -0
  77. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/cpp.py +0 -0
  78. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/databin.py +0 -0
  79. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/eh_frame.py +0 -0
  80. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/gcc_except_table.py +0 -0
  81. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/group.py +0 -0
  82. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/header.py +0 -0
  83. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/lib.py +0 -0
  84. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/linker_offset.py +0 -0
  85. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/pad.py +0 -0
  86. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/rdata.py +0 -0
  87. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/rodata.py +0 -0
  88. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/rodatabin.py +0 -0
  89. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/sbss.py +0 -0
  90. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/sdata.py +0 -0
  91. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/common/segment.py +0 -0
  92. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/__init__.py +0 -0
  93. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/ci.py +0 -0
  94. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/ci4.py +0 -0
  95. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/ci8.py +0 -0
  96. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/decompressor.py +0 -0
  97. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/gfx.py +0 -0
  98. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/header.py +0 -0
  99. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/i1.py +0 -0
  100. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/i4.py +0 -0
  101. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/i8.py +0 -0
  102. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/ia16.py +0 -0
  103. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/ia4.py +0 -0
  104. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/ia8.py +0 -0
  105. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/img.py +0 -0
  106. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/ipl3.py +0 -0
  107. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/mio0.py +0 -0
  108. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/palette.py +0 -0
  109. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/rgba16.py +0 -0
  110. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/rgba32.py +0 -0
  111. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/rsp.py +0 -0
  112. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/vtx.py +0 -0
  113. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/n64/yay0.py +0 -0
  114. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/ps2/__init__.py +0 -0
  115. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/ps2/ctor.py +0 -0
  116. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/ps2/lit4.py +0 -0
  117. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/ps2/lit8.py +0 -0
  118. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/ps2/vtables.py +0 -0
  119. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/psp/__init__.py +0 -0
  120. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/psx/__init__.py +0 -0
  121. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/psx/header.py +0 -0
  122. {splat64-0.32.3 → splat64-0.33.0}/src/splat/segtypes/segment.py +0 -0
  123. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/__init__.py +0 -0
  124. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/cache_handler.py +0 -0
  125. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/color.py +0 -0
  126. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/log.py +0 -0
  127. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/n64/__init__.py +0 -0
  128. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/n64/find_code_length.py +0 -0
  129. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/n64/rominfo.py +0 -0
  130. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/palettes.py +0 -0
  131. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/progress_bar.py +0 -0
  132. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/psx/__init__.py +0 -0
  133. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/psx/psxexeinfo.py +0 -0
  134. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/relocs.py +0 -0
  135. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/statistics.py +0 -0
  136. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/symbols.py +0 -0
  137. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/utils.py +0 -0
  138. {splat64-0.32.3 → splat64-0.33.0}/src/splat/util/vram_classes.py +0 -0
  139. {splat64-0.32.3 → splat64-0.33.0}/test/Dockerfile +0 -0
  140. {splat64-0.32.3 → splat64-0.33.0}/test/README.md +0 -0
  141. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/Makefile +0 -0
  142. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/dummy_ipl3.s +0 -0
  143. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/asm/data/main.bss.s +0 -0
  144. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/asm/data/main.data.s +0 -0
  145. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/asm/handwritten.s +1 -1
  146. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/asm/header.s +0 -0
  147. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/asm/nonmatchings/main/D_80000510.s +0 -0
  148. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/assets/dummy_ipl3.bin +0 -0
  149. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/src/main.c +0 -0
  150. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/undefined_funcs_auto.txt +0 -0
  151. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/expected/undefined_syms_auto.txt +0 -0
  152. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/handwritten.s +0 -0
  153. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/header.s +0 -0
  154. {splat64-0.32.3 → splat64-0.33.0}/test/basic_app/main.c +0 -0
@@ -0,0 +1,2 @@
1
+ # Force LF on everyfile
2
+ * text eol=lf
@@ -22,6 +22,7 @@ jobs:
22
22
  pip install mypy
23
23
  pip install -r requirements.txt
24
24
  pip install types-PyYAML
25
+ pip install -e .
25
26
 
26
27
  - name: mypy splat lib
27
28
  run: mypy --show-column-numbers --hide-error-context src/splat
@@ -0,0 +1,60 @@
1
+ name: unit_tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ unit_tests:
9
+ name: unit_tests on ${{ matrix.os.name }}
10
+ runs-on: ${{ matrix.os.runner }}
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [
15
+ {
16
+ name: linux,
17
+ runner: ubuntu-latest,
18
+ python_venv: .venv/bin/python3,
19
+ },
20
+ {
21
+ name: macos,
22
+ runner: macos-latest,
23
+ python_venv: .venv/bin/python3,
24
+ },
25
+ {
26
+ name: windows,
27
+ runner: windows-latest,
28
+ python_venv: .venv/Scripts/python3,
29
+ },
30
+ ]
31
+
32
+ steps:
33
+ - name: Checkout
34
+ uses: actions/checkout@v4
35
+
36
+ - name: Install dependencies
37
+ if: matrix.os.name == 'linux'
38
+ run: sudo apt-get install -y build-essential make binutils-mips-linux-gnu python3 python3-pip python3-venv wget
39
+
40
+ - name: Setup Python venv
41
+ run: |
42
+ python3 -m venv .venv
43
+
44
+ - name: Install Python dependencies
45
+ run: |
46
+ ${{ matrix.os.python_venv }} -m pip install -U -r requirements.txt
47
+ ${{ matrix.os.python_venv }} -m pip install -e .
48
+
49
+ - name: Build `basic_app` on ${{ matrix.os.name }}
50
+ if: matrix.os.name == 'linux'
51
+ # Linux CI checks if any of the test C code has changed without updating the generated binary
52
+ run: |
53
+ make -C test/basic_app clean
54
+ make -C test/basic_app download_kmc
55
+ make -C test/basic_app all
56
+ git diff --exit-code test/basic_app/build/basic_app.bin
57
+
58
+ - name: Run the test
59
+ run: |
60
+ ${{ matrix.os.python_venv }} test.py
@@ -1,9 +1,22 @@
1
1
  # splat Release Notes
2
2
 
3
+ ### 0.33.0
4
+
5
+ * BREAKING: Move splat config loading and parsing into "splat.util.conf" to modularize this part of the code and allow projects to interface with segment loading more gracefully.
6
+ * `initialize_config` is now `splat.util.conf.load`. It takes `Path`s instead of `str`s for YAML paths. It will no longer `exit` if config merging fails, but will throw a `TypeError` which can be handled as desired by callers.
7
+ * Fix splat on Windows not using forward slashes on generated paths.
8
+ * Setup CI to be run on Windows and MacOS too.
9
+ * Fix output of incbin segments for SN64 projects
10
+ * New yaml option: `make_full_disasm_for_code`
11
+ * Emit a full `.s` file for each `c`/`cpp` segment besides the generated `nonmatchings` individual functions.
12
+ * Can be used to generate "target" or "expected" objects for asm diffing.
13
+ * Also available as a cli parameter: `--make-full-disasm-for-code`
14
+ * `spimdisasm` 1.33.0 or above is now required.
15
+
3
16
  ### 0.32.3
4
17
 
5
- - Fix "unrecognized YAML option" error if disassemble_all is provided via CLI and as a YAML option.
6
- - Slightly speed up `bss` disassembly for projects over thousands of subsegments in the same top-level segment.
18
+ * Fix "unrecognized YAML option" error if disassemble_all is provided via CLI and as a YAML option.
19
+ * Slightly speed up `bss` disassembly for projects over thousands of subsegments in the same top-level segment.
7
20
 
8
21
  ### 0.32.2
9
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splat64
3
- Version: 0.32.3
3
+ Version: 0.33.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
@@ -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.32.3,<1.0.0
79
+ splat64[mips]>=0.33.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.32.3,<1.0.0
24
+ splat64[mips]>=0.33.0,<1.0.0
25
25
  ```
26
26
 
27
27
  ### Optional dependencies
@@ -403,20 +403,84 @@ ld_symbol_header_path: path/to/linker_symbol_header
403
403
 
404
404
  ### ld_discard_section
405
405
 
406
- Determines whether to add a discard section to the linker script
406
+ Determines whether to add a wildcard discard section to the linker script.
407
+
408
+ This tells the linker that every section not explicitly listed on the linker script will be discarded.
409
+
410
+ ### ld_sections_allowlist
411
+
412
+ A list of sections to preserve during link time. It can be useful to preserve debugging sections.
413
+
414
+ #### Usage
415
+
416
+ ```yaml
417
+ ld_sections_allowlist:
418
+ - .shstrtab
419
+ - .mdebug
420
+ - .mdebug.abi32
421
+ ```
422
+
423
+ Generates entries at the bottom of the linker script like:
424
+
425
+ ```text
426
+ .shstrtab 0 :
427
+ {
428
+ *(.shstrtab);
429
+ }
430
+ .mdebug 0 :
431
+ {
432
+ *(.mdebug);
433
+ }
434
+ .mdebug.abi32 0 :
435
+ {
436
+ *(.mdebug.abi32);
437
+ }
438
+ ```
439
+
440
+ ### ld_sections_denylist
441
+
442
+ A list of sections to discard during link time. It can be useful to avoid using the [wildcard discard](#ld_discard_section).
443
+
444
+ Note this option does not turn off [`ld_discard_section`](#ld_discard_section), neither checks if the listed sections overlap with it.
445
+
446
+ #### Usage
447
+
448
+ ```yaml
449
+ ld_sections_denylist:
450
+ - .reginfo
451
+ - .MIPS.abiflags
452
+ - .MIPS.options
453
+ - .note.gnu.build-id
454
+ - .interp
455
+ - .eh_frame
456
+ ```
457
+
458
+ Generates a discard section like this:
459
+
460
+ ```text
461
+ /DISCARD/ :
462
+ {
463
+ *(.reginfo);
464
+ *(.MIPS.abiflags);
465
+ *(.MIPS.options);
466
+ *(.note.gnu.build-id);
467
+ *(.interp);
468
+ *(.eh_frame);
469
+ }
470
+ ```
407
471
 
408
472
  ### ld_wildcard_sections
409
473
 
410
474
  Determines whether to add wildcards for section linking in the linker script (.rodata* for example)
411
475
 
412
- ### ld_use_symbolic_vram_addreses
476
+ ### ld_use_symbolic_vram_addresses
413
477
 
414
478
  Determines whether to use `follows_vram` (segment option) and `vram_symbol` / `follows_classes` (vram_class options) to calculate vram addresses in the linker script.
415
479
  Enabled by default. If disabled, this uses the plain integer values for vram addresses defined in the yaml.
416
480
 
417
481
  ### ld_partial_linking
418
482
 
419
- Change linker script generation to allow partially linking segments. Requires both `ld_partial_scripts_path` and `ld_partial_build_segments_path` to be set.
483
+ Change linker script generation to allow partially linking segments. Requires both [`ld_partial_scripts_path`](#ld_partial_scripts_path) and [`ld_partial_build_segments_path`](#ld_partial_build_segments_path) to be set.
420
484
 
421
485
  ### ld_partial_scripts_path
422
486
 
@@ -542,7 +606,7 @@ Determine the format that symbols should be named by default
542
606
 
543
607
  ### symbol_name_format_no_rom
544
608
 
545
- Same as `symbol_name_format` but for symbols with no rom address
609
+ Same as [`symbol_name_format`](#symbol_name_format) but for symbols with no rom address
546
610
 
547
611
  ### find_file_boundaries
548
612
 
@@ -663,6 +727,12 @@ Tries to detect redundant and unreferenced functions ends and merge them togethe
663
727
 
664
728
  Don't skip disassembling already matched functions and migrated sections
665
729
 
730
+ ### make_full_disasm_for_code
731
+
732
+ Emit a full `.s` file for each `c`/`cpp` segment besides the generated `nonmatchings` individual functions.
733
+
734
+ Can be used to generate "target" or "expected" objects for asm diffing.
735
+
666
736
  ### global_vram_start and global_vram_end
667
737
 
668
738
  Allow specifying that the global memory range may be larger than what was automatically detected.
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "splat64"
3
3
  # Should be synced with src/splat/__init__.py
4
- version = "0.32.3"
4
+ version = "0.33.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"}
@@ -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.32.0
7
+ spimdisasm>=1.33.0
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.32.3"
4
+ __version__ = "0.33.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, 32, 0)
10
+ SPIMDISASM_MIN = (1, 33, 0)
11
11
 
12
12
  def configure(self):
13
13
  # Configure spimdisasm
@@ -10,10 +10,6 @@ from .. import __package_name__, __version__
10
10
  from ..disassembler import disassembler_instance
11
11
  from ..util import cache_handler, progress_bar, vram_classes, statistics
12
12
 
13
- # This unused import makes the yaml library faster. don't remove
14
- import pylibyaml # pyright: ignore
15
- import yaml
16
-
17
13
  from colorama import Fore, Style
18
14
  from intervaltree import Interval, IntervalTree
19
15
  import sys
@@ -23,7 +19,7 @@ from ..segtypes.linker_entry import (
23
19
  get_segment_vram_end_symbol_name,
24
20
  )
25
21
  from ..segtypes.segment import Segment
26
- from ..util import log, options, palettes, symbols, relocs
22
+ from ..util import conf, log, options, palettes, symbols, relocs
27
23
 
28
24
  linker_writer: LinkerWriter
29
25
  config: Dict[str, Any]
@@ -142,34 +138,6 @@ def assign_symbols_to_segments():
142
138
  seg.add_symbol(symbol)
143
139
 
144
140
 
145
- def merge_configs(main_config, additional_config):
146
- # Merge rules are simple
147
- # For each key in the dictionary
148
- # - If list then append to list
149
- # - If a dictionary then repeat merge on sub dictionary entries
150
- # - Else assume string or number and replace entry
151
-
152
- for curkey in additional_config:
153
- if curkey not in main_config:
154
- main_config[curkey] = additional_config[curkey]
155
- elif type(main_config[curkey]) != type(additional_config[curkey]):
156
- log.error(f"Type for key {curkey} in configs does not match")
157
- else:
158
- # keys exist and match, see if a list to append
159
- if type(main_config[curkey]) == list:
160
- main_config[curkey] += additional_config[curkey]
161
- elif type(main_config[curkey]) == dict:
162
- # need to merge sub areas
163
- main_config[curkey] = merge_configs(
164
- main_config[curkey], additional_config[curkey]
165
- )
166
- else:
167
- # not a list or dictionary, must be a number or string, overwrite
168
- main_config[curkey] = additional_config[curkey]
169
-
170
- return main_config
171
-
172
-
173
141
  def brief_seg_name(seg: Segment, limit: int, ellipsis="…") -> str:
174
142
  s = seg.name.strip()
175
143
  if len(s) > limit:
@@ -203,25 +171,6 @@ def calc_segment_dependences(
203
171
  return vram_class_to_follows_segments
204
172
 
205
173
 
206
- def initialize_config(
207
- config_path: List[str],
208
- modes: Optional[List[str]],
209
- verbose: bool,
210
- disassemble_all: bool = False,
211
- ) -> Dict[str, Any]:
212
- config: Dict[str, Any] = {}
213
- for entry in config_path:
214
- with open(entry) as f:
215
- additional_config = yaml.load(f.read(), Loader=yaml.SafeLoader)
216
- config = merge_configs(config, additional_config)
217
-
218
- vram_classes.initialize(config.get("vram_classes"))
219
-
220
- options.initialize(config, config_path, modes, verbose, disassemble_all)
221
-
222
- return config
223
-
224
-
225
174
  def read_target_binary() -> bytes:
226
175
  rom_bytes = options.opts.target_path.read_bytes()
227
176
 
@@ -493,20 +442,27 @@ def dump_symbols() -> None:
493
442
 
494
443
 
495
444
  def main(
496
- config_path: List[str],
445
+ config_path: List[Path],
497
446
  modes: Optional[List[str]],
498
447
  verbose: bool,
499
448
  use_cache: bool = True,
500
449
  skip_version_check: bool = False,
501
450
  stdout_only: bool = False,
502
451
  disassemble_all: bool = False,
452
+ make_full_disasm_for_code=False,
503
453
  ):
504
454
  if stdout_only:
505
455
  progress_bar.out_file = sys.stdout
506
456
 
507
457
  # Load config
508
458
  global config
509
- config = initialize_config(config_path, modes, verbose, disassemble_all)
459
+ config = conf.load(
460
+ config_path,
461
+ modes,
462
+ verbose,
463
+ disassemble_all,
464
+ make_full_disasm_for_code,
465
+ )
510
466
 
511
467
  disassembler_instance.create_disassembler_instance(skip_version_check, __version__)
512
468
 
@@ -565,7 +521,10 @@ def main(
565
521
 
566
522
  def add_arguments_to_parser(parser: argparse.ArgumentParser):
567
523
  parser.add_argument(
568
- "config", help="path to a compatible config .yaml file", nargs="+"
524
+ "config",
525
+ help="path to a compatible config .yaml file",
526
+ nargs="+",
527
+ type=Path,
569
528
  )
570
529
  parser.add_argument("--modes", nargs="+", default="all")
571
530
  parser.add_argument("--verbose", action="store_true", help="Enable debug logging")
@@ -585,6 +544,11 @@ def add_arguments_to_parser(parser: argparse.ArgumentParser):
585
544
  help="Disasemble matched functions and migrated data",
586
545
  action="store_true",
587
546
  )
547
+ parser.add_argument(
548
+ "--make-full-disasm-for-code",
549
+ help="Emit a full `.s` file for each `c`/`cpp` segment besides the generated `nonmatchings` individual functions",
550
+ action="store_true",
551
+ )
588
552
 
589
553
 
590
554
  def process_arguments(args: argparse.Namespace):
@@ -596,6 +560,7 @@ def process_arguments(args: argparse.Namespace):
596
560
  args.skip_version_check,
597
561
  args.stdout_only,
598
562
  args.disassemble_all,
563
+ args.make_full_disasm_for_code,
599
564
  )
600
565
 
601
566
 
@@ -0,0 +1,29 @@
1
+ from pathlib import Path
2
+ from typing import Optional, List
3
+
4
+ from ...util import options
5
+
6
+ from .codesubsegment import CommonSegCodeSubsegment
7
+
8
+
9
+ class CommonSegAsm(CommonSegCodeSubsegment):
10
+ @staticmethod
11
+ def is_text() -> bool:
12
+ return True
13
+
14
+ def get_section_flags(self) -> Optional[str]:
15
+ return "ax"
16
+
17
+ def scan(self, rom_bytes: bytes):
18
+ if (
19
+ self.rom_start is not None
20
+ and self.rom_end is not None
21
+ and self.rom_start != self.rom_end
22
+ ):
23
+ self.scan_code(rom_bytes)
24
+
25
+ def split(self, rom_bytes: bytes):
26
+ if self.rom_start == self.rom_end:
27
+ return
28
+
29
+ self.split_as_asm_file(self.out_path())
@@ -24,7 +24,7 @@ class CommonSegAsmtu(CommonSegAsm):
24
24
 
25
25
  with open(out_path, "w", newline="\n") as f:
26
26
  # Write `.text` contents
27
- for line in self.get_file_header():
27
+ for line in self.get_asm_file_header():
28
28
  f.write(line + "\n")
29
29
  f.write(self.spim_section.disassemble())
30
30
 
@@ -40,7 +40,7 @@ class CommonSegAsmtu(CommonSegAsm):
40
40
  if (
41
41
  isinstance(sibling, CommonSegCodeSubsegment)
42
42
  and sibling.spim_section is not None
43
- and not sibling.should_split()
43
+ and not sibling.should_self_split()
44
44
  ):
45
45
  f.write("\n")
46
46
  f.write(f"{sibling.get_section_asm_line()}\n\n")
@@ -58,7 +58,7 @@ class CommonSegAsmtu(CommonSegAsm):
58
58
  if (
59
59
  isinstance(sibling, CommonSegCodeSubsegment)
60
60
  and sibling.spim_section is not None
61
- and not sibling.should_split()
61
+ and not sibling.should_self_split()
62
62
  ):
63
63
  f.write("\n")
64
64
  f.write(f"{sibling.get_section_asm_line()}\n\n")
@@ -276,6 +276,14 @@ class CommonSegC(CommonSegCodeSubsegment):
276
276
  spim_rodata_sym, asm_out_dir, rodata_sym
277
277
  )
278
278
 
279
+ if options.opts.make_full_disasm_for_code:
280
+ # Disable gpRelHack since this file is expected to be built with modern gas
281
+ section = self.spim_section.get_section()
282
+ old_value = section.getGpRelHack()
283
+ section.setGpRelHack(False)
284
+ self.split_as_asm_file(self.asm_out_path())
285
+ section.setGpRelHack(old_value)
286
+
279
287
  def get_c_preamble(self):
280
288
  ret = []
281
289
 
@@ -380,13 +388,16 @@ class CommonSegC(CommonSegCodeSubsegment):
380
388
  # IDO uses the asm processor to embeed assembly, and it doesn't require a special directive to include symbols
381
389
  asm_outpath = asm_out_dir / self.name / f"{sym.filename}.s"
382
390
  rel_asm_outpath = os.path.relpath(asm_outpath, options.opts.base_path)
383
- return f'#pragma GLOBAL_ASM("{rel_asm_outpath}")'
391
+ final_path = Path(rel_asm_outpath).as_posix()
392
+ return f'#pragma GLOBAL_ASM("{final_path}")'
384
393
 
385
394
  if options.opts.use_legacy_include_asm:
386
395
  rel_asm_out_dir = asm_out_dir.relative_to(options.opts.nonmatchings_path)
387
- return f'{macro_name}(const s32, "{rel_asm_out_dir / self.name}", {sym.filename});'
396
+ final_path = (rel_asm_out_dir / self.name).as_posix()
397
+ return f'{macro_name}(const s32, "{final_path}", {sym.filename});'
388
398
 
389
- return f'{macro_name}("{asm_out_dir / self.name}", {sym.filename});'
399
+ final_path = (asm_out_dir / self.name).as_posix()
400
+ return f'{macro_name}("{final_path}", {sym.filename});'
390
401
 
391
402
  def get_c_lines_for_function(
392
403
  self,
@@ -448,7 +459,7 @@ class CommonSegC(CommonSegCodeSubsegment):
448
459
  c_lines += self.get_c_lines_for_rodata_sym(rodata_sym, asm_out_dir)
449
460
 
450
461
  c_path.parent.mkdir(parents=True, exist_ok=True)
451
- with c_path.open("w") as f:
462
+ with c_path.open("w", newline=options.opts.c_newline) as f:
452
463
  f.write("\n".join(c_lines))
453
464
  log.write(f"Wrote {self.name} to {c_path}")
454
465
 
@@ -472,12 +483,12 @@ class CommonSegC(CommonSegCodeSubsegment):
472
483
 
473
484
  dep_path = build_path / c_path.with_suffix(".asmproc.d")
474
485
  dep_path.parent.mkdir(parents=True, exist_ok=True)
475
- with dep_path.open("w") as f:
486
+ with dep_path.open("w", newline=options.opts.c_newline) as f:
476
487
  if options.opts.use_o_as_suffix:
477
488
  o_path = build_path / c_path.with_suffix(".o")
478
489
  else:
479
490
  o_path = build_path / c_path.with_suffix(c_path.suffix + ".o")
480
- f.write(f"{o_path}:")
491
+ f.write(f"{o_path.as_posix()}:")
481
492
  depend_list = []
482
493
  for entry in symbols_entries:
483
494
  if entry.function is not None:
@@ -488,7 +499,7 @@ class CommonSegC(CommonSegCodeSubsegment):
488
499
  outpath.parent.mkdir(parents=True, exist_ok=True)
489
500
 
490
501
  depend_list.append(outpath)
491
- f.write(f" \\\n {outpath}")
502
+ f.write(f" \\\n {outpath.as_posix()}")
492
503
  else:
493
504
  for rodata_sym in entry.rodataSyms:
494
505
  rodata_name = rodata_sym.getName()
@@ -498,9 +509,9 @@ class CommonSegC(CommonSegCodeSubsegment):
498
509
  outpath.parent.mkdir(parents=True, exist_ok=True)
499
510
 
500
511
  depend_list.append(outpath)
501
- f.write(f" \\\n {outpath}")
512
+ f.write(f" \\\n {outpath.as_posix()}")
502
513
 
503
514
  f.write("\n")
504
515
 
505
516
  for depend_file in depend_list:
506
- f.write(f"{depend_file}:\n")
517
+ f.write(f"{depend_file.as_posix()}:\n")
@@ -1,4 +1,5 @@
1
- from typing import Optional
1
+ from pathlib import Path
2
+ from typing import Optional, List
2
3
 
3
4
  import spimdisasm
4
5
  import rabbitizer
@@ -60,7 +61,7 @@ class CommonSegCodeSubsegment(Segment):
60
61
  section.isHandwritten = self.is_hasm
61
62
  section.instrCat = self.instr_category
62
63
  section.detectRedundantFunctionEnd = self.detect_redundant_function_end
63
- section.gpRelHack = not self.use_gp_rel_macro
64
+ section.setGpRelHack(not self.use_gp_rel_macro)
64
65
 
65
66
  def scan_code(self, rom_bytes, is_hasm=False):
66
67
  self.is_hasm = is_hasm
@@ -194,3 +195,55 @@ class CommonSegCodeSubsegment(Segment):
194
195
  return (
195
196
  self.extract and options.opts.is_mode_active("code") and self.should_scan()
196
197
  ) # only split if the segment was scanned first
198
+
199
+ def should_self_split(self) -> bool:
200
+ return self.should_split()
201
+
202
+ def get_asm_file_header(self) -> List[str]:
203
+ ret = []
204
+
205
+ ret.append('.include "macro.inc"')
206
+ ret.append("")
207
+
208
+ ret.extend(self.get_asm_file_extra_directives())
209
+
210
+ preamble = options.opts.generated_s_preamble
211
+ if preamble:
212
+ ret.append(preamble)
213
+ ret.append("")
214
+
215
+ ret.append(self.get_section_asm_line())
216
+ ret.append("")
217
+
218
+ return ret
219
+
220
+ def get_asm_file_extra_directives(self) -> List[str]:
221
+ ret = []
222
+
223
+ ret.append(".set noat") # allow manual use of $at
224
+ ret.append(".set noreorder") # don't insert nops after branches
225
+ if options.opts.add_set_gp_64:
226
+ ret.append(".set gp=64") # allow use of 64-bit general purpose registers
227
+ ret.append("")
228
+
229
+ return ret
230
+
231
+ def asm_out_path(self) -> Path:
232
+ return options.opts.asm_path / self.dir / f"{self.name}.s"
233
+
234
+ def out_path(self) -> Optional[Path]:
235
+ return self.asm_out_path()
236
+
237
+ def split_as_asm_file(self, out_path: Optional[Path]):
238
+ if self.spim_section is None:
239
+ return
240
+
241
+ if out_path:
242
+ out_path.parent.mkdir(parents=True, exist_ok=True)
243
+
244
+ self.print_file_boundaries()
245
+
246
+ with open(out_path, "w", newline="\n") as f:
247
+ for line in self.get_asm_file_header():
248
+ f.write(line + "\n")
249
+ f.write(self.spim_section.disassemble())
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import Optional
2
+ from typing import Optional, List
3
3
  from ...util import options, symbols, log
4
4
 
5
5
  from .codesubsegment import CommonSegCodeSubsegment
@@ -38,33 +38,21 @@ class CommonSegData(CommonSegCodeSubsegment, CommonSegGroup):
38
38
  if self.rom_start is not None and self.rom_end is not None:
39
39
  self.disassemble_data(rom_bytes)
40
40
 
41
+ def get_asm_file_extra_directives(self) -> List[str]:
42
+ return []
43
+
41
44
  def split(self, rom_bytes: bytes):
42
45
  super().split(rom_bytes)
43
46
 
44
- if self.type.startswith(".") and not options.opts.disassemble_all:
45
- return
46
-
47
47
  if self.spim_section is None or not self.should_self_split():
48
48
  return
49
49
 
50
- path = self.asm_out_path()
51
-
52
- path.parent.mkdir(parents=True, exist_ok=True)
53
-
54
- self.print_file_boundaries()
55
-
56
- with path.open("w", newline="\n") as f:
57
- f.write('.include "macro.inc"\n\n')
58
- preamble = options.opts.generated_s_preamble
59
- if preamble:
60
- f.write(preamble + "\n")
61
-
62
- f.write(f"{self.get_section_asm_line()}\n\n")
63
-
64
- f.write(self.spim_section.disassemble())
50
+ self.split_as_asm_file(self.asm_out_path())
65
51
 
66
52
  def should_self_split(self) -> bool:
67
- return options.opts.is_mode_active("data")
53
+ return options.opts.is_mode_active("data") and (
54
+ not self.type.startswith(".") or options.opts.disassemble_all
55
+ )
68
56
 
69
57
  def should_scan(self) -> bool:
70
58
  return True