cartridge-sdk 1.0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lukas Soigneux
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: cartridge-sdk
3
+ Version: 1.0.0
4
+ Summary: GBA SDK — build tool for Game Boy Advance
5
+ Author-email: Lukas Soigneux <lukas.soigneux@epitech.eu>
6
+ License-Expression: MIT
7
+ Keywords: GBA,Game Boy,GameBoy Advance,Cartridge
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: click
11
+ Requires-Dist: Pillow
12
+ Dynamic: license-file
13
+
14
+ <div align="center">
15
+ <a href="https://github.com/lukas-sgx/">
16
+ <img src="https://github.com/lukas-sgx/GBA-sdk/blob/main/assets/gba-logo.png?raw=true" alt="Logo" height="180" style="border-radius: 10px">
17
+ </a>
18
+
19
+ <h3 align="center">GameBoy Advance - SDK</h3>
20
+
21
+ <p align="center">
22
+ A Software Development Kit for developers who want to build GameBoy Advance games.
23
+ <br />
24
+ <a href="https://github.com/lukas-sgx/GBA-sdk"><strong>Explore the docs »</strong></a>
25
+ <br />
26
+ <br />
27
+ <a href="https://github.com/lukas-sgx/GBA-sdk">View Demo</a>
28
+ &middot;
29
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
30
+ &middot;
31
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
32
+ </p>
33
+ </div>
34
+
35
+ <details>
36
+ <summary>Table of Contents</summary>
37
+ <ol>
38
+ <li>
39
+ <a href="#about-the-project">About The Project</a>
40
+ <ul>
41
+ <li><a href="#built-with">Built With</a></li>
42
+ </ul>
43
+ </li>
44
+ <li>
45
+ <a href="#getting-started">Getting Started</a>
46
+ <ul>
47
+ <li><a href="#prerequisites">Prerequisites</a></li>
48
+ <li><a href="#installation">Installation</a></li>
49
+ </ul>
50
+ </li>
51
+ <li><a href="#usage">Usage</a></li>
52
+ <li><a href="#roadmap">Roadmap</a></li>
53
+ <li><a href="#contributing">Contributing</a></li>
54
+ <li><a href="#license">License</a></li>
55
+ <li><a href="#contact">Contact</a></li>
56
+ <li><a href="#acknowledgments">Acknowledgments</a></li>
57
+ </ol>
58
+ </details>
59
+
60
+ ## About The Project
61
+
62
+ This SDK aims to simplify GameBoy Advance homebrew development by allowing developers to write game logic in Python. It handles the underlying compilation, asset conversion, and bindings to interface efficiently with the GBA hardware.
63
+
64
+ ### Built With
65
+
66
+ [![Python][Python-shield]][Python-url]
67
+ [![C][C-shield]][C-url]
68
+ [![Assembly][ASM-shield]][ASM-url]
69
+ [![CMake][CMake-shield]][CMake-url]
70
+
71
+ ## Getting Started
72
+
73
+ To get a local copy up and running, follow these simple steps.
74
+
75
+ ### Prerequisites
76
+
77
+ You need Python 3.x installed on your system
78
+
79
+ ### Installation
80
+
81
+ #### Development mode (clone the repo, with local changes)
82
+ 1. Clone the repo
83
+ ```sh
84
+ git clone https://github.com/lukas-sgx/GBA-sdk.git
85
+ cd GBA-sdk
86
+ ```
87
+ 2. Install the SDK in development mode
88
+ ```sh
89
+ pip install -e .
90
+ ```
91
+
92
+ #### Release mode (stable version from PyPI)
93
+ ```sh
94
+ pip install cartridge-sdk
95
+ ```
96
+ ## Usage
97
+
98
+ Here is a quick example of how to dump header of ROM:
99
+ ```sh
100
+ $ cartridge-sdk hdr dump bin/ExampleGBA.gba
101
+ bin/ExampleGBA.gba:
102
+ |-- entry
103
+ | |-- valid: True
104
+ | |-- raw: 0xea00002e
105
+ | `-- opcode: b 0xc0
106
+ |-- nintendo logo:
107
+ | |-- status: True
108
+ | `-- debugging: True
109
+ |-- game title: EXAMPLEGBA
110
+ |-- game code:
111
+ | |-- code: BXXE
112
+ | |-- date: 2003.. (new)
113
+ | `-- language: USA/English
114
+ |-- marker code:
115
+ | |-- id: 01
116
+ | `-- developer: Nintendo
117
+ |-- fixed value: valid (96h)
118
+ |-- unit code: 00h
119
+ |-- device type: 00h
120
+ |-- reserved: valid
121
+ |-- software_ver: 00h
122
+ `-- checksum:
123
+ |-- valid: True
124
+ |-- rom: e3
125
+ `-- our: e3
126
+ ```
127
+
128
+ *For more advanced examples, please refer to the [Documentation](https://github.com/lukas-sgx/GBA-sdk).*
129
+
130
+
131
+ ## Roadmap
132
+
133
+ - [x] Automated header checker `.gba` ROM
134
+ - [x] Automated compilation to `.gba` ROM
135
+ - [x] Font asset pipeline (PNG to C file converter)
136
+ - [ ] Core GBA bindings (Video, Audio, Inputs)
137
+ - [ ] Asset pipeline (PNG to GBA sprite palette converter)
138
+
139
+ See the [open issues](https://github.com/lukas-sgx/GBA-sdk/issues) for a full list of proposed features (and known issues).
140
+
141
+
142
+ ## Contributing
143
+
144
+ Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
145
+
146
+ 1. Fork the Project
147
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
148
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
149
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
150
+ 5. Open a Pull Request
151
+
152
+ ### Top contributors:
153
+
154
+ <a href="https://github.com/lukas-sgx/GBA-sdk/graphs/contributors">
155
+ <img src="https://contrib.rocks/image?repo=lukas-sgx/GBA-sdk" alt="contrib.rocks image" />
156
+ </a>
157
+
158
+ ## License
159
+
160
+ Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
161
+
162
+ ## Contact
163
+
164
+ Lukas Soigneux - lukas.soigneux@epitech.eu
165
+
166
+ ## Acknowledgments
167
+
168
+ * [GBATEK](https://mgba-emu.github.io/gbatek/) - GameBoy Advance Technical Info
169
+ * [Ayyboy-Advance](https://github.com/YannMagnin/ayyboy-advance) - Great emulator for testing
170
+
171
+ [Python-shield]: https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white
172
+ [Python-url]: https://www.python.org/
173
+ [C-shield]: https://img.shields.io/badge/C-005895?style=for-the-badge&logo=c&logoColor=white
174
+ [C-url]: https://www.c-language.org/
175
+ [ASM-shield]: https://img.shields.io/badge/-Assembly-f2921d?style=for-the-badge&logo=assemblyscript&logoColor=white
176
+ [ASM-url]: https://developer.arm.com/
177
+ [CMake-shield]: https://img.shields.io/badge/-CMake-064F8C?style=for-the-badge&logo=cmake&logoColor=white
178
+ [CMake-url]: https://cmake.org/
@@ -0,0 +1,165 @@
1
+ <div align="center">
2
+ <a href="https://github.com/lukas-sgx/">
3
+ <img src="https://github.com/lukas-sgx/GBA-sdk/blob/main/assets/gba-logo.png?raw=true" alt="Logo" height="180" style="border-radius: 10px">
4
+ </a>
5
+
6
+ <h3 align="center">GameBoy Advance - SDK</h3>
7
+
8
+ <p align="center">
9
+ A Software Development Kit for developers who want to build GameBoy Advance games.
10
+ <br />
11
+ <a href="https://github.com/lukas-sgx/GBA-sdk"><strong>Explore the docs »</strong></a>
12
+ <br />
13
+ <br />
14
+ <a href="https://github.com/lukas-sgx/GBA-sdk">View Demo</a>
15
+ &middot;
16
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
17
+ &middot;
18
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
19
+ </p>
20
+ </div>
21
+
22
+ <details>
23
+ <summary>Table of Contents</summary>
24
+ <ol>
25
+ <li>
26
+ <a href="#about-the-project">About The Project</a>
27
+ <ul>
28
+ <li><a href="#built-with">Built With</a></li>
29
+ </ul>
30
+ </li>
31
+ <li>
32
+ <a href="#getting-started">Getting Started</a>
33
+ <ul>
34
+ <li><a href="#prerequisites">Prerequisites</a></li>
35
+ <li><a href="#installation">Installation</a></li>
36
+ </ul>
37
+ </li>
38
+ <li><a href="#usage">Usage</a></li>
39
+ <li><a href="#roadmap">Roadmap</a></li>
40
+ <li><a href="#contributing">Contributing</a></li>
41
+ <li><a href="#license">License</a></li>
42
+ <li><a href="#contact">Contact</a></li>
43
+ <li><a href="#acknowledgments">Acknowledgments</a></li>
44
+ </ol>
45
+ </details>
46
+
47
+ ## About The Project
48
+
49
+ This SDK aims to simplify GameBoy Advance homebrew development by allowing developers to write game logic in Python. It handles the underlying compilation, asset conversion, and bindings to interface efficiently with the GBA hardware.
50
+
51
+ ### Built With
52
+
53
+ [![Python][Python-shield]][Python-url]
54
+ [![C][C-shield]][C-url]
55
+ [![Assembly][ASM-shield]][ASM-url]
56
+ [![CMake][CMake-shield]][CMake-url]
57
+
58
+ ## Getting Started
59
+
60
+ To get a local copy up and running, follow these simple steps.
61
+
62
+ ### Prerequisites
63
+
64
+ You need Python 3.x installed on your system
65
+
66
+ ### Installation
67
+
68
+ #### Development mode (clone the repo, with local changes)
69
+ 1. Clone the repo
70
+ ```sh
71
+ git clone https://github.com/lukas-sgx/GBA-sdk.git
72
+ cd GBA-sdk
73
+ ```
74
+ 2. Install the SDK in development mode
75
+ ```sh
76
+ pip install -e .
77
+ ```
78
+
79
+ #### Release mode (stable version from PyPI)
80
+ ```sh
81
+ pip install cartridge-sdk
82
+ ```
83
+ ## Usage
84
+
85
+ Here is a quick example of how to dump header of ROM:
86
+ ```sh
87
+ $ cartridge-sdk hdr dump bin/ExampleGBA.gba
88
+ bin/ExampleGBA.gba:
89
+ |-- entry
90
+ | |-- valid: True
91
+ | |-- raw: 0xea00002e
92
+ | `-- opcode: b 0xc0
93
+ |-- nintendo logo:
94
+ | |-- status: True
95
+ | `-- debugging: True
96
+ |-- game title: EXAMPLEGBA
97
+ |-- game code:
98
+ | |-- code: BXXE
99
+ | |-- date: 2003.. (new)
100
+ | `-- language: USA/English
101
+ |-- marker code:
102
+ | |-- id: 01
103
+ | `-- developer: Nintendo
104
+ |-- fixed value: valid (96h)
105
+ |-- unit code: 00h
106
+ |-- device type: 00h
107
+ |-- reserved: valid
108
+ |-- software_ver: 00h
109
+ `-- checksum:
110
+ |-- valid: True
111
+ |-- rom: e3
112
+ `-- our: e3
113
+ ```
114
+
115
+ *For more advanced examples, please refer to the [Documentation](https://github.com/lukas-sgx/GBA-sdk).*
116
+
117
+
118
+ ## Roadmap
119
+
120
+ - [x] Automated header checker `.gba` ROM
121
+ - [x] Automated compilation to `.gba` ROM
122
+ - [x] Font asset pipeline (PNG to C file converter)
123
+ - [ ] Core GBA bindings (Video, Audio, Inputs)
124
+ - [ ] Asset pipeline (PNG to GBA sprite palette converter)
125
+
126
+ See the [open issues](https://github.com/lukas-sgx/GBA-sdk/issues) for a full list of proposed features (and known issues).
127
+
128
+
129
+ ## Contributing
130
+
131
+ Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
132
+
133
+ 1. Fork the Project
134
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
135
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
136
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
137
+ 5. Open a Pull Request
138
+
139
+ ### Top contributors:
140
+
141
+ <a href="https://github.com/lukas-sgx/GBA-sdk/graphs/contributors">
142
+ <img src="https://contrib.rocks/image?repo=lukas-sgx/GBA-sdk" alt="contrib.rocks image" />
143
+ </a>
144
+
145
+ ## License
146
+
147
+ Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
148
+
149
+ ## Contact
150
+
151
+ Lukas Soigneux - lukas.soigneux@epitech.eu
152
+
153
+ ## Acknowledgments
154
+
155
+ * [GBATEK](https://mgba-emu.github.io/gbatek/) - GameBoy Advance Technical Info
156
+ * [Ayyboy-Advance](https://github.com/YannMagnin/ayyboy-advance) - Great emulator for testing
157
+
158
+ [Python-shield]: https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white
159
+ [Python-url]: https://www.python.org/
160
+ [C-shield]: https://img.shields.io/badge/C-005895?style=for-the-badge&logo=c&logoColor=white
161
+ [C-url]: https://www.c-language.org/
162
+ [ASM-shield]: https://img.shields.io/badge/-Assembly-f2921d?style=for-the-badge&logo=assemblyscript&logoColor=white
163
+ [ASM-url]: https://developer.arm.com/
164
+ [CMake-shield]: https://img.shields.io/badge/-CMake-064F8C?style=for-the-badge&logo=cmake&logoColor=white
165
+ [CMake-url]: https://cmake.org/
@@ -0,0 +1,2 @@
1
+ __version__ = "1.0.0"
2
+ __author__ = "lukas-sgx"
@@ -0,0 +1,17 @@
1
+ import click
2
+ from cartridge.modules.build import build
3
+ from cartridge.modules.hdr import hdr
4
+ from cartridge.modules.conv import conv
5
+ from cartridge.modules.init import init
6
+
7
+ @click.group(help="Cartridge SDK CLI entry")
8
+ def cli():
9
+ pass
10
+
11
+ cli.add_command(build)
12
+ cli.add_command(hdr)
13
+ cli.add_command(conv)
14
+ cli.add_command(init)
15
+
16
+ def main():
17
+ cli()
@@ -0,0 +1,38 @@
1
+ import click
2
+ import subprocess
3
+ import os
4
+ import glob
5
+ from cartridge.services.generation import Generation
6
+
7
+ def listBin() -> list[str]:
8
+ return glob.glob("bin/*.bin")
9
+
10
+ @click.command(help="Craft cartridge ROM (GBA)")
11
+ @click.option("-s", "--source", required=True, type=click.Path(exists=True), help="Path to the CMake source directory")
12
+ def build(source: str) -> None:
13
+ os.makedirs("bin", exist_ok=True)
14
+ os.makedirs("build", exist_ok=True)
15
+
16
+ PREV_DIR = os.getcwd()
17
+
18
+ os.chdir(source)
19
+
20
+ subprocess.run([
21
+ "cmake",
22
+ "."
23
+ ], check=True)
24
+
25
+ os.chdir(PREV_DIR)
26
+
27
+ subprocess.run([
28
+ "cmake",
29
+ "--build",
30
+ source
31
+ ], check=True)
32
+
33
+ for binary in listBin():
34
+ project = binary.split(".")[0]
35
+ output_path = f"{project}.gba"
36
+
37
+ with open(output_path, "wb") as output_file:
38
+ Generation(binary, output_file, project.split("/")[1], "BXXE")
@@ -0,0 +1,19 @@
1
+ import click
2
+ from PIL import Image
3
+ from cartridge.services.conversion import Conversion
4
+
5
+ @click.command(help="Convert font asset into raw C file")
6
+ @click.option("-o", "--output", required=True, type=click.Path(exists=False), help="Path to the generated C file")
7
+ @click.option("-w", "--width", required=True, type=click.INT, help="Character width in pixels")
8
+ @click.option("-h", "--height", required=True, type=click.INT, help="Character height in pixels")
9
+ @click.option("-m", "--margin", required=True, type=click.INT, help="Margin between characters in pixels")
10
+ @click.option("-l", "--line-height", required=True, type=click.INT, help="Virtual alignment line height in pixels")
11
+ @click.option("-d", "--debug", is_flag=True, help="Enable debug mode")
12
+ @click.argument("ASSET_PATH", required=True, type=click.STRING, nargs=1)
13
+ def conv(asset_path: str, output: str, width: int, height: int, margin: int, line_height: int, debug) -> None:
14
+ img = Image.open(asset_path, mode="r").convert("L")
15
+ dimension = (height, width, line_height, margin)
16
+ convert = Conversion(img, dimension, debug)
17
+
18
+ with open(output, mode="w") as file:
19
+ file.write(convert.get_content())
@@ -0,0 +1,10 @@
1
+ import click
2
+ from cartridge.modules.hdr.dump import dump
3
+ from cartridge.modules.hdr.gen import gen
4
+
5
+ @click.group(help="Manage the cartridge header")
6
+ def hdr():
7
+ pass
8
+
9
+ hdr.add_command(dump)
10
+ hdr.add_command(gen)
@@ -0,0 +1,9 @@
1
+ import click
2
+ from cartridge.services.header import Header
3
+
4
+ @click.command(help="Dump cartridge header information")
5
+ @click.argument("GBA_FILES", required=False, nargs=-1)
6
+ def dump(gba_files):
7
+ for file in gba_files:
8
+ header = Header(file)
9
+ header.display_header()
@@ -0,0 +1,10 @@
1
+ import click
2
+ from cartridge.services.generation import Generation
3
+ from typing import BinaryIO
4
+
5
+ @click.command(help="Insert the header into a binary")
6
+ @click.option("-b", "--binary", required=True, nargs=1, type=click.File("rb"), help="input binary file")
7
+ @click.option("-o", "--output", required=True, nargs=1, type=click.File("wb"), help="output filename")
8
+ @click.option("-n", "--name", nargs=1, type=click.STRING, help="internal cartridge name", show_default=True, default="filename")
9
+ def gen(binary: click.File, output: BinaryIO, name: str):
10
+ Generation(binary.name, output, name, "BXXE")
@@ -0,0 +1,42 @@
1
+ import click
2
+ import urllib.request
3
+ import io
4
+ import tarfile
5
+ from cartridge import __version__
6
+
7
+ @click.command(help="Initialize project with default build")
8
+ def init():
9
+ url = f"https://api.github.com/repos/lukas-sgx/GBA-sdk/tarball/v{__version__}"
10
+ headers = {"User-Agent": "Python-Cartridge"}
11
+ req = urllib.request.Request(url, headers=headers)
12
+
13
+ click.echo(
14
+ click.style("Downloading ", fg="green", bold=True) +
15
+ click.style(f"GBA-sdk v{__version__}\n") +
16
+ click.style(f"\t {url}", fg=(138,138,138))
17
+ )
18
+
19
+ with urllib.request.urlopen(req) as response:
20
+ tar_data = io.BytesIO(response.read())
21
+
22
+ extracted_count = 0
23
+ with tarfile.open(fileobj=tar_data, mode="r:gz") as tar:
24
+
25
+ for member in tar.getmembers():
26
+ parts = member.name.split("/", 1)
27
+ if len(parts) < 2:
28
+ continue
29
+
30
+ relative_path = parts[1]
31
+ if relative_path.startswith(("sandbox/", "libs/")) and not member.isdir():
32
+ member.name = relative_path
33
+ click.echo(
34
+ click.style("Extracting ", fg="green", bold=True) +
35
+ click.style(f"{member.name} -> .")
36
+ )
37
+ tar.extract(member, path=".")
38
+ extracted_count += 1
39
+ click.echo(
40
+ click.style("Finished", fg="green", bold=True) +
41
+ click.style(f" {extracted_count} dir(s) extracted\n")
42
+ )
@@ -0,0 +1,156 @@
1
+ class Conversion:
2
+ def __init__(self, img, attributes, debug) -> None:
3
+ self.img = img
4
+ self.height = attributes[0]
5
+ self.width = attributes[1]
6
+ self.attributes = attributes
7
+ self.is_debug = debug
8
+
9
+ def get_content(self):
10
+ content = "// Auto Generated font asset file - CARTRIDGE\n\n"
11
+ content += "#include <stdint.h>\n"
12
+ content += '#include "font.h"\n\n'
13
+
14
+ content += "void init_font(gba_font_t *font, enum font_type type) {\n"
15
+ content += "\tstatic const uint8_t font_bitmap_monospaced[] = {\n"
16
+
17
+ for ascii in range(32, 128):
18
+ pixels_bytes = self.get_pixels_bitmap_glyph(ascii)
19
+
20
+ row_values = ", ".join(self.get_monospace_glyph(pixels_bytes, ascii))
21
+ printable = (
22
+ chr(ascii) if chr(ascii) not in ("\\", "'") else f"\\{chr(ascii)}"
23
+ )
24
+ content += f"\t\t{row_values}, // {ascii}: '{printable}'\n"
25
+
26
+ content += "\t};\n\n"
27
+
28
+ content += "\tconst glyph_t glyphs_monospaced[] = {\n\t\t{"
29
+ content += f" .width = {self.width}, .height = {self.height} "
30
+ content += "},\n\t\t{ .width = 0, .height = 0 }\n"
31
+ content += "\t};\n\n"
32
+
33
+ content += "\tstatic const uint8_t font_bitmap_proportional[] = {\n"
34
+ for ascii in range(32, 128):
35
+ pixels_bytes = self.get_pixels_bitmap_glyph(ascii)
36
+ proporitonal_glyph = self.get_proportional_glyph(pixels_bytes)
37
+ content += "\t\t"
38
+ content += ", ".join(proporitonal_glyph)
39
+ content += ",\n"
40
+ content += "\t};\n\n"
41
+
42
+ content += "\tstatic const glyph_t glyphs_proportional[] = {\n"
43
+ for ascii in range(32, 128):
44
+ pixels_bytes = self.get_pixels_bitmap_glyph(ascii)
45
+ proporitonal_glyph = self.get_proportional_glyph(pixels_bytes)
46
+ content += "\t\t{"
47
+ content += f".height = {len(proporitonal_glyph)}, "
48
+ content += f".width = {self.get_max_width_proportional(proporitonal_glyph)}, "
49
+ content += "},\n"
50
+ content += "\t};\n\n"
51
+
52
+ content += "\tfont->type = type;\n"
53
+ content += "\tfont->monospaced.bitmap = font_bitmap_monospaced;\n"
54
+ content += "\tfont->monospaced.glyphs = glyphs_monospaced;\n"
55
+ content += "\tfont->proportional.bitmap = font_bitmap_proportional;\n"
56
+ content += "\tfont->proportional.glyphs = glyphs_proportional;\n"
57
+
58
+ content += "}\n"
59
+
60
+ return content
61
+
62
+ def get_glyph_list(self, pixel_bytes: bytes) -> list[str]:
63
+ glyph_list: list[str] = []
64
+
65
+ for y in range(self.height):
66
+ row = str()
67
+ for x in range(self.width):
68
+ row += "1" if pixel_bytes[y * self.width + x] > 200 else "0"
69
+ glyph_list.append(f"0x{(int(row, 2)):02X}")
70
+
71
+ return glyph_list
72
+
73
+ def get_monospace_glyph(self, pixel_bytes: bytes, ascii: int) -> list[str]:
74
+ glyph_list: list[str] = self.get_glyph_list(pixel_bytes)
75
+
76
+ if self.is_debug:
77
+ print(f"{ascii}:")
78
+ print(format("~" * 28))
79
+ for glyph in glyph_list:
80
+ bytes_glyph = bytes.fromhex(glyph[2:])
81
+ row = "".join(f"{b:08b}" for b in bytes_glyph)
82
+ print(
83
+ f"{''.join('#' if r == '1' else '-' for r in row)} -> {row} -> 0x{int(row, 2):02x}"
84
+ )
85
+ print(format("~" * 28) + "\n")
86
+
87
+ return glyph_list
88
+
89
+ def get_max_width_proportional(self, glyph_list: list[str]) -> int:
90
+ min_x = None
91
+ max_x = None
92
+ max_width = 0
93
+
94
+ for glyph in glyph_list:
95
+ bytes_glyph = bytes.fromhex(glyph[2:])
96
+ row = "".join(f"{b:08b}" for b in bytes_glyph)
97
+ start_idx = row.find("1")
98
+ end_idx = row.rfind("1")
99
+
100
+ if start_idx != -1:
101
+ if min_x is None or start_idx < min_x:
102
+ min_x = start_idx
103
+ if max_x is None or end_idx > max_x:
104
+ max_x = end_idx + 1
105
+
106
+ if min_x is None or max_x is None:
107
+ return 8
108
+ else:
109
+ max_width = max_x - min_x
110
+ return max_width
111
+
112
+ def get_proportional_glyph(self, pixel_bytes: bytes) -> list[str]:
113
+ glyph_list: list[str] = self.get_glyph_list(pixel_bytes)
114
+ min_x = None
115
+ max_x = None
116
+ min_y = None
117
+ max_y = None
118
+ idx = 0
119
+
120
+ for glyph in glyph_list:
121
+ bytes_glyph = bytes.fromhex(glyph[2:])
122
+ glyph = "".join(f"{b:08b}" for b in bytes_glyph)
123
+ start_idx = glyph.find("1")
124
+ end_idx = glyph.rfind("1")
125
+ if start_idx != -1:
126
+ if min_y is None:
127
+ min_y = idx
128
+ max_y = idx + 1
129
+ if min_x is None or start_idx < min_x:
130
+ min_x = start_idx
131
+ if max_x is None or end_idx > max_x:
132
+ max_x = end_idx + 1
133
+ idx += 1
134
+
135
+ if min_y is not None or max_y is not None:
136
+ glyph_list = glyph_list[min_y:max_y]
137
+
138
+ idx = 0
139
+ for glyph in glyph_list:
140
+ bytes_glyph = bytes.fromhex(glyph[2:])
141
+ row = "".join(f"{b:08b}" for b in bytes_glyph)
142
+ row = row[min_x:max_x]
143
+ glyph_list[idx] = f"0x{(int(row, 2)):02X}"
144
+ idx += 1
145
+
146
+ return glyph_list
147
+
148
+ def get_pixels_bitmap_glyph(self, ascii):
149
+ index = ascii - 32
150
+ top = index // 16 * (self.attributes[0] + self.attributes[2])
151
+ left = index % 16 * (self.attributes[1] + self.attributes[3])
152
+ glyph = self.img.crop(
153
+ (left, top, left + self.attributes[1], top + self.attributes[0])
154
+ )
155
+
156
+ return glyph.tobytes()
@@ -0,0 +1,85 @@
1
+ import click
2
+ import struct
3
+ from typing import BinaryIO
4
+
5
+ ROM_ENTRY_POINT: int = 0xEA00002E
6
+
7
+ NINTENDO_LOGO = bytes([
8
+ 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21,
9
+ 0x3D, 0x84, 0x82, 0x0A, 0x84, 0xE4, 0x09, 0xAD,
10
+ 0x11, 0x24, 0x8B, 0x98, 0xC0, 0x81, 0x7F, 0x21,
11
+ 0xA3, 0x52, 0xBE, 0x19, 0x93, 0x09, 0xCE, 0x20,
12
+ 0x10, 0x46, 0x4A, 0x4A, 0xF8, 0x27, 0x31, 0xEC,
13
+ 0x58, 0xC7, 0xE8, 0x33, 0x82, 0xE3, 0xCE, 0xBF,
14
+ 0x85, 0xF4, 0xDF, 0x94, 0xCE, 0x4B, 0x09, 0xC1,
15
+ 0x94, 0x56, 0x8A, 0xC0, 0x13, 0x72, 0xA7, 0xFC,
16
+ 0x9F, 0x84, 0x4D, 0x73, 0xA3, 0xCA, 0x9A, 0x61,
17
+ 0x58, 0x97, 0xA3, 0x27, 0xFC, 0x03, 0x98, 0x76,
18
+ 0x23, 0x1D, 0xC7, 0x61, 0x03, 0x04, 0xAE, 0x56,
19
+ 0xBF, 0x38, 0x84, 0x00, 0x40, 0xA7, 0x0E, 0xFD,
20
+ 0xFF, 0x52, 0xFE, 0x03, 0x6F, 0x95, 0x30, 0xF1,
21
+ 0x97, 0xFB, 0xC0, 0x85, 0x60, 0xD6, 0x80, 0x25,
22
+ 0xA9, 0x63, 0xBE, 0x03, 0x01, 0x4E, 0x38, 0xE2,
23
+ 0xF9, 0xA2, 0x34, 0xFF, 0xBB, 0x3E, 0x03, 0x44,
24
+ 0x78, 0x00, 0x90, 0xCB, 0x88, 0x11, 0x3A, 0x94,
25
+ 0x65, 0xC0, 0x7C, 0x63, 0x87, 0xF0, 0x3C, 0xAF,
26
+ 0xD6, 0x25, 0xE4, 0x8B, 0x38, 0x0A, 0xAC, 0x72,
27
+ 0x21, 0xD4, 0xF8, 0x07,
28
+ ])
29
+
30
+
31
+ class Generation:
32
+ def __init__(self, bin_file: str, output: BinaryIO, name: str, game_code: str) -> None:
33
+ self.bin_file = bin_file
34
+ self.output = output
35
+ self.name = name
36
+ self.game_code = game_code
37
+
38
+ self.write()
39
+
40
+ def checksum(self, header: bytearray) -> int:
41
+ chk = 0
42
+ for i in range(0xA0, 0xBD):
43
+ chk -= header[i]
44
+ return (chk - 0x19) & 0xFF
45
+
46
+
47
+ def header_fill(self,
48
+ game_title: str = "",
49
+ game_code: str = "BXXE",
50
+ maker_code: str = "01",
51
+ version: int = 0,
52
+ ) -> bytearray:
53
+ header = bytearray(0xC0)
54
+ struct.pack_into("<I", header, 0x00, ROM_ENTRY_POINT)
55
+
56
+ header[0x04:0xA0] = NINTENDO_LOGO
57
+ header[0xA0:0xAC] = game_title.upper().encode("ascii")[:12].ljust(12, b"\x00")
58
+ header[0xAC:0xB0] = game_code.upper().encode("ascii")[:4].ljust(4, b"\x00")
59
+ header[0xB0:0xB2] = maker_code.upper().encode("ascii")[:2].ljust(2, b"\x00")
60
+ header[0xB2] = 0x96
61
+ header[0xB3] = 0x00
62
+ header[0xB4] = 0x00
63
+ header[0xBC] = version & 0xFF
64
+ header[0xBD] = self.checksum(header)
65
+ header[0xBE] = 0x0
66
+ header[0xBF] = 0x0
67
+
68
+
69
+ return header
70
+
71
+ def write(self):
72
+ click.echo(
73
+ click.style("[GEN] ", fg="blue", bold=True) +
74
+ click.style(f"{self.bin_file} " , bold=True) +
75
+ click.style(f"-> {self.output.name}", fg="cyan")
76
+ )
77
+
78
+ with open(f"{self.bin_file}", "rb") as bin_content:
79
+ header = self.header_fill(game_title=self.name, game_code=self.game_code)
80
+ rom_data = bin_content.read()
81
+
82
+ self.output.write(header + rom_data)
83
+
84
+
85
+ click.echo(click.style("[OK] GBA succefully build", fg="green", bold=True))
@@ -0,0 +1,228 @@
1
+ import click
2
+
3
+ BYTE_LEN = 4
4
+ LOGO_BYTE_LEN = 156
5
+ GAME_TITLE_BYTE_LEN = 12
6
+
7
+ class Header:
8
+ def __init__(self, gba_file: str) -> None:
9
+ self.gba_file = gba_file
10
+ self.raw: bytes
11
+ self.address = []
12
+ self.pc = 0
13
+
14
+ with open(gba_file, "rb") as fd:
15
+ self.raw = fd.read()
16
+ self.stock_address()
17
+
18
+ def debug(self):
19
+ for addr in self.address:
20
+ click.echo(f"{addr:08x}")
21
+
22
+ def stock_address(self):
23
+ for i in range(0, len(self.raw), 4):
24
+ chunk = self.raw[i : i + 4]
25
+ if len(chunk) == 4:
26
+ self.address.append(int.from_bytes(chunk, "big"))
27
+
28
+ def to_little_endian(self, addr: int) -> int:
29
+ return int.from_bytes(addr.to_bytes(4, "big"), "little")
30
+
31
+ def get_opcode_branch(self, addr: int) -> str:
32
+ addr = self.to_little_endian(addr)
33
+ family = (addr >> 25) & 0x7
34
+ instruction = ""
35
+
36
+ if family == 0b101:
37
+ instruction = "b"
38
+ offset = addr & 0xFFFFFF
39
+ if offset & 0x8000000:
40
+ offset |= 0xFF0000000
41
+ target = self.pc + (BYTE_LEN * 2) + (offset << 2)
42
+ return f"{instruction} 0x{target:02x}"
43
+
44
+ def is_valid_entry(self, addr: int) -> bool:
45
+ addr = self.to_little_endian(addr)
46
+ cond = (addr >> 28) & 0xF
47
+ family = (addr >> 25) & 0x7
48
+ return cond == 0xE and family == 0b101
49
+
50
+ def is_valid_nintendo_logo(self, addresses: list[int]) -> bool:
51
+ NINTENDO_LOGO = bytes([
52
+ 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21,
53
+ 0x3D, 0x84, 0x82, 0x0A, 0x84, 0xE4, 0x09, 0xAD,
54
+ 0x11, 0x24, 0x8B, 0x98, 0xC0, 0x81, 0x7F, 0x21,
55
+ 0xA3, 0x52, 0xBE, 0x19, 0x93, 0x09, 0xCE, 0x20,
56
+ 0x10, 0x46, 0x4A, 0x4A, 0xF8, 0x27, 0x31, 0xEC,
57
+ 0x58, 0xC7, 0xE8, 0x33, 0x82, 0xE3, 0xCE, 0xBF,
58
+ 0x85, 0xF4, 0xDF, 0x94, 0xCE, 0x4B, 0x09, 0xC1,
59
+ 0x94, 0x56, 0x8A, 0xC0, 0x13, 0x72, 0xA7, 0xFC,
60
+ 0x9F, 0x84, 0x4D, 0x73, 0xA3, 0xCA, 0x9A, 0x61,
61
+ 0x58, 0x97, 0xA3, 0x27, 0xFC, 0x03, 0x98, 0x76,
62
+ 0x23, 0x1D, 0xC7, 0x61, 0x03, 0x04, 0xAE, 0x56,
63
+ 0xBF, 0x38, 0x84, 0x00, 0x40, 0xA7, 0x0E, 0xFD,
64
+ 0xFF, 0x52, 0xFE, 0x03, 0x6F, 0x95, 0x30, 0xF1,
65
+ 0x97, 0xFB, 0xC0, 0x85, 0x60, 0xD6, 0x80, 0x25,
66
+ 0xA9, 0x63, 0xBE, 0x03, 0x01, 0x4E, 0x38, 0xE2,
67
+ 0xF9, 0xA2, 0x34, 0xFF, 0xBB, 0x3E, 0x03, 0x44,
68
+ 0x78, 0x00, 0x90, 0xCB, 0x88, 0x11, 0x3A, 0x94,
69
+ 0x65, 0xC0, 0x7C, 0x63, 0x87, 0xF0, 0x3C, 0xAF,
70
+ 0xD6, 0x25, 0xE4, 0x8B, 0x38, 0x0A, 0xAC, 0x72,
71
+ 0x21, 0xD4, 0xF8, 0x07,
72
+ ])
73
+ raw_bytes = b''
74
+ for hexAddr in addresses:
75
+ raw_bytes += hexAddr.to_bytes(4, byteorder="big") # todo: verif bit 2, 7 on 0x21h if debugging or not -> https://mgba-emu.github.io/gbatek/#gbacartridgeheader
76
+
77
+ return raw_bytes == NINTENDO_LOGO
78
+
79
+ def is_debugging(self, addr: int) -> bool:
80
+ address = list(addr.to_bytes(4, byteorder="big"))
81
+ return (address[0] & 0b00100001) == 0b00100001
82
+
83
+ def get_game_title(self, addresses: list[int]) -> str:
84
+ listByte = b""
85
+ for addr in addresses:
86
+ listByte += addr.to_bytes(4, byteorder="big")
87
+ result: str = str(listByte.decode(encoding="UTF-8")).rstrip("\x00")
88
+ return result
89
+
90
+ def get_code(self, addr: int) -> str:
91
+ code = addr.to_bytes(4, byteorder="big")
92
+ return str(code.decode(encoding="UTF-8"))
93
+
94
+ def get_date(self, addr: int) -> str:
95
+ code = self.get_code(addr)
96
+ if code[0] == "A":
97
+ return "2001..2003 (old)"
98
+ if code[0] == "B":
99
+ return "2003.. (new)"
100
+ return ""
101
+
102
+ def get_language(self, addr: int) -> str:
103
+ code = self.get_code(addr)
104
+ if code[3] == "E":
105
+ return "USA/English"
106
+ if code[3] == "J":
107
+ return "Japan"
108
+ if code[3] == "P":
109
+ return "Europe/Elsewhere"
110
+ if code[3] == "D":
111
+ return "German"
112
+ if code[3] == "F":
113
+ return "French"
114
+ if code[3] == "I":
115
+ return "Italian"
116
+ if code[3] == "S":
117
+ return "Spanish"
118
+ return ""
119
+
120
+ def get_marker_id(self, addr: int) -> str:
121
+ hexa = hex(addr)[2:6]
122
+ n1 = hexa[:2]
123
+ ascii1 = chr((int(n1[0]) ** 1) * 16 + (int(n1[1]) * 16 ** 0))
124
+ n2 = hexa[2:]
125
+ ascii2 = chr((int(n2[0]) ** 1) * 16 + (int(n2[1]) * 16 ** 0))
126
+ return f"{ascii1}{ascii2}"
127
+
128
+ def get_developer(self, addr: int) -> str:
129
+ if self.get_marker_id(addr) == "01":
130
+ return "Nintendo"
131
+ return ""
132
+
133
+ def get_valid_fixed(self, addr: int) -> str:
134
+ return hex(addr)[6:8]
135
+
136
+ def is_valid_fixed(self, addr: int) -> str:
137
+ if self.get_valid_fixed(addr) == "96":
138
+ return "valid"
139
+ return "invalid"
140
+
141
+ def get_unit_code(self, addr: int) -> str:
142
+ return hex(addr)[8:10]
143
+
144
+ def get_device_type(self, addr: int) -> str:
145
+ return "0" + hex(addr)[2:4]
146
+
147
+ def is_valid_reserved_area(self, prev_addr: int, curr_addr: int) -> str:
148
+ if (prev_addr & 0xFFFFFF) == 0 and (curr_addr & 0xFFFFFFFF) == 0:
149
+ return "valid"
150
+ return "invalid"
151
+
152
+ def get_software_version(self, addr: int) -> str:
153
+ hexa = hex(addr)[2:]
154
+
155
+ if len(str(hexa)) <= 6:
156
+ return "00"
157
+ return f"{hexa[0:2]}"
158
+
159
+ def get_rom_sum(self, addr: int) -> str:
160
+ hexa = hex(addr)[2:]
161
+
162
+ if len(str(hexa)) <= 6:
163
+ hexa = "00" + hexa
164
+ return hexa[2:4]
165
+
166
+ def calc_sum(self) -> str:
167
+ chk = 0
168
+
169
+ for i in range(0xA0, 0xBD):
170
+ chk = (chk - self.raw[i]) & 0xFF
171
+ chk = (chk - 0x19) & 0xFF
172
+ return f"{chk:02x}"
173
+
174
+ def display_header(self):
175
+ is_valid_entry = self.is_valid_entry(self.address[self.pc])
176
+ raw_entry = self.to_little_endian(self.address[self.pc])
177
+ op_code_entry = self.get_opcode_branch(self.address[self.pc])
178
+ self.pc += int(BYTE_LEN / BYTE_LEN)
179
+ is_valid_nintendo_logo = self.is_valid_nintendo_logo(self.address[self.pc:self.pc + int(LOGO_BYTE_LEN / BYTE_LEN)])
180
+ self.pc += int(LOGO_BYTE_LEN / BYTE_LEN)
181
+ is_debuging = self.is_debugging(self.address[self.pc - 1])
182
+ title_game = self.get_game_title(self.address[self.pc:self.pc + 3])
183
+ self.pc += int(GAME_TITLE_BYTE_LEN / BYTE_LEN)
184
+ game_code = self.get_code(self.address[self.pc])
185
+ game_release = self.get_date(self.address[self.pc])
186
+ game_language = self.get_language(self.address[self.pc])
187
+ self.pc += int(BYTE_LEN / BYTE_LEN)
188
+ marker_id = self.get_marker_id(self.address[self.pc])
189
+ developer = self.get_developer(self.address[self.pc])
190
+ is_valid_fixed = self.is_valid_fixed(self.address[self.pc])
191
+ valid_fixed = self.get_valid_fixed(self.address[self.pc])
192
+ unit_code = self.get_unit_code(self.address[self.pc])
193
+ self.pc += int(BYTE_LEN / BYTE_LEN)
194
+ device_type = self.get_device_type(self.address[self.pc])
195
+ self.pc += int(BYTE_LEN / BYTE_LEN)
196
+ is_valid_reserved_area = self.is_valid_reserved_area(self.address[self.pc - 1], self.address[self.pc])
197
+ self.pc += int(BYTE_LEN / BYTE_LEN)
198
+ software_version = self.get_software_version(self.address[self.pc])
199
+ rom_sum = self.get_rom_sum(self.address[self.pc])
200
+ sum = self.calc_sum()
201
+ is_valid_sum = rom_sum == sum
202
+
203
+
204
+ click.echo(self.gba_file + ":")
205
+ click.echo("|-- entry")
206
+ click.echo(f"| |-- valid: {is_valid_entry}")
207
+ click.echo(f"| |-- raw: 0x{raw_entry:08x}")
208
+ click.echo(f"| `-- opcode: {op_code_entry}")
209
+ click.echo("|-- nintendo logo:")
210
+ click.echo(f"| |-- status: {is_valid_nintendo_logo}")
211
+ click.echo(f"| `-- debugging: {is_debuging}")
212
+ click.echo(f"|-- game title: {title_game}")
213
+ click.echo("|-- game code:")
214
+ click.echo(f"| |-- code: {game_code}")
215
+ click.echo(f"| |-- date: {game_release}")
216
+ click.echo(f"| `-- language: {game_language}")
217
+ click.echo("|-- marker code:")
218
+ click.echo(f"| |-- id: {marker_id}")
219
+ click.echo(f"| `-- developer: {developer}")
220
+ click.echo(f"|-- fixed value: {is_valid_fixed} ({valid_fixed}h)")
221
+ click.echo(f"|-- unit code: {unit_code}h")
222
+ click.echo(f"|-- device type: {device_type}h")
223
+ click.echo(f"|-- reserved: {is_valid_reserved_area}")
224
+ click.echo(f"|-- software_ver: {software_version}h")
225
+ click.echo("`-- checksum:")
226
+ click.echo(f" |-- valid: {is_valid_sum}")
227
+ click.echo(f" |-- rom: {rom_sum}")
228
+ click.echo(f" `-- our: {sum}")
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: cartridge-sdk
3
+ Version: 1.0.0
4
+ Summary: GBA SDK — build tool for Game Boy Advance
5
+ Author-email: Lukas Soigneux <lukas.soigneux@epitech.eu>
6
+ License-Expression: MIT
7
+ Keywords: GBA,Game Boy,GameBoy Advance,Cartridge
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: click
11
+ Requires-Dist: Pillow
12
+ Dynamic: license-file
13
+
14
+ <div align="center">
15
+ <a href="https://github.com/lukas-sgx/">
16
+ <img src="https://github.com/lukas-sgx/GBA-sdk/blob/main/assets/gba-logo.png?raw=true" alt="Logo" height="180" style="border-radius: 10px">
17
+ </a>
18
+
19
+ <h3 align="center">GameBoy Advance - SDK</h3>
20
+
21
+ <p align="center">
22
+ A Software Development Kit for developers who want to build GameBoy Advance games.
23
+ <br />
24
+ <a href="https://github.com/lukas-sgx/GBA-sdk"><strong>Explore the docs »</strong></a>
25
+ <br />
26
+ <br />
27
+ <a href="https://github.com/lukas-sgx/GBA-sdk">View Demo</a>
28
+ &middot;
29
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
30
+ &middot;
31
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
32
+ </p>
33
+ </div>
34
+
35
+ <details>
36
+ <summary>Table of Contents</summary>
37
+ <ol>
38
+ <li>
39
+ <a href="#about-the-project">About The Project</a>
40
+ <ul>
41
+ <li><a href="#built-with">Built With</a></li>
42
+ </ul>
43
+ </li>
44
+ <li>
45
+ <a href="#getting-started">Getting Started</a>
46
+ <ul>
47
+ <li><a href="#prerequisites">Prerequisites</a></li>
48
+ <li><a href="#installation">Installation</a></li>
49
+ </ul>
50
+ </li>
51
+ <li><a href="#usage">Usage</a></li>
52
+ <li><a href="#roadmap">Roadmap</a></li>
53
+ <li><a href="#contributing">Contributing</a></li>
54
+ <li><a href="#license">License</a></li>
55
+ <li><a href="#contact">Contact</a></li>
56
+ <li><a href="#acknowledgments">Acknowledgments</a></li>
57
+ </ol>
58
+ </details>
59
+
60
+ ## About The Project
61
+
62
+ This SDK aims to simplify GameBoy Advance homebrew development by allowing developers to write game logic in Python. It handles the underlying compilation, asset conversion, and bindings to interface efficiently with the GBA hardware.
63
+
64
+ ### Built With
65
+
66
+ [![Python][Python-shield]][Python-url]
67
+ [![C][C-shield]][C-url]
68
+ [![Assembly][ASM-shield]][ASM-url]
69
+ [![CMake][CMake-shield]][CMake-url]
70
+
71
+ ## Getting Started
72
+
73
+ To get a local copy up and running, follow these simple steps.
74
+
75
+ ### Prerequisites
76
+
77
+ You need Python 3.x installed on your system
78
+
79
+ ### Installation
80
+
81
+ #### Development mode (clone the repo, with local changes)
82
+ 1. Clone the repo
83
+ ```sh
84
+ git clone https://github.com/lukas-sgx/GBA-sdk.git
85
+ cd GBA-sdk
86
+ ```
87
+ 2. Install the SDK in development mode
88
+ ```sh
89
+ pip install -e .
90
+ ```
91
+
92
+ #### Release mode (stable version from PyPI)
93
+ ```sh
94
+ pip install cartridge-sdk
95
+ ```
96
+ ## Usage
97
+
98
+ Here is a quick example of how to dump header of ROM:
99
+ ```sh
100
+ $ cartridge-sdk hdr dump bin/ExampleGBA.gba
101
+ bin/ExampleGBA.gba:
102
+ |-- entry
103
+ | |-- valid: True
104
+ | |-- raw: 0xea00002e
105
+ | `-- opcode: b 0xc0
106
+ |-- nintendo logo:
107
+ | |-- status: True
108
+ | `-- debugging: True
109
+ |-- game title: EXAMPLEGBA
110
+ |-- game code:
111
+ | |-- code: BXXE
112
+ | |-- date: 2003.. (new)
113
+ | `-- language: USA/English
114
+ |-- marker code:
115
+ | |-- id: 01
116
+ | `-- developer: Nintendo
117
+ |-- fixed value: valid (96h)
118
+ |-- unit code: 00h
119
+ |-- device type: 00h
120
+ |-- reserved: valid
121
+ |-- software_ver: 00h
122
+ `-- checksum:
123
+ |-- valid: True
124
+ |-- rom: e3
125
+ `-- our: e3
126
+ ```
127
+
128
+ *For more advanced examples, please refer to the [Documentation](https://github.com/lukas-sgx/GBA-sdk).*
129
+
130
+
131
+ ## Roadmap
132
+
133
+ - [x] Automated header checker `.gba` ROM
134
+ - [x] Automated compilation to `.gba` ROM
135
+ - [x] Font asset pipeline (PNG to C file converter)
136
+ - [ ] Core GBA bindings (Video, Audio, Inputs)
137
+ - [ ] Asset pipeline (PNG to GBA sprite palette converter)
138
+
139
+ See the [open issues](https://github.com/lukas-sgx/GBA-sdk/issues) for a full list of proposed features (and known issues).
140
+
141
+
142
+ ## Contributing
143
+
144
+ Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
145
+
146
+ 1. Fork the Project
147
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
148
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
149
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
150
+ 5. Open a Pull Request
151
+
152
+ ### Top contributors:
153
+
154
+ <a href="https://github.com/lukas-sgx/GBA-sdk/graphs/contributors">
155
+ <img src="https://contrib.rocks/image?repo=lukas-sgx/GBA-sdk" alt="contrib.rocks image" />
156
+ </a>
157
+
158
+ ## License
159
+
160
+ Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
161
+
162
+ ## Contact
163
+
164
+ Lukas Soigneux - lukas.soigneux@epitech.eu
165
+
166
+ ## Acknowledgments
167
+
168
+ * [GBATEK](https://mgba-emu.github.io/gbatek/) - GameBoy Advance Technical Info
169
+ * [Ayyboy-Advance](https://github.com/YannMagnin/ayyboy-advance) - Great emulator for testing
170
+
171
+ [Python-shield]: https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white
172
+ [Python-url]: https://www.python.org/
173
+ [C-shield]: https://img.shields.io/badge/C-005895?style=for-the-badge&logo=c&logoColor=white
174
+ [C-url]: https://www.c-language.org/
175
+ [ASM-shield]: https://img.shields.io/badge/-Assembly-f2921d?style=for-the-badge&logo=assemblyscript&logoColor=white
176
+ [ASM-url]: https://developer.arm.com/
177
+ [CMake-shield]: https://img.shields.io/badge/-CMake-064F8C?style=for-the-badge&logo=cmake&logoColor=white
178
+ [CMake-url]: https://cmake.org/
@@ -0,0 +1,20 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ cartridge/__init__.py
5
+ cartridge/cli.py
6
+ cartridge/modules/build.py
7
+ cartridge/modules/conv.py
8
+ cartridge/modules/init.py
9
+ cartridge/modules/hdr/__init__.py
10
+ cartridge/modules/hdr/dump.py
11
+ cartridge/modules/hdr/gen.py
12
+ cartridge/services/conversion.py
13
+ cartridge/services/generation.py
14
+ cartridge/services/header.py
15
+ cartridge_sdk.egg-info/PKG-INFO
16
+ cartridge_sdk.egg-info/SOURCES.txt
17
+ cartridge_sdk.egg-info/dependency_links.txt
18
+ cartridge_sdk.egg-info/entry_points.txt
19
+ cartridge_sdk.egg-info/requires.txt
20
+ cartridge_sdk.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cartridge-sdk = cartridge.cli:main
@@ -0,0 +1,2 @@
1
+ click
2
+ Pillow
@@ -0,0 +1 @@
1
+ cartridge
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cartridge-sdk"
7
+ description = "GBA SDK — build tool for Game Boy Advance"
8
+
9
+ version = "1.0.0"
10
+ dependencies = [
11
+ "click",
12
+ "Pillow",
13
+ ]
14
+
15
+ authors = [
16
+ {name = "Lukas Soigneux", email = "lukas.soigneux@epitech.eu"},
17
+ ]
18
+
19
+ readme = "README.md"
20
+ license = "MIT"
21
+ license-files = ["LICENSE"]
22
+ keywords = ["GBA", "Game Boy", "GameBoy Advance", "Cartridge"]
23
+
24
+ [tool.setuptools.packages.find]
25
+ include = ["cartridge*"]
26
+
27
+ [project.scripts]
28
+ cartridge-sdk = "cartridge.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+