Skip to content

Commit 40b1cae

Browse files
committed
proper cartridge generation
- Pad to 128kb. snes9x header check reports corruption for less than 1 mbit (size < 7) - setup.py - skip cc65 installation if done already - quiet sneschk.bat
1 parent 66b4ad3 commit 40b1cae

File tree

9 files changed

+72
-29
lines changed

9 files changed

+72
-29
lines changed

.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
root = true
4+
5+
[*]
6+
indent_style = space
7+
indent_size = 3
8+
end_of_line = lf
9+
charset = utf-8
10+
11+
[*.asm]
12+
indent_style = tab
13+
indent_size = 8

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
After checking out the repo, run setup.py to install tools.
1111

12-
Recommended to add SNESKIT to your environment pointing to the SNESKIT folder.
12+
SNESKIT should be added to your environment pointing to the sneskit folder.
1313

1414
## Tool installations:
1515

@@ -75,5 +75,8 @@ This is a graphics converter for SNES which is a modified version of the "GBA Ra
7575
Transmogrifier" by Cearn, which is distributed under the GPL. Please contact me
7676
([email protected]) if you would like to obtain the [modified] source code.
7777

78+
## Contributions welcome!
79+
Check the issues page for things to do.
80+
7881
## IRC
7982
Join #snesdev on EFNet!

setup.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
#!/usr/bin/env python3
22
#***********************************************************
33
# Configure environment
4-
import venv, os, shutil
4+
import venv, os, shutil, glob
55

6+
print("Creating venv.")
67
venv.create(".venv", with_pip=True)
8+
9+
print("Activating venv.")
710
if os.name == "nt":
811
os.system(".venv\\Scripts\\activate")
912
elif os.name == "posix":
1013
os.system("source .venv/bin/activate")
1114

15+
print("Installing Python requirements.")
1216
os.system("pip install -r requirements.txt")
17+
18+
print("Okay, ready for work.")
1319
#***********************************************************
1420

1521
import os, requests, urllib.request, zipfile
@@ -23,6 +29,11 @@ def run(command):
2329

2430
#-----------------------------------------------------------------------------------------
2531
def install_cc65():
32+
print("Installing cc65.")
33+
if glob.glob("cc65/*") != []:
34+
print("*** cc65 has files in it. Skipping installation.")
35+
return
36+
2637
if os.name == 'nt':
2738
# Windows
2839
# Download from sourceforge
@@ -41,13 +52,15 @@ def install_cc65():
4152

4253
#-----------------------------------------------------------------------------------------
4354
def install_tools():
55+
print("Installing tools.")
4456
if os.name == "posix":
57+
# Linux calls the script directly.
4558
shutil.copy("tool-src/sneschk/sneschk.py", "tools/sneschk")
4659
os.chmod("tools/sneschk", 0o755)
4760
else:
48-
shutil.copy("tool-src/sneschk/sneschk.py", "tools/sneschk.py")
4961
# Windows will use the corresponding .bat file to call the py file.
62+
shutil.copy("tool-src/sneschk/sneschk.py", "tools/sneschk.py")
5063

5164
#-----------------------------------------------------------------------------------------
5265
install_cc65()
53-
install_tools()
66+
install_tools()

templates/snes-project/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build

templates/snes-project/Makefile

+6-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ ifeq ($(strip $(SNESKIT)),)
1010
$(error SNESKIT not found, please add SNESKIT=<path to sneskit> to your environment)
1111
endif
1212

13-
# Might have to prefix .py tools with "py" on Windows Python3 installations.
14-
1513
SNESTOOLS := $(SNESKIT)/tools
1614
SNESGRIT := $(SNESTOOLS)/snesgrit
1715
SNESCHK := $(SNESTOOLS)/sneschk
@@ -36,7 +34,7 @@ SOURCES := source
3634
INCLUDES := include
3735
GRAPHICS := graphics
3836
AUDIO := audio
39-
MAPMODE := hirom
37+
MAPMODE := lorom
4038
MAPFILE := YES
4139

4240
#-----------------------------------------------------------------------------------------
@@ -49,10 +47,14 @@ ASFLAGS := --cpu 65816 -s
4947
# For snesmod (if used), we convert to a soundbank target.
5048
SOUNDBANK := $(BUILD)/soundbank
5149
SMCONVFLAGS := --soundbank --output $(SOUNDBANK)
50+
SNESCHKFLAGS := --fix
5251

5352
ifeq ($(MAPMODE),hirom)
5453
ASFLAGS += -D HIROM
5554
SMCONVFLAGS += --hirom
55+
SNESCHKFLAGS += -m hirom
56+
else
57+
SNESCHKFLAGS += -m lorom
5658
endif
5759

