Exemplo n.º 1
0
def rebuild_comm(filename, comm_data):
    # COMM chunk is *always* 0x430 bytes in size

    # Read in the JSON file and start plugging in data
    with open(filename + ".json", 'r') as json_in:
        data = json_in.read()
        decoded_data = json.loads(data)
    json_in.close()

    COMM_layout = pw64_lib.COMM_layout

    comm_assembler = io.BytesIO()
    comm_assembler.write(comm_data)

    comm_assembler.seek(COMM_layout["pilot_class"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["pilot_class"]))
    comm_assembler.seek(COMM_layout["vehicle"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["vehicle"]))
    comm_assembler.seek(COMM_layout["test_number"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["test_number"]))
    comm_assembler.seek(COMM_layout["level"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["level"]))
    comm_assembler.seek(COMM_layout["skybox"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["skybox"]))
    comm_assembler.seek(COMM_layout["snow"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["snow"]))

    comm_assembler.seek(COMM_layout["wind_WE"], 0)
    comm_assembler.write(pw64_lib.float_to_hex(
        decoded_data["COMM"]["wind_WE"]))
    comm_assembler.seek(COMM_layout["wind_SN"], 0)
    comm_assembler.write(pw64_lib.float_to_hex(
        decoded_data["COMM"]["wind_SN"]))
    comm_assembler.seek(COMM_layout["wind_UD"], 0)
    comm_assembler.write(pw64_lib.float_to_hex(
        decoded_data["COMM"]["wind_UD"]))

    # These get converted from int() to bytes([])
    comm_assembler.seek(COMM_layout["THER"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["THER"]]))
    comm_assembler.seek(COMM_layout["LWND"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["LWND"]]))
    comm_assembler.seek(COMM_layout["TPAD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["TPAD"]]))
    comm_assembler.seek(COMM_layout["LPAD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["LPAD"]]))
    comm_assembler.seek(COMM_layout["LSTP"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["LSTP"]]))
    comm_assembler.seek(COMM_layout["RNGS"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["RNGS"]]))
    comm_assembler.seek(COMM_layout["BALS"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["BALS"]]))
    comm_assembler.seek(COMM_layout["TARG"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["TARG"]]))
    comm_assembler.seek(COMM_layout["HPAD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["HPAD"]]))
    comm_assembler.seek(COMM_layout["BTGT"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["BTGT"]]))
    comm_assembler.seek(COMM_layout["PHTS"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["PHTS"]]))
    comm_assembler.seek(COMM_layout["FALC"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["FALC"]]))
    comm_assembler.seek(COMM_layout["UNKN"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["UNKN"]]))
    comm_assembler.seek(COMM_layout["CNTG"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["CNTG"]]))
    comm_assembler.seek(COMM_layout["HOPD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["HOPD"]]))

    # Rewind and populate the data to return. Yeah I know this can be optimized. Bite me.
    comm_assembler.seek(0)
    final_comm_data = comm_assembler.read()

    print("* COMM data rebuilt.")

    return final_comm_data
Exemplo n.º 2
0
def rebuild_upwt_chunk(filename, chunk):
    # This function does the actual "rebuilding" of objects from JSON data.
    # If an object doesn't exist in the JSON input, it won't get added to the UPWT container. Related to COMM counts.

    # Read in the JSON file and start plugging in data
    with open(filename + ".json", 'r') as json_in:
        data = json_in.read()
        decoded_data = json.loads(data)
    json_in.close()

    # Stock chunks from ROM, to be modified if JSON data differs
    # This _mostly_ should be OK in terms of "unknown" bytes in the data stream... seems to work out fine at least for E_GC_1
    TPAD_template = bytes.fromhex(
        'C222EB85C3CADCAC4120000041C00000000000000000000000000000000000000000000000000000000000003F800000'
    )
    RNGS_template = bytes.fromhex(
        'C392E937432F03D7425C000041B8000000000000000000000000000000010000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201000000000000000000006E0000004000000000000000000000007901000000000000000000000000000000000000'
    )
    LSTP_template = bytes.fromhex(
        'C1E68F5CC3D80E3541200000C316D74CC31E0C4A412000000000000001000000420C000000000000'
    )
    BALS_template = bytes.fromhex(
        'C290210643A74D71433600008000000000000000000000000000000041200000000000003C23D70A0000000000000000402000000000001E0000000000000000411CCCCD0000000000000000000000000000000000000000000000003F80000000000000411CCCCD'
    )

    upwt_io = io.BytesIO()

    if chunk == "TPAD":
        XPAD_layout = pw64_lib.TPAD_layout
        upwt_io.write(TPAD_template)  # Lay down the default, unmodified chunk
    elif chunk == "LSTP":
        XPAD_layout = pw64_lib.LSTP_layout
        upwt_io.write(LSTP_template)
    elif chunk == "BALS":
        BALS_layout = pw64_lib.BALS_layout
        upwt_io.write(BALS_template)

        # Set up a stream object just in case we have >1 BALS chunk
        bals_stack = io.BytesIO()

        if decoded_data["COMM"]["BALS"] >= 1:
            for ball_num in range(1, decoded_data["COMM"]["BALS"] + 1):
                #print(binascii.hexlify(BALS_template))
                ball_num = str(ball_num)
                upwt_io.seek(0)
                # Go through our structure and populate the template, convert all floats back to hex
                upwt_io.seek(BALS_layout["x"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(decoded_data[chunk][ball_num]["x"]))
                upwt_io.seek(BALS_layout["z"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(decoded_data[chunk][ball_num]["z"]))
                upwt_io.seek(BALS_layout["y"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(decoded_data[chunk][ball_num]["y"]))
                upwt_io.seek(BALS_layout["scale"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(
                        decoded_data[chunk][ball_num]["scale"]))
                # Things that don't get converted to float or decoded are straight ASCII hex
                upwt_io.seek(BALS_layout["color"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ball_num]["color"]))
                upwt_io.seek(BALS_layout["type"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ball_num]["type"]))
                upwt_io.seek(BALS_layout["solidity"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ball_num]["solidity"]))
                upwt_io.seek(BALS_layout["weight"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ball_num]["weight"]))
                upwt_io.seek(BALS_layout["popforce"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ball_num]["popforce"]))

                # Go to the end of the BALS stack, add current ring
                bals_stack.seek(0, 2)
                upwt_io.seek(0, 0)
                bals_stack.write(upwt_io.read())

        # Seek to start of bals_stack then return the ring stack (containing the X number of BALS)
        bals_stack.seek(0, 0)
        bals_data = bals_stack.read()
        bals_stack.close()

        print("* UPWT data chunk(s) rebuilt: %s" % chunk)

        return bals_data

    elif chunk == "RNGS":
        RNGS_layout = pw64_lib.RNGS_layout
        upwt_io.write(RNGS_template)

        # We likely have more than one ring so lets make a buffer for it
        rngs_stack = io.BytesIO()

        ring_count = decoded_data["COMM"]["RNGS"]

        if ring_count >= 1:
            for ring_num in range(1, ring_count + 1):
                ring_num = str(ring_num)
                upwt_io.seek(0)
                # Go through our structure and populate the template, convert all floats back to hex
                upwt_io.seek(RNGS_layout["x"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["x"]))
                upwt_io.seek(RNGS_layout["z"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["z"]))
                upwt_io.seek(RNGS_layout["y"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["y"]))
                upwt_io.seek(RNGS_layout["yaw"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(
                        decoded_data[chunk][ring_num]["yaw"]))
                upwt_io.seek(RNGS_layout["pitch"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(
                        decoded_data[chunk][ring_num]["pitch"]))
                upwt_io.seek(RNGS_layout["roll"], 0)
                upwt_io.write(
                    pw64_lib.float_to_hex(
                        decoded_data[chunk][ring_num]["roll"]))
                upwt_io.seek(RNGS_layout["size"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ring_num]["size"]))
                upwt_io.seek(RNGS_layout["state"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ring_num]["state"]))
                upwt_io.seek(RNGS_layout["motion_axis"], 0)
                upwt_io.write(
                    bytes.fromhex(
                        decoded_data[chunk][ring_num]["motion_axis"]))
                upwt_io.seek(RNGS_layout["motion_rad_start"], 0)
                upwt_io.write(
                    bytes.fromhex(
                        decoded_data[chunk][ring_num]["motion_rad_start"]))
                upwt_io.seek(RNGS_layout["motion_rad_end"], 0)
                upwt_io.write(
                    bytes.fromhex(
                        decoded_data[chunk][ring_num]["motion_rad_end"]))
                upwt_io.seek(RNGS_layout["rotation"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ring_num]["rotation"]))
                upwt_io.seek(RNGS_layout["rotation_speed"], 0)
                upwt_io.write(
                    bytes.fromhex(
                        decoded_data[chunk][ring_num]["rotation_speed"]))
                upwt_io.seek(RNGS_layout["ring_special"], 0)
                upwt_io.write(
                    bytes.fromhex(
                        decoded_data[chunk][ring_num]["ring_special"]))
                upwt_io.seek(RNGS_layout["next_ring_unknown"], 0)
                upwt_io.write(
                    bytes.fromhex(
                        decoded_data[chunk][ring_num]["next_ring_unknown"]))
                upwt_io.seek(RNGS_layout["next_ring_order_count"], 0)
                upwt_io.write(
                    bytes.fromhex(decoded_data[chunk][ring_num]
                                  ["next_ring_order_count"]))

                if int(decoded_data[chunk][ring_num]["next_ring_order_count"],
                       16) >= 1:
                    for ring_index in range(
                            0,
                            int(
                                decoded_data[chunk][ring_num]
                                ["next_ring_order_count"], 16)):
                        upwt_io.seek(
                            RNGS_layout["next_ring_index"][ring_index], 0)
                        upwt_io.write(
                            bytes.fromhex(
                                decoded_data[chunk][ring_num]
                                ["next_ring_index"][str(ring_index)]))
                else:
                    # This is a new one I found out while writing this tool...
                    # There seems to be form of list of rings to clear in order in some tasks. Sometimes its linear, sometimes there's choices.
                    # Prior to reading the next ring we have to clear the current/old index data of the "available" rings.
                    # Otherwise the next ring (even if it has 0 for the count) will re-use the RNGS_template with the previous ring's ring count.
                    # Im so tired and not sure why this works... but it will probably break for any level other than E_GC_1
                    # This will need attention later for sure.
                    upwt_io.seek(RNGS_layout["next_ring_index"][ring_index], 0)
                    upwt_io.write(bytes.fromhex("00000000"))

                # Add to our stack
                rngs_stack.seek(0, 2)
                upwt_io.seek(0, 0)
                rngs_stack.write(upwt_io.read())

        # For some reason theres another 0x4 bytes at the end of the RNGS chunk...
        rngs_stack.seek(0, 2)
        rngs_stack.write(b'\x00\x00\x00\x00')

        # Seek to start of rngs_stack, then dump the whole ring stack to a var for return
        rngs_stack.seek(0, 0)
        #upwt_io.seek(0, 0)
        ring_data = rngs_stack.read()
        rngs_stack.close()

        print("* UPWT data chunk(s) rebuilt: %s" % chunk)

        return ring_data

    if chunk == "TPAD" or chunk == "LSTP":
        upwt_io.seek(0)
        # Go through our structure and populate the template, convert all floats back to hex
        upwt_io.seek(XPAD_layout["x"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["x"]))
        upwt_io.seek(XPAD_layout["z"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["z"]))
        upwt_io.seek(XPAD_layout["y"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["y"]))
        upwt_io.seek(XPAD_layout["yaw"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["yaw"]))
        upwt_io.seek(XPAD_layout["roll"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["roll"]))
        upwt_io.seek(XPAD_layout["pitch"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["pitch"]))
        if chunk == "TPAD":
            upwt_io.seek(XPAD_layout["vehicle_fuel"], 0)
            upwt_io.write(
                pw64_lib.float_to_hex(decoded_data[chunk]["vehicle_fuel"]))

    # If we reach here, we return one of the singular objects like TakeOff Pad or Landing Strip.
    upwt_io.seek(0)
    upwt_output_chunk = upwt_io.read()
    upwt_io.close()

    print("* UPWT data chunk(s) rebuilt: %s" % chunk)

    return upwt_output_chunk
