Don't know if it's of interest to anyone, but I've been playing around with extracting the puzzle data from the Picross 3D ROM. Why? I don't know. I'm kind of interested in figuring out how they generate the clues for the puzzles when you create them. I thought it'd be nice to have the set of puzzles in a format my own programs could use. Maybe even make a Windows or Flash clone or variant.
Anyway, I've figured out that the puzzles are spread in groups throughout the ROM. They have the following format (I'll be using the "Blackboard" puzzle as an example):
0000 to 000F: Don't know. Blackboard has this: 20 04 05 1E 1E 03 C7 08 00 10 F0 4B 08 F6 01 0F. They always start with 20, and always have 00 10 F0 4B in those four bytes near the end. I'm thinking this might be some kind of color palette.
0010 to 0027: Puzzle ID name, padded with leading spaces. Blackboard has " 037_BLACKBOARD". It's always a 3 digit number, underscore, and all-caps name.
0028 to 0097: Don't know. Blackboard has: 00 00 00 00 00 00 00 00 71 05 C8 00 A0 01 C0 00 58 6B AC 35 10 42 08 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.
0098 to 0187: Descriptive name of the puzzle, in two-byte Unicode, in five languages (48 bytes each, padded with trailing zeros). English is first. Blackboard has "Blackboard".
0188 to 056F: The actual puzzle data. 1000 bytes, which covers the maximum 10x10x10 size of a puzzle. It appears to go up in columns, starting at the bottom of the puzzle and going up, then going to the next column, etc., until a layer is filled, then repeating for all 10 layers. It appears to fill completely empty columns with zero, but empty spaces in partially-filled columns with 0x20. The non-empty spaces contain 0x30, 0x31, 0x32, etc., which are color indexes (I can see by looking at a puzzle, for example, that everywhere it has a 0x30 is the same color, and all 0x31s are their own color, as are 0x32s, etc.).
0570 to 05FF: Don't know. Blackboard has: 02 00 02 00 00 00 02 00 02 00 02 00 00 00 00 00 00 00 00 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 00 00 00 00 00 00 3D 00 3D 00 3D 00 3D 00 3E 00 3E 00 3D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.
Each puzzle uses a 0x0600 size chunk, and they always start on page boundaries that are multiples of 0x0200. They're packed together in small groups, but the groups are spread throughout the ROM.
I've written a Python script to find all the puzzle chunks, and write them out to XML.
CODEimport xml.dom.minidom
def hex_string(inStr):
ÂÂÂÂoutStr = ""
ÂÂÂÂfor byte in inStr:
ÂÂÂÂÂÂÂÂoutStr += '%02X' % ord(byte) + ' '
ÂÂÂÂoutStr = outStr[:-1]
ÂÂÂÂreturn outStr
ÂÂÂÂ
romfile = open("4926 - Picross 3D (USA).nds", "rb")
romdata = romfile.read()
romfile.close()
doc = xml.dom.minidom.Document()
root = doc.createElement("puzzles")
doc.appendChild(root)
dataIdx = 0
while (dataIdx < len(romdata)):
ÂÂÂÂchunk = romdata[dataIdx:dataIdx + 0x600]
ÂÂÂÂname = chunk[0x10:0x28].lstrip(' ')
ÂÂÂÂ
ÂÂÂÂif (len(name) > 4):
ÂÂÂÂÂÂÂÂif ((name[3] == '_') and name[0].isdigit() and name[1].isdigit() and name[2].isdigit()):
ÂÂÂÂÂÂÂÂÂÂÂÂhead1 = chunk[:0x10]
ÂÂÂÂÂÂÂÂÂÂÂÂ# name
ÂÂÂÂÂÂÂÂÂÂÂÂhead2 = chunk[0x28:0x98]
ÂÂÂÂÂÂÂÂÂÂÂÂenglishName = chunk[0x98:0xc8].replace(chr(0), '')
ÂÂÂÂÂÂÂÂÂÂÂÂhead3 = chunk[0xc8:0x188] # other languages
ÂÂÂÂÂÂÂÂÂÂÂÂdata = chunk[0x188:0x570]
ÂÂÂÂÂÂÂÂÂÂÂÂtail = chunk[0x570:0x600]
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl = doc.createElement("puzzle")
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("name", name)
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("englishname", englishName)
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("header1", hex_string(head1))
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("header2", hex_string(head2))
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("tail", hex_string(tail))
ÂÂÂÂÂÂÂÂÂÂÂÂtn = doc.createTextNode(hex_string(data));
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.appendChild(tn)
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ
ÂÂÂÂÂÂÂÂÂÂÂÂroot.appendChild(puzEl)
ÂÂÂÂdataIdx += 0x100
outfile = open("puzzles.xml", "wb")
outfile.write(doc.toprettyxml())
outfile.close
That's what I've figure out so far. I'm guessing when you create your own puzzles and save them, they're saved in a similar format in the save game file. I may mess with that later, and create some diagnostic puzzles to try to nail down what those other chunks are (and how the color palettes are defined).
Anyway, I've figured out that the puzzles are spread in groups throughout the ROM. They have the following format (I'll be using the "Blackboard" puzzle as an example):
0000 to 000F: Don't know. Blackboard has this: 20 04 05 1E 1E 03 C7 08 00 10 F0 4B 08 F6 01 0F. They always start with 20, and always have 00 10 F0 4B in those four bytes near the end. I'm thinking this might be some kind of color palette.
0010 to 0027: Puzzle ID name, padded with leading spaces. Blackboard has " 037_BLACKBOARD". It's always a 3 digit number, underscore, and all-caps name.
0028 to 0097: Don't know. Blackboard has: 00 00 00 00 00 00 00 00 71 05 C8 00 A0 01 C0 00 58 6B AC 35 10 42 08 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.
0098 to 0187: Descriptive name of the puzzle, in two-byte Unicode, in five languages (48 bytes each, padded with trailing zeros). English is first. Blackboard has "Blackboard".
0188 to 056F: The actual puzzle data. 1000 bytes, which covers the maximum 10x10x10 size of a puzzle. It appears to go up in columns, starting at the bottom of the puzzle and going up, then going to the next column, etc., until a layer is filled, then repeating for all 10 layers. It appears to fill completely empty columns with zero, but empty spaces in partially-filled columns with 0x20. The non-empty spaces contain 0x30, 0x31, 0x32, etc., which are color indexes (I can see by looking at a puzzle, for example, that everywhere it has a 0x30 is the same color, and all 0x31s are their own color, as are 0x32s, etc.).
0570 to 05FF: Don't know. Blackboard has: 02 00 02 00 00 00 02 00 02 00 02 00 00 00 00 00 00 00 00 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 00 00 00 00 00 00 3D 00 3D 00 3D 00 3D 00 3E 00 3E 00 3D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.
Each puzzle uses a 0x0600 size chunk, and they always start on page boundaries that are multiples of 0x0200. They're packed together in small groups, but the groups are spread throughout the ROM.
I've written a Python script to find all the puzzle chunks, and write them out to XML.
CODEimport xml.dom.minidom
def hex_string(inStr):
ÂÂÂÂoutStr = ""
ÂÂÂÂfor byte in inStr:
ÂÂÂÂÂÂÂÂoutStr += '%02X' % ord(byte) + ' '
ÂÂÂÂoutStr = outStr[:-1]
ÂÂÂÂreturn outStr
ÂÂÂÂ
romfile = open("4926 - Picross 3D (USA).nds", "rb")
romdata = romfile.read()
romfile.close()
doc = xml.dom.minidom.Document()
root = doc.createElement("puzzles")
doc.appendChild(root)
dataIdx = 0
while (dataIdx < len(romdata)):
ÂÂÂÂchunk = romdata[dataIdx:dataIdx + 0x600]
ÂÂÂÂname = chunk[0x10:0x28].lstrip(' ')
ÂÂÂÂ
ÂÂÂÂif (len(name) > 4):
ÂÂÂÂÂÂÂÂif ((name[3] == '_') and name[0].isdigit() and name[1].isdigit() and name[2].isdigit()):
ÂÂÂÂÂÂÂÂÂÂÂÂhead1 = chunk[:0x10]
ÂÂÂÂÂÂÂÂÂÂÂÂ# name
ÂÂÂÂÂÂÂÂÂÂÂÂhead2 = chunk[0x28:0x98]
ÂÂÂÂÂÂÂÂÂÂÂÂenglishName = chunk[0x98:0xc8].replace(chr(0), '')
ÂÂÂÂÂÂÂÂÂÂÂÂhead3 = chunk[0xc8:0x188] # other languages
ÂÂÂÂÂÂÂÂÂÂÂÂdata = chunk[0x188:0x570]
ÂÂÂÂÂÂÂÂÂÂÂÂtail = chunk[0x570:0x600]
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl = doc.createElement("puzzle")
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("name", name)
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("englishname", englishName)
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("header1", hex_string(head1))
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("header2", hex_string(head2))
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.setAttribute("tail", hex_string(tail))
ÂÂÂÂÂÂÂÂÂÂÂÂtn = doc.createTextNode(hex_string(data));
ÂÂÂÂÂÂÂÂÂÂÂÂpuzEl.appendChild(tn)
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ
ÂÂÂÂÂÂÂÂÂÂÂÂroot.appendChild(puzEl)
ÂÂÂÂdataIdx += 0x100
outfile = open("puzzles.xml", "wb")
outfile.write(doc.toprettyxml())
outfile.close
That's what I've figure out so far. I'm guessing when you create your own puzzles and save them, they're saved in a similar format in the save game file. I may mess with that later, and create some diagnostic puzzles to try to nail down what those other chunks are (and how the color palettes are defined).