5860
# Include paths.
@@ -134,6 +136,6 @@ else
134136
endif
135137

136138
@echo "Running sneschk (checksums and padding)"
137-
@$(SNESCHK) $@
139+
$(SNESCHK) $(SNESCHKFLAGS) $@
138140

139141
@echo Okay! :: $(notdir $@)

templates/snes-project/config.ld

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@ MEMORY {
2929

3030
#-------------------------------------------------------------------------
3131
# Insert ROM sections for program data.
32+
# Note that some tools will say your cart is corrupt if it's smaller than
33+
# 1 mbit, hence 3 ROM sections here by default.
3234
ROM1: start = $818000, size = $8000, fill = yes;
33-
# ROM2: start = $828000, size = $8000, fill = yes;
35+
ROM2: start = $820000, size = $8000, fill = yes;
36+
ROM3: start = $828000, size = $8000, fill = yes;
3437

3538
# For HiROM, use these instead of the above (size 64k, bank $C0+)
3639
# ROM1: start = $C18000, size = $10000, fill = yes;

tool-src/sneschk/sneschk.py

+20-12
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,22 @@ def compute_romsize(filesize):
2626
return size_header, actualsize
2727

2828
#-----------------------------------------------------------------------------------------
29-
def calc_checksum(file):
29+
def calc_checksum(file, header_start):
3030
file.seek(0, os.SEEK_SET)
31+
checksum_addr = header_start + 0xDC
3132

32-
checksum = 0xFF * 2 # Default checksum bytes at FFDC and FFDD
33+
checksum = 0xFF * 2 # Default checksum bytes at 7FDC and 7FDD
3334
address = 0
3435
while True:
3536
c = file.read(4096)
3637
if c == b'':
3738
break
3839

3940
checksum += sum(c)
40-
relative_ffdc = 0xFFDC - address
41+
relative_ffdc = checksum_addr - address
4142
if relative_ffdc >= 0 and relative_ffdc < len(c):
4243
# This block contains the checksum bytes. Exclude them from the result.
43-
checksum -= sum(c[0xFFDC - address:0xFFDC - address + 4])
44+
checksum -= sum(c[checksum_addr - address:checksum_addr - address + 4])
4445

4546
address += len(c)
4647

@@ -67,9 +68,9 @@ def get_file_byte(file, address):
6768
return file.read(1)[0]
6869

6970
#-----------------------------------------------------------------------------------------
70-
def validate_sfc(file):
71+
def validate_sfc(file, minsize):
7172
file.seek(0, os.SEEK_END)
72-
if file.tell() < 0x10000:
73+
if file.tell() < minsize: # Double on hirom.
7374
return "file is too small to contain a valid cartridge header."
7475

7576
# Kind of hard to find any special signature to verify.
@@ -96,6 +97,7 @@ def main(args=None, output_file=None):
9697
""")
9798

9899
parser.add_argument('path', help='Path to SFC or SMC file. Must be headerless.')
100+
parser.add_argument("-m", "--mode", required=True, choices=['lorom', 'hirom'], help='Cartridge mode')
99101
parser.add_argument("--fix", action="store_true", help='Make changes to the given file. Otherwise, just print info about it.')
100102
parser.add_argument("-q", "--quiet", action='store_true', help='Suppress output.')
101103
args = parser.parse_args(args=args)
@@ -104,38 +106,44 @@ def main(args=None, output_file=None):
104106
if args.fix:
105107
mode = "r+b"
106108

109+
header_start = 0x7F00
110+
min_size = 0x8000
111+
if args.mode == "hirom":
112+
header_start = 0xFF00
113+
min_size = 0x10000
114+
107115
with open(args.path, mode) as f:
108116
f.seek(0, os.SEEK_END)
109117
mprint(f"File size: {f.tell()} bytes")
110118

111-
validation_error = validate_sfc(f)
119+
validation_error = validate_sfc(f, min_size)
112120
if validation_error:
113121
mprint(f"Validation error: {validation_error}")
114122
mprint("Does this file have an SMC header? Those are not supported.")
115123
return
116124

117125
size_header, full_size = compute_romsize(f.tell())
118126
mprint(f"Computed size header value: {size_header}")
119-
f.seek(0xFFD7, os.SEEK_SET)
127+
f.seek(header_start + 0xD7, os.SEEK_SET)
120128
mprint(f"Current size header value: {f.read(1)[0]}")
121129
mprint(f"Full size: {full_size//1024} KiB")
122130

123131
if args.fix:
124132
pad_file(f, full_size)
125-
f.seek(0xFFD7, os.SEEK_SET)
133+
f.seek(header_start + 0xD7, os.SEEK_SET)
126134
f.write(bytes([size_header]))
127135

128-
f.seek(0xFFDC, os.SEEK_SET)
136+
f.seek(header_start + 0xDC, os.SEEK_SET)
129137
current_checksum_bytes = f.read(4)
130138
mprint(f"Current checksum bytes: {format_checksum_bytes(current_checksum_bytes)}")
131139

132-
checksum = calc_checksum(f)
140+
checksum = calc_checksum(f, header_start)
133141
xchecksum = checksum ^ 0xFFFF
134142
checksum_bytes = bytes([xchecksum & 0xFF, xchecksum >> 8, checksum & 0xFF, checksum >> 8])
135143
mprint(f"Computed checksum bytes: {format_checksum_bytes(checksum_bytes)}")
136144

137145
if args.fix:
138-
f.seek(0xFFDC, os.SEEK_SET)
146+
f.seek(header_start + 0xDC, os.SEEK_SET)
139147
f.write(checksum_bytes)
140148

141149
if __name__ == "__main__":

tool-src/sneschk/sneschk_test.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ def create_sfc_tmp(self, size):
3232
# output which may be an arbitrary file size.
3333
def test_padding(self):
3434
with self.create_sfc_tmp(80000) as f: pass
35-
output = self.call_sneschk([SFC_TMP_FILE])
35+
output = self.call_sneschk([SFC_TMP_FILE, "-m", "hirom"])
3636
self.assertIn("File size: 80000", output)
3737
self.assertIn("Computed size header value: 7", output)
3838
self.assertIn("Current size header value: 0", output)
3939
self.assertIn("Full size: 128 KiB", output)
4040
self.assertNotIn("bytes of zero-padding", output)
4141
self.assertEqual(os.path.getsize(SFC_TMP_FILE), 80000)
4242

43-
output = self.call_sneschk([SFC_TMP_FILE, "--fix"])
43+
output = self.call_sneschk([SFC_TMP_FILE, "--fix", "-m", "hirom"])
4444
self.assertIn("File size: 80000", output)
4545
self.assertIn(f"Writing {131072 - 80000} bytes of zero-padding", output)
4646
self.assertEqual(os.path.getsize(SFC_TMP_FILE), 131072)
@@ -57,15 +57,15 @@ def test_padding(self):
5757
# It's tricky to have a real validation.
5858
def test_validation(self):
5959
with self.create_sfc_tmp(0) as f: pass
60-
output = self.call_sneschk([SFC_TMP_FILE])
60+
output = self.call_sneschk([SFC_TMP_FILE, "-m", "hirom"])
6161
self.assertIn("file is too small to contain a valid cartridge header", output)
6262

6363
with self.create_sfc_tmp(65535) as f: pass
64-
output = self.call_sneschk([SFC_TMP_FILE])
64+
output = self.call_sneschk([SFC_TMP_FILE, "-m", "hirom"])
6565
self.assertIn("file is too small to contain a valid cartridge header", output)
6666

6767
with self.create_sfc_tmp(65536) as f: pass
68-
output = self.call_sneschk([SFC_TMP_FILE])
68+
output = self.call_sneschk([SFC_TMP_FILE, "-m", "hirom"])
6969
self.assertIn("Current checksum bytes: 00 00 00 00", output)
7070

7171
os.remove(SFC_TMP_FILE)
@@ -93,11 +93,11 @@ def test_checksum(self):
9393
ccs = cs ^ 0xFFFF
9494
csformatted = self.format_byte_string([ccs & 0xFF, ccs >> 8, cs & 0xFF, cs >> 8])
9595

96-
output = self.call_sneschk([SFC_TMP_FILE])
96+
output = self.call_sneschk([SFC_TMP_FILE, "-m", "hirom"])
9797
self.assertIn("Current checksum bytes: 11 11 11 11", output)
9898
self.assertIn(f"Computed checksum bytes: {csformatted}", output)
9999

100-
output = self.call_sneschk([SFC_TMP_FILE, "--fix"])
100+
output = self.call_sneschk([SFC_TMP_FILE, "--fix", "-m", "hirom"])
101101

102102
# The extra +7 is the ROM size header byte set by --fix
103103
cs = (1 + 2 + 3 + 4 + 0xFF * 266 + 0xFF*2 + 7 + 10 * 16384) & 0xFFFF

tools/sneschk.bat

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
@REM Windows should pick this up with the raw name "sneschk" without the extension.
22
@REM Forward to the Python script with the Windows py launcher.
3-
py %SNESKIT%/tools/sneschk.py %*
3+
@py %SNESKIT%/tools/sneschk.py %*

0 commit comments

Comments
 (0)