Exemplo n.º 3
0
def rebuild_comm(filename, comm_data):
    # COMM chunk is *always* 0x430 bytes in size
    UPWT_COMM_maker_and_size = b'434F4D4D00000430'

    # TODO: Update UPWT size when chunks are added

    # Read in the JSON file and start plugging in data
    with open(filename + ".json", 'r') as json_in:
        data = json_in.read()
        decoded_data = json.loads(data)
    json_in.close()

    UPWT_COMM_template_bytes = comm_data

    COMM_layout = pw64_lib.COMM_layout
    print("pilot_class: %s" % UPWT_COMM_template_bytes[COMM_layout["pilot_class"]])
    print("vehicle: %s" % UPWT_COMM_template_bytes[COMM_layout["vehicle"]])
    print("test_number: %s" % UPWT_COMM_template_bytes[COMM_layout["test_number"]])
    print("level: %s" % UPWT_COMM_template_bytes[COMM_layout["level"]])
    print("skybox: %s" % UPWT_COMM_template_bytes[COMM_layout["skybox"]])
    print("snow: %s" % UPWT_COMM_template_bytes[COMM_layout["snow"]])
    print("wind_WE: %x%x%x%x" % (int(UPWT_COMM_template_bytes[COMM_layout["wind_WE"]]),int(UPWT_COMM_template_bytes[COMM_layout["wind_WE"]+1]),
                                int(UPWT_COMM_template_bytes[COMM_layout["wind_WE"]+2]), int(UPWT_COMM_template_bytes[COMM_layout["wind_WE"]+3])))
    print("wind_SN: %x%x%x%x" % (int(UPWT_COMM_template_bytes[COMM_layout["wind_SN"]]),int(UPWT_COMM_template_bytes[COMM_layout["wind_SN"]+1]),
                                int(UPWT_COMM_template_bytes[COMM_layout["wind_SN"]+2]), int(UPWT_COMM_template_bytes[COMM_layout["wind_SN"]+3])))
    print("wind_UD: %x%x%x%x" % (int(UPWT_COMM_template_bytes[COMM_layout["wind_UD"]]),int(UPWT_COMM_template_bytes[COMM_layout["wind_UD"]+1]),
                                int(UPWT_COMM_template_bytes[COMM_layout["wind_UD"]+2]), int(UPWT_COMM_template_bytes[COMM_layout["wind_UD"]+3])))
    print("BALS: %s" % UPWT_COMM_template_bytes[COMM_layout["BALS"]])
    print("RNGS: %s" % UPWT_COMM_template_bytes[COMM_layout["RNGS"]])

    # For manipulating the bytes
    comm_assembler = io.BytesIO()
    #comm_assembler.write(UPWT_COMM_maker_and_size) For when we need tochange COMM size? But this should never be needed
    comm_assembler.write(UPWT_COMM_template_bytes)

    comm_assembler.seek(COMM_layout["pilot_class"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["pilot_class"]))
    comm_assembler.seek(COMM_layout["vehicle"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["vehicle"]))
    comm_assembler.seek(COMM_layout["test_number"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["test_number"]))
    comm_assembler.seek(COMM_layout["level"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["level"]))
    comm_assembler.seek(COMM_layout["skybox"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["skybox"]))
    comm_assembler.seek(COMM_layout["snow"], 0)
    comm_assembler.write(bytes.fromhex(decoded_data["COMM"]["snow"]))

    comm_assembler.seek(COMM_layout["wind_WE"], 0)
    comm_assembler.write(pw64_lib.float_to_hex(decoded_data["COMM"]["wind_WE"]))
    comm_assembler.seek(COMM_layout["wind_SN"], 0)
    comm_assembler.write(pw64_lib.float_to_hex(decoded_data["COMM"]["wind_SN"]))
    comm_assembler.seek(COMM_layout["wind_UD"], 0)
    comm_assembler.write(pw64_lib.float_to_hex(decoded_data["COMM"]["wind_UD"]))
    
    # These get converted from int() to bytes([])
    comm_assembler.seek(COMM_layout["THER"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["THER"]]))
    comm_assembler.seek(COMM_layout["LWND"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["LWND"]]))
    comm_assembler.seek(COMM_layout["TPAD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["TPAD"]]))
    comm_assembler.seek(COMM_layout["LPAD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["LPAD"]]))
    comm_assembler.seek(COMM_layout["LSTP"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["LSTP"]]))
    comm_assembler.seek(COMM_layout["RNGS"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["RNGS"]]))
    comm_assembler.seek(COMM_layout["BALS"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["BALS"]]))
    comm_assembler.seek(COMM_layout["TARG"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["TARG"]]))
    comm_assembler.seek(COMM_layout["HPAD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["HPAD"]]))
    comm_assembler.seek(COMM_layout["BTGT"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["BTGT"]]))
    comm_assembler.seek(COMM_layout["PHTS"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["PHTS"]]))
    comm_assembler.seek(COMM_layout["FALC"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["FALC"]]))
    comm_assembler.seek(COMM_layout["UNKN"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["UNKN"]]))
    comm_assembler.seek(COMM_layout["CNTG"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["CNTG"]]))
    comm_assembler.seek(COMM_layout["HOPD"], 0)
    comm_assembler.write(bytes([decoded_data["COMM"]["HOPD"]]))
    
    # Rewind and populate the data to return
    comm_assembler.seek(0)
    final_comm_data = comm_assembler.read()

    return final_comm_data
