cartridge-sdk 1.0.0b3__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,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: cartridge-sdk
3
+ Version: 1.0.0b3
4
+ Summary: GBA SDK — build tool for Game Boy Advance
5
+ Author: Alessandro Paris
6
+ Author-email: Lukas Soigneux <lukas.soigneux@epitech.eu>
7
+ License-Expression: MIT
8
+ Keywords: GBA,Game Boy,GameBoy Advance
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: click
12
+ Requires-Dist: Pillow
13
+ Dynamic: license-file
14
+
15
+ <div align="center">
16
+ <a href="https://github.com/lukas-sgx/">
17
+ <img src="assets/gba-logo.png" alt="Logo" height="180" style="border-radius: 10px">
18
+ </a>
19
+
20
+ <h3 align="center">GameBoy Advance - SDK</h3>
21
+
22
+ <p align="center">
23
+ A Software Development Kit for developers who want to build GameBoy Advance games.
24
+ <br />
25
+ <a href="https://github.com/lukas-sgx/GBA-sdk"><strong>Explore the docs »</strong></a>
26
+ <br />
27
+ <br />
28
+ <a href="https://github.com/lukas-sgx/GBA-sdk">View Demo</a>
29
+ &middot;
30
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
31
+ &middot;
32
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
33
+ </p>
34
+ </div>
35
+
36
+ <details>
37
+ <summary>Table of Contents</summary>
38
+ <ol>
39
+ <li>
40
+ <a href="#about-the-project">About The Project</a>
41
+ <ul>
42
+ <li><a href="#built-with">Built With</a></li>
43
+ </ul>
44
+ </li>
45
+ <li>
46
+ <a href="#getting-started">Getting Started</a>
47
+ <ul>
48
+ <li><a href="#prerequisites">Prerequisites</a></li>
49
+ <li><a href="#installation">Installation</a></li>
50
+ </ul>
51
+ </li>
52
+ <li><a href="#usage">Usage</a></li>
53
+ <li><a href="#roadmap">Roadmap</a></li>
54
+ <li><a href="#contributing">Contributing</a></li>
55
+ <li><a href="#license">License</a></li>
56
+ <li><a href="#contact">Contact</a></li>
57
+ <li><a href="#acknowledgments">Acknowledgments</a></li>
58
+ </ol>
59
+ </details>
60
+
61
+ ## About The Project
62
+
63
+ 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.
64
+
65
+ ### Built With
66
+
67
+ [![Python][Python-shield]][Python-url]
68
+ [![C][C-shield]][C-url]
69
+ [![Assembly][ASM-shield]][ASM-url]
70
+ [![CMake][CMake-shield]][CMake-url]
71
+
72
+ ## Getting Started
73
+
74
+ To get a local copy up and running, follow these simple steps.
75
+
76
+ ### Prerequisites
77
+
78
+ You need Python 3.x installed on your system
79
+
80
+ ### Installation
81
+
82
+ 1. Clone the repo
83
+
84
+ ```sh
85
+ git clone https://github.com/lukas-sgx/GBA-sdk.git
86
+ ```
87
+
88
+ 2. Install the SDK in development mode
89
+
90
+ ```sh
91
+ pip install -e .
92
+ ```
93
+
94
+ ## Usage
95
+
96
+ Here is a quick example of how to dump header of ROM:
97
+ ```sh
98
+ $ cartridge-sdk hdr dump bin/ExampleGBA.gba
99
+ bin/ExampleGBA.gba:
100
+ |-- entry
101
+ | |-- valid: True
102
+ | |-- raw: 0xea00002e
103
+ | `-- opcode: b 0xc0
104
+ |-- nintendo logo:
105
+ | |-- status: True
106
+ | `-- debugging: True
107
+ |-- game title: EXAMPLEGBA
108
+ |-- game code:
109
+ | |-- code: BXXE
110
+ | |-- date: 2003.. (new)
111
+ | `-- language: USA/English
112
+ |-- marker code:
113
+ | |-- id: 01
114
+ | `-- developer: Nintendo
115
+ |-- fixed value: valid (96h)
116
+ |-- unit code: 00h
117
+ |-- device type: 00h
118
+ |-- reserved: valid
119
+ |-- software_ver: 00h
120
+ `-- checksum:
121
+ |-- valid: True
122
+ |-- rom: e3
123
+ `-- our: e3
124
+ ```
125
+
126
+ *For more advanced examples, please refer to the [Documentation](https://github.com/lukas-sgx/GBA-sdk).*
127
+
128
+
129
+ ## Roadmap
130
+
131
+ - [x] Automated header checker `.gba` ROM
132
+ - [x] Automated compilation to `.gba` ROM
133
+ - [x] Font asset pipeline (PNG to C file converter)
134
+ - [ ] Core GBA bindings (Video, Audio, Inputs)
135
+ - [ ] Asset pipeline (PNG to GBA sprite palette converter)
136
+
137
+ See the [open issues](https://github.com/lukas-sgx/GBA-sdk/issues) for a full list of proposed features (and known issues).
138
+
139
+
140
+ ## Contributing
141
+
142
+ Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
143
+
144
+ 1. Fork the Project
145
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
146
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
147
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
148
+ 5. Open a Pull Request
149
+
150
+ ### Top contributors:
151
+
152
+ <a href="https://github.com/lukas-sgx/GBA-sdk/graphs/contributors">
153
+ <img src="https://contrib.rocks/image?repo=lukas-sgx/GBA-sdk" alt="contrib.rocks image" />
154
+ </a>
155
+
156
+ ## License
157
+
158
+ Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
159
+
160
+ ## Contact
161
+
162
+ Lukas Soigneux - lukas.soigneux@epitech.eu
163
+
164
+ ## Acknowledgments
165
+
166
+ * [GBATEK](https://mgba-emu.github.io/gbatek/) - GameBoy Advance Technical Info
167
+ * [Ayyboy-Advance](https://github.com/YannMagnin/ayyboy-advance) - Great emulator for testing
168
+
169
+ [Python-shield]: https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white
170
+ [Python-url]: https://www.python.org/
171
+ [C-shield]: https://img.shields.io/badge/C-005895?style=for-the-badge&logo=c&logoColor=white
172
+ [C-url]: https://www.c-language.org/
173
+ [ASM-shield]: https://img.shields.io/badge/-Assembly-f2921d?style=for-the-badge&logo=assemblyscript&logoColor=white
174
+ [ASM-url]: https://developer.arm.com/
175
+ [CMake-shield]: https://img.shields.io/badge/-CMake-064F8C?style=for-the-badge&logo=cmake&logoColor=white
176
+ [CMake-url]: https://cmake.org/
@@ -0,0 +1,162 @@
1
+ <div align="center">
2
+ <a href="https://github.com/lukas-sgx/">
3
+ <img src="assets/gba-logo.png" 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
+ 1. Clone the repo
69
+
70
+ ```sh
71
+ git clone https://github.com/lukas-sgx/GBA-sdk.git
72
+ ```
73
+
74
+ 2. Install the SDK in development mode
75
+
76
+ ```sh
77
+ pip install -e .
78
+ ```
79
+
80
+ ## Usage
81
+
82
+ Here is a quick example of how to dump header of ROM:
83
+ ```sh
84
+ $ cartridge-sdk hdr dump bin/ExampleGBA.gba
85
+ bin/ExampleGBA.gba:
86
+ |-- entry
87
+ | |-- valid: True
88
+ | |-- raw: 0xea00002e
89
+ | `-- opcode: b 0xc0
90
+ |-- nintendo logo:
91
+ | |-- status: True
92
+ | `-- debugging: True
93
+ |-- game title: EXAMPLEGBA
94
+ |-- game code:
95
+ | |-- code: BXXE
96
+ | |-- date: 2003.. (new)
97
+ | `-- language: USA/English
98
+ |-- marker code:
99
+ | |-- id: 01
100
+ | `-- developer: Nintendo
101
+ |-- fixed value: valid (96h)
102
+ |-- unit code: 00h
103
+ |-- device type: 00h
104
+ |-- reserved: valid
105
+ |-- software_ver: 00h
106
+ `-- checksum:
107
+ |-- valid: True
108
+ |-- rom: e3
109
+ `-- our: e3
110
+ ```
111
+
112
+ *For more advanced examples, please refer to the [Documentation](https://github.com/lukas-sgx/GBA-sdk).*
113
+
114
+
115
+ ## Roadmap
116
+
117
+ - [x] Automated header checker `.gba` ROM
118
+ - [x] Automated compilation to `.gba` ROM
119
+ - [x] Font asset pipeline (PNG to C file converter)
120
+ - [ ] Core GBA bindings (Video, Audio, Inputs)
121
+ - [ ] Asset pipeline (PNG to GBA sprite palette converter)
122
+
123
+ See the [open issues](https://github.com/lukas-sgx/GBA-sdk/issues) for a full list of proposed features (and known issues).
124
+
125
+
126
+ ## Contributing
127
+
128
+ Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
129
+
130
+ 1. Fork the Project
131
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
132
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
133
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
134
+ 5. Open a Pull Request
135
+
136
+ ### Top contributors:
137
+
138
+ <a href="https://github.com/lukas-sgx/GBA-sdk/graphs/contributors">
139
+ <img src="https://contrib.rocks/image?repo=lukas-sgx/GBA-sdk" alt="contrib.rocks image" />
140
+ </a>
141
+
142
+ ## License
143
+
144
+ Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
145
+
146
+ ## Contact
147
+
148
+ Lukas Soigneux - lukas.soigneux@epitech.eu
149
+
150
+ ## Acknowledgments
151
+
152
+ * [GBATEK](https://mgba-emu.github.io/gbatek/) - GameBoy Advance Technical Info
153
+ * [Ayyboy-Advance](https://github.com/YannMagnin/ayyboy-advance) - Great emulator for testing
154
+
155
+ [Python-shield]: https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white
156
+ [Python-url]: https://www.python.org/
157
+ [C-shield]: https://img.shields.io/badge/C-005895?style=for-the-badge&logo=c&logoColor=white
158
+ [C-url]: https://www.c-language.org/
159
+ [ASM-shield]: https://img.shields.io/badge/-Assembly-f2921d?style=for-the-badge&logo=assemblyscript&logoColor=white
160
+ [ASM-url]: https://developer.arm.com/
161
+ [CMake-shield]: https://img.shields.io/badge/-CMake-064F8C?style=for-the-badge&logo=cmake&logoColor=white
162
+ [CMake-url]: https://cmake.org/
@@ -0,0 +1,2 @@
1
+ __version__ = "1.0.0-alpha.2"
2
+ __author__ = "lukas-sgx"
@@ -0,0 +1,15 @@
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
+
6
+ @click.group(help="Cartridge SDK CLI entry")
7
+ def cli():
8
+ pass
9
+
10
+ cli.add_command(build)
11
+ cli.add_command(hdr)
12
+ cli.add_command(conv)
13
+
14
+ def main():
15
+ 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="set CMake dir")
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 a font asset into a raw C file")
6
+ @click.option("-o", "--output", required=True, type=click.Path(exists=False), help="generated C file")
7
+ @click.option("-w", "--width", required=True, type=click.INT, help="width size of char in pixel")
8
+ @click.option("-h", "--height", required=True, type=click.INT, help="height size of char in pixel")
9
+ @click.option("-m", "--margin", required=True, type=click.INT, help="margin between of char in pixel")
10
+ @click.option("-l", "--line-height", required=True, type=click.INT, help="virtual alignement line height in pixel")
11
+ @click.option("-d", "--debug", is_flag=True, help="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="Cartridge header interface")
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 / display 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
+
4
+ @click.command(help="gen / generate cartridge ROM")
5
+ @click.option("-b", "--binary", required=True, nargs=1, type=click.File("rb"), help="input binary file")
6
+ @click.option("-o", "--output", required=True, nargs=1, type=click.File("wb"), help="output filename")
7
+ @click.option("-n", "--name", nargs=1, type=click.STRING, help="internal cartridge name", show_default=True, default="filename")
8
+ def gen(binary: click.File, output: click.File, name: str):
9
+ Generation(binary.name, output, name, "BXXE")
10
+
@@ -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,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: cartridge-sdk
3
+ Version: 1.0.0b3
4
+ Summary: GBA SDK — build tool for Game Boy Advance
5
+ Author: Alessandro Paris
6
+ Author-email: Lukas Soigneux <lukas.soigneux@epitech.eu>
7
+ License-Expression: MIT
8
+ Keywords: GBA,Game Boy,GameBoy Advance
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: click
12
+ Requires-Dist: Pillow
13
+ Dynamic: license-file
14
+
15
+ <div align="center">
16
+ <a href="https://github.com/lukas-sgx/">
17
+ <img src="assets/gba-logo.png" alt="Logo" height="180" style="border-radius: 10px">
18
+ </a>
19
+
20
+ <h3 align="center">GameBoy Advance - SDK</h3>
21
+
22
+ <p align="center">
23
+ A Software Development Kit for developers who want to build GameBoy Advance games.
24
+ <br />
25
+ <a href="https://github.com/lukas-sgx/GBA-sdk"><strong>Explore the docs »</strong></a>
26
+ <br />
27
+ <br />
28
+ <a href="https://github.com/lukas-sgx/GBA-sdk">View Demo</a>
29
+ &middot;
30
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=bug&template=bug-report---.md">Report Bug</a>
31
+ &middot;
32
+ <a href="https://github.com/lukas-sgx/GBA-sdk/issues/new?labels=enhancement&template=feature-request---.md">Request Feature</a>
33
+ </p>
34
+ </div>
35
+
36
+ <details>
37
+ <summary>Table of Contents</summary>
38
+ <ol>
39
+ <li>
40
+ <a href="#about-the-project">About The Project</a>
41
+ <ul>
42
+ <li><a href="#built-with">Built With</a></li>
43
+ </ul>
44
+ </li>
45
+ <li>
46
+ <a href="#getting-started">Getting Started</a>
47
+ <ul>
48
+ <li><a href="#prerequisites">Prerequisites</a></li>
49
+ <li><a href="#installation">Installation</a></li>
50
+ </ul>
51
+ </li>
52
+ <li><a href="#usage">Usage</a></li>
53
+ <li><a href="#roadmap">Roadmap</a></li>
54
+ <li><a href="#contributing">Contributing</a></li>
55
+ <li><a href="#license">License</a></li>
56
+ <li><a href="#contact">Contact</a></li>
57
+ <li><a href="#acknowledgments">Acknowledgments</a></li>
58
+ </ol>
59
+ </details>
60
+
61
+ ## About The Project
62
+
63
+ 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.
64
+
65
+ ### Built With
66
+
67
+ [![Python][Python-shield]][Python-url]
68
+ [![C][C-shield]][C-url]
69
+ [![Assembly][ASM-shield]][ASM-url]
70
+ [![CMake][CMake-shield]][CMake-url]
71
+
72
+ ## Getting Started
73
+
74
+ To get a local copy up and running, follow these simple steps.
75
+
76
+ ### Prerequisites
77
+
78
+ You need Python 3.x installed on your system
79
+
80
+ ### Installation
81
+
82
+ 1. Clone the repo
83
+
84
+ ```sh
85
+ git clone https://github.com/lukas-sgx/GBA-sdk.git
86
+ ```
87
+
88
+ 2. Install the SDK in development mode
89
+
90
+ ```sh
91
+ pip install -e .
92
+ ```
93
+
94
+ ## Usage
95
+
96
+ Here is a quick example of how to dump header of ROM:
97
+ ```sh
98
+ $ cartridge-sdk hdr dump bin/ExampleGBA.gba
99
+ bin/ExampleGBA.gba:
100
+ |-- entry
101
+ | |-- valid: True
102
+ | |-- raw: 0xea00002e
103
+ | `-- opcode: b 0xc0
104
+ |-- nintendo logo:
105
+ | |-- status: True
106
+ | `-- debugging: True
107
+ |-- game title: EXAMPLEGBA
108
+ |-- game code:
109
+ | |-- code: BXXE
110
+ | |-- date: 2003.. (new)
111
+ | `-- language: USA/English
112
+ |-- marker code:
113
+ | |-- id: 01
114
+ | `-- developer: Nintendo
115
+ |-- fixed value: valid (96h)
116
+ |-- unit code: 00h
117
+ |-- device type: 00h
118
+ |-- reserved: valid
119
+ |-- software_ver: 00h
120
+ `-- checksum:
121
+ |-- valid: True
122
+ |-- rom: e3
123
+ `-- our: e3
124
+ ```
125
+
126
+ *For more advanced examples, please refer to the [Documentation](https://github.com/lukas-sgx/GBA-sdk).*
127
+
128
+
129
+ ## Roadmap
130
+
131
+ - [x] Automated header checker `.gba` ROM
132
+ - [x] Automated compilation to `.gba` ROM
133
+ - [x] Font asset pipeline (PNG to C file converter)
134
+ - [ ] Core GBA bindings (Video, Audio, Inputs)
135
+ - [ ] Asset pipeline (PNG to GBA sprite palette converter)
136
+
137
+ See the [open issues](https://github.com/lukas-sgx/GBA-sdk/issues) for a full list of proposed features (and known issues).
138
+
139
+
140
+ ## Contributing
141
+
142
+ Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
143
+
144
+ 1. Fork the Project
145
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
146
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
147
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
148
+ 5. Open a Pull Request
149
+
150
+ ### Top contributors:
151
+
152
+ <a href="https://github.com/lukas-sgx/GBA-sdk/graphs/contributors">
153
+ <img src="https://contrib.rocks/image?repo=lukas-sgx/GBA-sdk" alt="contrib.rocks image" />
154
+ </a>
155
+
156
+ ## License
157
+
158
+ Distributed under the MIT License. See [LICENSE](./LICENSE) for more information.
159
+
160
+ ## Contact
161
+
162
+ Lukas Soigneux - lukas.soigneux@epitech.eu
163
+
164
+ ## Acknowledgments
165
+
166
+ * [GBATEK](https://mgba-emu.github.io/gbatek/) - GameBoy Advance Technical Info
167
+ * [Ayyboy-Advance](https://github.com/YannMagnin/ayyboy-advance) - Great emulator for testing
168
+
169
+ [Python-shield]: https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white
170
+ [Python-url]: https://www.python.org/
171
+ [C-shield]: https://img.shields.io/badge/C-005895?style=for-the-badge&logo=c&logoColor=white
172
+ [C-url]: https://www.c-language.org/
173
+ [ASM-shield]: https://img.shields.io/badge/-Assembly-f2921d?style=for-the-badge&logo=assemblyscript&logoColor=white
174
+ [ASM-url]: https://developer.arm.com/
175
+ [CMake-shield]: https://img.shields.io/badge/-CMake-064F8C?style=for-the-badge&logo=cmake&logoColor=white
176
+ [CMake-url]: https://cmake.org/
@@ -0,0 +1,19 @@
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/hdr/__init__.py
9
+ cartridge/modules/hdr/dump.py
10
+ cartridge/modules/hdr/gen.py
11
+ cartridge/services/conversion.py
12
+ cartridge/services/generation.py
13
+ cartridge/services/header.py
14
+ cartridge_sdk.egg-info/PKG-INFO
15
+ cartridge_sdk.egg-info/SOURCES.txt
16
+ cartridge_sdk.egg-info/dependency_links.txt
17
+ cartridge_sdk.egg-info/entry_points.txt
18
+ cartridge_sdk.egg-info/requires.txt
19
+ 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,29 @@
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-beta.3"
10
+ dependencies = [
11
+ "click",
12
+ "Pillow"
13
+ ]
14
+
15
+ authors = [
16
+ {name = "Lukas Soigneux", email = "lukas.soigneux@epitech.eu"},
17
+ {name = "Alessandro Paris"},
18
+ ]
19
+
20
+ readme = "README.md"
21
+ license = "MIT"
22
+ license-files = ["LICENSE"]
23
+ keywords = ["GBA", "Game Boy", "GameBoy Advance"]
24
+
25
+ [tool.setuptools.packages.find]
26
+ include = ["cartridge*"]
27
+
28
+ [project.scripts]
29
+ cartridge-sdk = "cartridge.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+