Extracting puzzle data from Picross 3D

Discussion in 'NDS - ROM Hacking and Translations' started by Martoon, Aug 31, 2010.

Aug 31, 2010
  1. Martoon
    OP

    Newcomer Martoon Member

    Joined:
    Nov 10, 2006
    Messages:
    14
    Country:
    United States
    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).
     

Share This Page