Exemplo n.º 4
0
def parse_upwt_json_test(filename):
    with open(filename + ".json", 'r') as json_in:
        data = json_in.read()
        #print(json.dumps(data))

        decoded_data = json.loads(data)
        print(decoded_data)
        print(decoded_data["task_id"])
        print(decoded_data["COMM"])
        print(decoded_data["COMM"]["vehicle"])

        ring_count = decoded_data["COMM"]["RNGS"]
        print("Number of Rings: %s" % ring_count)

        for ring in range(1, ring_count+1):
            print("Ring %s - X %f / %s" %(ring, decoded_data["RNGS"][str(ring)]["x"], binascii.hexlify(pw64_lib.float_to_hex(decoded_data["RNGS"][str(ring)]["x"]))))
    json_in.close()
Exemplo n.º 5
0
def parse_upwt_json(filename, chunk):
    # Read in the JSON file and start plugging in data
    with open(filename + ".json", 'r') as json_in:
        data = json_in.read()
        decoded_data = json.loads(data)
    json_in.close()

    # Stock chunks from ROM, to be modified if JSON data differs
    # This _mostly_ should be OK in terms of "unknown" bytes in the data stream... seems to work out fine at least for E_GC_1
    TPAD_template = bytes.fromhex('C222EB85C3CADCAC4120000041C00000000000000000000000000000000000000000000000000000000000003F800000')
    RNGS_template = bytes.fromhex('C392E937432F03D7425C000041B8000000000000000000000000000000010000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000201000000000000000000006E0000004000000000000000000000007901000000000000000000000000000000000000')
    LSTP_template = bytes.fromhex('C1E68F5CC3D80E3541200000C316D74CC31E0C4A412000000000000001000000420C000000000000')
    BALS_template = bytes.fromhex('C290210643A74D71433600008000000000000000000000000000000041200000000000003C23D70A0000000000000000402000000000001E0000000000000000411CCCCD0000000000000000000000000000000000000000000000003F80000000000000411CCCCD')

    upwt_io = io.BytesIO()

    if chunk == "TPAD":
        XPAD_layout = pw64_lib.TPAD_layout
        upwt_io.write(TPAD_template) # Lay down the default, unmodified chunk
    elif chunk == "LSTP":
        XPAD_layout = pw64_lib.LSTP_layout
        upwt_io.write(LSTP_template)
    elif chunk == "BALS":
        BALS_layout = pw64_lib.BALS_layout
        upwt_io.write(BALS_template)

        # For when we have >1 BALS chunk
        bals_stack = io.BytesIO()

        if decoded_data["COMM"]["BALS"] >= 1:
            for ball_num in range(1, decoded_data["COMM"]["BALS"] + 1):
                #print(binascii.hexlify(BALS_template))
                ball_num = str(ball_num)
                upwt_io.seek(0)
                # Go through our structure and populate the template, convert all floats back to hex
                upwt_io.seek(BALS_layout["x"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ball_num]["x"]))
                upwt_io.seek(BALS_layout["z"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ball_num]["z"]))
                upwt_io.seek(BALS_layout["y"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ball_num]["y"]))
                upwt_io.seek(BALS_layout["scale"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ball_num]["scale"]))
                #print("* %s" % decoded_data[chunk][ball_num]["scale"])
                upwt_io.seek(BALS_layout["color"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ball_num]["color"]))
                upwt_io.seek(BALS_layout["type"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ball_num]["type"]))
                upwt_io.seek(BALS_layout["solidity"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ball_num]["solidity"]))
                upwt_io.seek(BALS_layout["weight"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ball_num]["weight"]))
                upwt_io.seek(BALS_layout["popforce"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ball_num]["popforce"]))

                # Add to our stack
                bals_stack.seek(0, 2)
                upwt_io.seek(0, 0)
                bals_stack.write(upwt_io.read())

        # Seek to start of rngs_stack and upwt_io, then dump the ring stack into the upwt_io
        bals_stack.seek(0, 0)
        #upwt_io.seek(0, 0)
        bals_data = bals_stack.read()
        bals_stack.close()

        return bals_data

    elif chunk == "RNGS":
        RNGS_layout = pw64_lib.RNGS_layout
        upwt_io.write(RNGS_template)

        # we likely have more than one ring so lets make a buffer for it
        rngs_stack = io.BytesIO()

        ring_count  = decoded_data["COMM"]["RNGS"]
        if  ring_count >= 1:
            for ring_num in range(1, ring_count + 1):
                ring_num = str(ring_num)
                upwt_io.seek(0)
                # Go through our structure and populate the template, convert all floats back to hex
                upwt_io.seek(RNGS_layout["x"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["x"]))
                upwt_io.seek(RNGS_layout["z"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["z"]))
                upwt_io.seek(RNGS_layout["y"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["y"]))
                upwt_io.seek(RNGS_layout["yaw"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["yaw"]))
                upwt_io.seek(RNGS_layout["pitch"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["pitch"]))
                upwt_io.seek(RNGS_layout["roll"], 0)
                upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk][ring_num]["roll"]))
                upwt_io.seek(RNGS_layout["size"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["size"]))
                upwt_io.seek(RNGS_layout["state"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["state"]))
                upwt_io.seek(RNGS_layout["motion_axis"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["motion_axis"]))
                upwt_io.seek(RNGS_layout["motion_rad_start"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["motion_rad_start"]))
                upwt_io.seek(RNGS_layout["motion_rad_end"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["motion_rad_end"]))
                upwt_io.seek(RNGS_layout["rotation"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["rotation"]))
                upwt_io.seek(RNGS_layout["rotation_speed"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["rotation_speed"]))
                upwt_io.seek(RNGS_layout["ring_special"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["ring_special"]))
                upwt_io.seek(RNGS_layout["next_ring_unknown"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["next_ring_unknown"]))
                upwt_io.seek(RNGS_layout["next_ring_order_count"], 0)
                upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["next_ring_order_count"]))

                if int(decoded_data[chunk][ring_num]["next_ring_order_count"], 16) >= 1:
                    for ring_index in range(0, int(decoded_data[chunk][ring_num]["next_ring_order_count"], 16)):
                        print("!!!: %s" % ring_index)
                        upwt_io.seek(RNGS_layout["next_ring_index"][ring_index], 0)
                        upwt_io.write(bytes.fromhex(decoded_data[chunk][ring_num]["next_ring_index"][str(ring_index)]))
                else:
                    # Prior to reading the next index we have to clear the index data of the available rings.
                    # Otherwise the next ring (even if it has 0 for the count) will re-use the RNGS_template with the previous ring's ring count.
                    # Im so tired and not sure why this works... but it will probably break for any level other than E_GC_1
                    # This will need attention later for sure.
                    print(ring_index)
                    upwt_io.seek(RNGS_layout["next_ring_index"][ring_index], 0)
                    upwt_io.write(bytes.fromhex("00000000"))


                # Add to our stack
                rngs_stack.seek(0, 2)
                upwt_io.seek(0, 0)
                rngs_stack.write(upwt_io.read())

                ### Test
                #upwt_io.seek(0, 0)
                #print("Ring: %s" % ring_num)
                #print(binascii.hexlify(upwt_io.read()))
                #upwt_io.seek(0, 0)
                #print("---")
                #rngs_dump_test(upwt_io.read())
                #print("---")

        # For some reason theres another 0x4 bytes at the end of the RNGS chunk...
        rngs_stack.seek(0, 2)
        rngs_stack.write(b'\x00\x00\x00\x00')

        # Seek to start of rngs_stack and upwt_io, then dump the ring stack into the upwt_io
        rngs_stack.seek(0, 0)
        #upwt_io.seek(0, 0)
        ring_data = rngs_stack.read()
        print("FINAL:")
        print(binascii.hexlify(ring_data))
        rngs_stack.close()

        return ring_data

    if chunk == "TPAD" or chunk == "LSTP":
        #print(binascii.hexlify(TPAD_template))
        upwt_io.seek(0)
        # Go through our structure and populate the template, convert all floats back to hex
        upwt_io.seek(XPAD_layout["x"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["x"]))
        upwt_io.seek(XPAD_layout["z"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["z"]))
        upwt_io.seek(XPAD_layout["y"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["y"]))
        upwt_io.seek(XPAD_layout["yaw"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["yaw"]))
        upwt_io.seek(XPAD_layout["roll"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["roll"]))
        upwt_io.seek(XPAD_layout["pitch"], 0)
        upwt_io.write(pw64_lib.float_to_hex(decoded_data[chunk]["pitch"]))

    upwt_io.seek(0)
    upwt_output_chunk = upwt_io.read()
    upwt_io.close()

    #sys.exit(0)
    return upwt_output_chunk