예제 #1
0
        public void LoadTables()
        {
            m_FileTable = new ushort[m_FileTableLength];

            NitroOverlay ovl0 = new NitroOverlay(this, 0);

            for (uint i = 0; i < m_FileTableLength; i++)
            {
                if (ovl0.Read32(m_FileTableOffset + (i * 4)) != 0 || m_Version != Version.EUR)
                {
                    uint   str_offset = ovl0.ReadPointer(m_FileTableOffset + (i * 4));
                    string fname      = ovl0.ReadString(str_offset, 0);
                    m_FileTable[i] = GetFileIDFromName(fname);
                    m_FileEntries[GetFileIDFromName(fname)].InternalID = (ushort)i;
                }
                else
                {
                    m_FileTable[i] = 0xffff;
                }
            }

            m_FileStream.Position = m_LevelOvlIDTableOffset;
            m_LevelOvlIDTable     = new uint[52];
            for (uint i = 0; i < 52; i++)
            {
                m_LevelOvlIDTable[i] = m_BinReader.ReadUInt32();
            }
        }
예제 #2
0
        public void LoadTables()
        {
            m_FileTable = new ushort[m_FileTableLength];

            NitroOverlay ovl0 = new NitroOverlay(this, 0);

            for (uint i = 0; i < m_FileTableLength; i++)
            {
                uint   str_offset = ovl0.ReadPointer(m_FileTableOffset + (i * 4));
                string fname      = ovl0.ReadString(str_offset, 0);
                m_FileTable[i] = GetFileIDFromName(fname);
            }

            m_FileStream.Position = m_LevelOvlIDTableOffset;
            m_LevelOvlIDTable     = new uint[52];
            for (uint i = 0; i < 52; i++)
            {
                m_LevelOvlIDTable[i] = m_BinReader.ReadUInt32();
            }
        }
예제 #3
0
        private void Patch_v2(BackgroundWorker lazyman)
        {
            lazyman.ReportProgress(200);

            uint levelptr_table = 0, objbank_table = 0;
            uint mempatch1 = 0, mempatch2 = 0;

            byte[] loadercode = null;
            uint   lvl_ovlid_table = 0;
            uint   unload_patch = 0, unload_branchop = 0;

            uint[] ovltable_addr_patch = new uint[4];
            uint[] ovltable_size_patch = new uint[4];
            uint   lvlload_addr_patch = 0, lvlload_code_patch = 0;
            uint   objbank_addr_patch = 0, objbank_code_patch = 0;

            switch (m_Version)
            {
            case Version.EUR:
                levelptr_table         = 0x92208;
                objbank_table          = 0x75998;
                mempatch1              = 0x18B60;
                mempatch2              = 0x58DE0;
                loadercode             = Properties.Resources.level_ovl_init_EUR;
                lvl_ovlid_table        = 0x758C8;
                unload_patch           = 0x2DE80;
                unload_branchop        = ARM_BL(0x0202DE80, 0x0214EAD8);
                ovltable_addr_patch[0] = 0x17E90; ovltable_addr_patch[1] = 0x17F30;
                ovltable_addr_patch[2] = 0x17FCC; ovltable_addr_patch[3] = 0x180FC;
                ovltable_size_patch[0] = 0x17E80; ovltable_size_patch[1] = 0x17ED0;
                ovltable_size_patch[2] = 0x17F70; ovltable_size_patch[3] = 0x180C4;
                lvlload_addr_patch     = 0x2D62C;
                lvlload_code_patch     = 0x2D288;
                objbank_addr_patch     = 0x2E074;
                objbank_code_patch     = 0x2DFAC;
                break;

            case Version.JAP:
                levelptr_table         = 0x902B8;
                objbank_table          = 0x73C08;
                mempatch1              = 0x57368;
                mempatch2              = 0xFFFFFFFF;
                loadercode             = Properties.Resources.level_ovl_init_JAP;
                lvl_ovlid_table        = 0x73B38;
                unload_patch           = 0x2CDD8;
                unload_branchop        = ARM_BL(0x0202CDD8, 0x021434B8);
                ovltable_addr_patch[0] = 0x17DE4; ovltable_addr_patch[1] = 0x17E84;
                ovltable_addr_patch[2] = 0x17F20; ovltable_addr_patch[3] = 0x18050;
                ovltable_size_patch[0] = 0x17DD4; ovltable_size_patch[1] = 0x17E24;
                ovltable_size_patch[2] = 0x17EC4; ovltable_size_patch[3] = 0x18018;
                lvlload_addr_patch     = 0x2C7D4;
                lvlload_code_patch     = 0x2C430;
                objbank_addr_patch     = 0x2CFCC;
                objbank_code_patch     = 0x2CF04;
                break;

            case Version.USA_v1:
                levelptr_table         = 0x8FDB0;
                objbank_table          = 0x73664;
                mempatch1              = 0x56EB8;
                mempatch2              = 0xFFFFFFFF;
                loadercode             = Properties.Resources.level_ovl_init_USAv1;
                lvl_ovlid_table        = 0x73594;
                unload_patch           = 0x2CB00;
                unload_branchop        = ARM_BL(0x0202CB00, 0x02143BD8);
                ovltable_addr_patch[0] = 0x17D70; ovltable_addr_patch[1] = 0x17E10;
                ovltable_addr_patch[2] = 0x17EAC; ovltable_addr_patch[3] = 0x17FDC;
                ovltable_size_patch[0] = 0x17D60; ovltable_size_patch[1] = 0x17DB0;
                ovltable_size_patch[2] = 0x17E50; ovltable_size_patch[3] = 0x17FA4;
                lvlload_addr_patch     = 0x2C4FC;
                lvlload_code_patch     = 0x2C15C;
                objbank_addr_patch     = 0x2CCF4;
                objbank_code_patch     = 0x2CC2C;
                break;

            case Version.USA_v2:
                levelptr_table         = 0x90ACC;
                objbank_table          = 0x74384;
                mempatch1              = 0x18A44;
                mempatch2              = 0x57B30;
                loadercode             = Properties.Resources.level_ovl_init_USAv2;
                lvl_ovlid_table        = 0x742B4;
                unload_patch           = 0x2CE14;
                unload_branchop        = ARM_BL(0x0202CE14, 0x02145758);
                ovltable_addr_patch[0] = 0x17DE4; ovltable_addr_patch[1] = 0x17E84;
                ovltable_addr_patch[2] = 0x17F20; ovltable_addr_patch[3] = 0x18050;
                ovltable_size_patch[0] = 0x17DD4; ovltable_size_patch[1] = 0x17E24;
                ovltable_size_patch[2] = 0x17EC4; ovltable_size_patch[3] = 0x18018;
                lvlload_addr_patch     = 0x2C810;
                lvlload_code_patch     = 0x2C46C;
                objbank_addr_patch     = 0x2D008;
                objbank_code_patch     = 0x2CF40;
                break;
            }

            // tweak the root heap start address to gain more overlay space :)
            m_FileStream.Position = mempatch1;
            uint lvl_start = m_BinReader.ReadUInt32();

            uint lvl_end = lvl_start + LEVEL_OVERLAY_SIZE;

            m_FileStream.Position = mempatch1;
            m_BinWriter.Write(lvl_end);
            if (mempatch2 != 0xFFFFFFFF)
            {
                m_FileStream.Position = mempatch2;
                m_BinWriter.Write(lvl_end);
            }

            // patch level overlay unloading
            m_FileStream.Position = unload_patch;
            m_BinWriter.Write(unload_branchop);

            // patch more stuff
            foreach (uint addr in ovltable_addr_patch)
            {
                m_FileStream.Position = addr;
                m_BinWriter.Write((uint)(0x02000000 + levelptr_table));
            }
            foreach (uint addr in ovltable_size_patch)
            {
                m_FileStream.Position = addr;
                uint op = m_BinReader.ReadUInt32();
                op = (op & 0xFFFFFF00) | 0x0D;
                m_FileStream.Position = addr;
                m_BinWriter.Write(op);
            }

            lazyman.ReportProgress(201);

            // for each level, create a new overlay with the loader code and level data
            uint dataoffset = (uint)((loadercode.Length + 3) & ~3);

            for (int i = 0; i < 52; i++)
            {
                uint overlayid = AddOverlay(lvl_start);

                m_FileStream.Position = lvl_ovlid_table + (i * 4);
                uint old_overlayid = m_BinReader.ReadUInt32();

                m_FileStream.Position = lvl_ovlid_table + (i * 4);
                m_BinWriter.Write(overlayid);

                NitroOverlay ovl = new NitroOverlay(this, overlayid);
                ovl.SetInitializer(lvl_start, 4);
                ovl.WriteBlock(0, loadercode);

                // write the object bank settings
                m_FileStream.Position = objbank_table + (i * 7);
                byte[] objbanks = m_BinReader.ReadBytes(7);
                ovl.WriteBlock(dataoffset, objbanks);

                ovl.Write8(dataoffset + 0x7, 0x00);
                ovl.Write32(dataoffset + 0x8, (uint)(i + 1));

                // copy level data
                {
                    NitroOverlay oldovl  = new NitroOverlay(this, old_overlayid);
                    uint         oldbase = oldovl.GetRAMAddr();

                    m_FileStream.Position = levelptr_table + (i * 4);
                    uint header_offset = m_BinReader.ReadUInt32() - oldbase;

                    uint curoffset = dataoffset + 0x40;

                    // CLPS (collision behaviors) chunk
                    {
                        uint   clps_addr = oldovl.ReadPointer(header_offset);
                        ushort clps_num  = oldovl.Read16(clps_addr + 0x06);
                        uint   clps_size = (uint)(8 + (clps_num * 8));
                        byte[] clps      = oldovl.ReadBlock(clps_addr, clps_size);
                        ovl.WriteBlock(curoffset, clps);

                        ovl.WritePointer(dataoffset + 0x0C, curoffset);
                        curoffset += clps_size;
                    }

                    // misc objects table list
                    {
                        uint   tables_addr = oldovl.ReadPointer(header_offset + 0x4);
                        ushort tables_num  = oldovl.Read16(tables_addr);
                        tables_addr = oldovl.ReadPointer(tables_addr + 0x4);

                        ovl.WritePointer(dataoffset + 0x10, curoffset);
                        ovl.Write16(curoffset, tables_num);
                        ovl.WritePointer(curoffset + 0x04, curoffset + 0x8);
                        curoffset += 0x8;

                        uint objdata_offset = (uint)(curoffset + (tables_num * 8));
                        for (ushort t = 0; t < tables_num; t++)
                        {
                            uint tbl_part1 = oldovl.Read32(tables_addr);
                            uint tbl_addr  = oldovl.ReadPointer(tables_addr + 0x4);

                            ovl.Write32(curoffset, tbl_part1);
                            ovl.WritePointer(curoffset + 0x4, objdata_offset);

                            tables_addr += 8;
                            curoffset   += 8;

                            byte tbltype    = (byte)(tbl_part1 & 0x1F);
                            byte numentries = (byte)((tbl_part1 >> 8) & 0xFF);

                            if ((tbltype == 13) || (tbltype > 14))
                            {
                                throw new Exception(String.Format("Wrong object table type {0} (0x{0:X2}) in level data", tbltype));
                            }

                            int[] datasizes = { 16, 16, 6, 6, 14, 8, 8, 8, 8, 12, 14, 2, 2, 0, 4 };
                            int   datasize  = numentries * datasizes[tbltype];

                            byte[] data = oldovl.ReadBlock(tbl_addr, (uint)datasize);
                            ovl.WriteBlock(objdata_offset, data);

                            objdata_offset += (uint)((datasize + 3) & ~3);
                        }

                        curoffset = objdata_offset;
                    }

                    // main object table lists
                    // and that stuff about texture scrolling
                    {
                        uint tlists_addr = oldovl.ReadPointer(header_offset + 0x10);
                        byte tlists_num  = oldovl.Read8(header_offset + 0x14);

                        uint tlists_new_addr = curoffset;
                        curoffset += (uint)(tlists_num * 12);

                        ovl.WritePointer(dataoffset + 0x1C, tlists_new_addr);
                        ovl.Write8(dataoffset + 0x20, tlists_num);

                        for (byte tl = 0; tl < tlists_num; tl++)
                        {
                            uint tables_addr = oldovl.ReadPointer(tlists_addr);
                            uint texanm_addr = oldovl.ReadPointer(tlists_addr + 0x4);

                            if (tables_addr != 0xFFFFFFFF)
                            {
                                ovl.WritePointer(tlists_new_addr, curoffset);

                                ushort tables_num = oldovl.Read16(tables_addr);
                                tables_addr = oldovl.ReadPointer(tables_addr + 0x4);

                                ovl.Write16(curoffset, tables_num);
                                ovl.WritePointer(curoffset + 0x04, curoffset + 0x8);
                                curoffset += 0x8;

                                uint objdata_offset = (uint)(curoffset + (tables_num * 8));
                                for (ushort t = 0; t < tables_num; t++)
                                {
                                    uint tbl_part1 = oldovl.Read32(tables_addr);
                                    uint tbl_addr  = oldovl.ReadPointer(tables_addr + 0x4);

                                    ovl.Write32(curoffset, tbl_part1);
                                    ovl.WritePointer(curoffset + 0x4, objdata_offset);

                                    tables_addr += 8;
                                    curoffset   += 8;

                                    byte tbltype    = (byte)(tbl_part1 & 0x1F);
                                    byte numentries = (byte)((tbl_part1 >> 8) & 0xFF);

                                    if ((tbltype == 13) || (tbltype > 14))
                                    {
                                        throw new Exception(String.Format("Wrong object table type {0} (0x{0:X2}) in level data", tbltype));
                                    }

                                    int[] datasizes = { 16, 16, 6, 6, 14, 8, 8, 8, 8, 12, 14, 2, 2, 0, 4 };
                                    int   datasize  = numentries * datasizes[tbltype];

                                    byte[] data = oldovl.ReadBlock(tbl_addr, (uint)datasize);
                                    ovl.WriteBlock(objdata_offset, data);

                                    objdata_offset += (uint)((datasize + 3) & ~3);
                                }

                                curoffset = objdata_offset;
                            }
                            else
                            {
                                ovl.Write32(tlists_new_addr, 0);
                            }

                            if (texanm_addr != 0xFFFFFFFF)//If not null
                            {
                                ovl.WritePointer(tlists_new_addr + 0x4, curoffset);

                                uint texanm_new_addr = curoffset;
                                curoffset += 0x18;

                                uint textures_addr = oldovl.ReadPointer(texanm_addr + 0x14);
                                uint numscale = 0, numrot = 0, numtrans = 0;
                                uint numtextures = oldovl.Read32(texanm_addr + 0x10);

                                ovl.Write32(texanm_new_addr, oldovl.Read32(texanm_addr));
                                ovl.Write32(texanm_new_addr + 0x10, numtextures);

                                uint textures_new_addr = curoffset;
                                curoffset += (numtextures * 0x1C);

                                ovl.WritePointer(texanm_new_addr + 0x14, textures_new_addr);
                                for (uint t = 0; t < numtextures; t++)
                                {
                                    uint tex_old_addr = textures_addr + (t * 0x1C);
                                    uint tex_new_addr = textures_new_addr + (t * 0x1C);

                                    ushort tex_scalenum   = oldovl.Read16(tex_old_addr + 0x0C);
                                    ushort tex_scalestart = oldovl.Read16(tex_old_addr + 0x0E);
                                    ushort tex_rotnum     = oldovl.Read16(tex_old_addr + 0x10);
                                    ushort tex_rotstart   = oldovl.Read16(tex_old_addr + 0x12);
                                    ushort tex_transnum   = oldovl.Read16(tex_old_addr + 0x14);
                                    ushort tex_transstart = oldovl.Read16(tex_old_addr + 0x16);

                                    if ((tex_scalestart + tex_scalenum) > numscale)
                                    {
                                        numscale = (uint)(tex_scalestart + tex_scalenum);
                                    }
                                    if ((tex_rotstart + tex_rotnum) > numrot)
                                    {
                                        numrot = (uint)(tex_rotstart + tex_rotnum);
                                    }
                                    if ((tex_transstart + tex_transnum) > numtrans)
                                    {
                                        numtrans = (uint)(tex_transstart + tex_transnum);
                                    }

                                    ovl.Write32(tex_new_addr, oldovl.Read32(tex_old_addr));
                                    ovl.WritePointer(tex_new_addr + 0x4, curoffset);
                                    ovl.Write32(tex_new_addr + 0x8, oldovl.Read32(tex_old_addr + 0x8));
                                    ovl.Write16(tex_new_addr + 0xC, tex_scalenum);
                                    ovl.Write16(tex_new_addr + 0xE, tex_scalestart);
                                    ovl.Write16(tex_new_addr + 0x10, tex_rotnum);
                                    ovl.Write16(tex_new_addr + 0x12, tex_rotstart);
                                    ovl.Write16(tex_new_addr + 0x14, tex_transnum);
                                    ovl.Write16(tex_new_addr + 0x16, tex_transstart);
                                    ovl.Write32(tex_new_addr + 0x18, oldovl.Read32(tex_old_addr + 0x18));

                                    string tex_matname = oldovl.ReadString(oldovl.ReadPointer(tex_old_addr + 0x4), 0);
                                    ovl.WriteString(curoffset, tex_matname, 0);
                                    curoffset += (uint)((tex_matname.Length + 3) & ~3);
                                }

                                uint scale_addr = oldovl.ReadPointer(texanm_addr + 0x4);
                                ovl.WritePointer(texanm_new_addr + 0x4, curoffset);
                                for (uint v = 0; v < numscale; v++)
                                {
                                    ovl.Write32(curoffset, oldovl.Read32(scale_addr + (v * 4)));
                                    curoffset += 4;
                                }

                                uint rot_addr = oldovl.ReadPointer(texanm_addr + 0x8);
                                ovl.WritePointer(texanm_new_addr + 0x8, curoffset);
                                for (uint v = 0; v < numrot; v++)
                                {
                                    ovl.Write16(curoffset, oldovl.Read16(rot_addr + (v * 2)));
                                    curoffset += 2;
                                }
                                curoffset = (uint)((curoffset + 3) & ~3);

                                uint trans_addr = oldovl.ReadPointer(texanm_addr + 0xC);
                                ovl.WritePointer(texanm_new_addr + 0xC, curoffset);
                                for (uint v = 0; v < numtrans; v++)
                                {
                                    ovl.Write32(curoffset, oldovl.Read32(trans_addr + (v * 4)));
                                    curoffset += 4;
                                }
                            }
                            else
                            {
                                ovl.Write32(tlists_new_addr + 0x4, 0);
                            }

                            ovl.Write32(tlists_new_addr + 0x8, oldovl.Read32(tlists_addr + 0x8));
                            tlists_new_addr += 12;
                            tlists_addr     += 12;
                        }
                    }

                    // misc header pieces
                    ovl.Write32(dataoffset + 0x14, oldovl.Read32(header_offset + 0x8));
                    ovl.Write32(dataoffset + 0x18, oldovl.Read32(header_offset + 0xC));
                    ovl.Write16(dataoffset + 0x22, oldovl.Read16(header_offset + 0x16));
                    ovl.Write8(dataoffset + 0x24, oldovl.Read8(header_offset + 0x18));
                }

                ovl.SaveChanges();
                lazyman.ReportProgress(201 + (int)((98f / 54f) * i));
            }

            // fix the level loader as to load from the new overlays
            m_FileStream.Position = lvlload_addr_patch;
            m_BinWriter.Write((uint)(lvl_start + dataoffset + 0xC));

            m_FileStream.Position = lvlload_code_patch;
            m_BinWriter.Write((uint)0xE1A00000);

            // refill the old level header address table
            // to adapt it to its new usages
            m_FileStream.Position = levelptr_table;
            for (uint i = 0; i < 13; i++)
            {
                m_BinWriter.Write((uint)0xFFFFFFFF);
                m_BinWriter.Write((uint)0x00000000);
                m_BinWriter.Write((uint)0x00000000);
            }
            m_BinWriter.Write((uint)0x00000000);
            m_BinWriter.Write((uint)0x00000000);

            // fix the object banks thingy
            m_FileStream.Position = objbank_addr_patch;
            m_BinWriter.Write((uint)(levelptr_table + 0x0200009C));

            m_FileStream.Position = objbank_code_patch;
            m_BinWriter.Write((uint)0xE1A07001);

            // phew! what a goddamn long thing
            lazyman.ReportProgress(299);
        }
예제 #4
0
        public void LoadTables()
        {
            m_FileTable = new ushort[m_FileTableLength];

            NitroOverlay ovl0 = new NitroOverlay(this, 0);
            for (uint i = 0; i < m_FileTableLength; i++)
            {
                uint str_offset = ovl0.ReadPointer(m_FileTableOffset + (i*4));
                string fname = ovl0.ReadString(str_offset, 0);
                m_FileTable[i] = GetFileIDFromName(fname);
            }

            m_FileStream.Position = m_LevelOvlIDTableOffset;
            m_LevelOvlIDTable = new uint[52];
            for (uint i = 0; i < 52; i++)
                m_LevelOvlIDTable[i] = m_BinReader.ReadUInt32();
        }
예제 #5
0
        private void Patch_v2(BackgroundWorker lazyman)
        {
            lazyman.ReportProgress(200);

            uint levelptr_table = 0, objbank_table = 0;
            uint mempatch1 = 0, mempatch2 = 0;
            byte[] loadercode = null;
            uint lvl_ovlid_table = 0;
            uint unload_patch = 0, unload_branchop = 0;
            uint[] ovltable_addr_patch = new uint[4];
            uint[] ovltable_size_patch = new uint[4];
            uint lvlload_addr_patch = 0, lvlload_code_patch = 0;
            uint objbank_addr_patch = 0, objbank_code_patch = 0;

            switch (m_Version)
            {
            case Version.EUR:
                levelptr_table = 0x92208;
                objbank_table = 0x75998;
                mempatch1 = 0x18B60;
                mempatch2 = 0x58DE0;
                loadercode = Properties.Resources.level_ovl_init_EUR;
                lvl_ovlid_table = 0x758C8;
                unload_patch = 0x2DE80;
                unload_branchop = ARM_BL(0x0202DE80, 0x0214EAD8);
                ovltable_addr_patch[0] = 0x17E90; ovltable_addr_patch[1] = 0x17F30;
                ovltable_addr_patch[2] = 0x17FCC; ovltable_addr_patch[3] = 0x180FC;
                ovltable_size_patch[0] = 0x17E80; ovltable_size_patch[1] = 0x17ED0;
                ovltable_size_patch[2] = 0x17F70; ovltable_size_patch[3] = 0x180C4;
                lvlload_addr_patch = 0x2D62C;
                lvlload_code_patch = 0x2D288;
                objbank_addr_patch = 0x2E074;
                objbank_code_patch = 0x2DFAC;
                break;

            case Version.JAP:
                levelptr_table = 0x902B8;
                objbank_table = 0x73C08;
                mempatch1 = 0x57368;
                mempatch2 = 0xFFFFFFFF;
                loadercode = Properties.Resources.level_ovl_init_JAP;
                lvl_ovlid_table = 0x73B38;
                unload_patch = 0x2CDD8;
                unload_branchop = ARM_BL(0x0202CDD8, 0x021434B8);
                ovltable_addr_patch[0] = 0x17DE4; ovltable_addr_patch[1] = 0x17E84;
                ovltable_addr_patch[2] = 0x17F20; ovltable_addr_patch[3] = 0x18050;
                ovltable_size_patch[0] = 0x17DD4; ovltable_size_patch[1] = 0x17E24;
                ovltable_size_patch[2] = 0x17EC4; ovltable_size_patch[3] = 0x18018;
                lvlload_addr_patch = 0x2C7D4;
                lvlload_code_patch = 0x2C430;
                objbank_addr_patch = 0x2CFCC;
                objbank_code_patch = 0x2CF04;
                break;

            case Version.USA_v1:
                levelptr_table = 0x8FDB0;
                objbank_table = 0x73664;
                mempatch1 = 0x56EB8;
                mempatch2 = 0xFFFFFFFF;
                loadercode = Properties.Resources.level_ovl_init_USAv1;
                lvl_ovlid_table = 0x73594;
                unload_patch = 0x2CB00;
                unload_branchop = ARM_BL(0x0202CB00, 0x02143BD8);
                ovltable_addr_patch[0] = 0x17D70; ovltable_addr_patch[1] = 0x17E10;
                ovltable_addr_patch[2] = 0x17EAC; ovltable_addr_patch[3] = 0x17FDC;
                ovltable_size_patch[0] = 0x17D60; ovltable_size_patch[1] = 0x17DB0;
                ovltable_size_patch[2] = 0x17E50; ovltable_size_patch[3] = 0x17FA4;
                lvlload_addr_patch = 0x2C4FC;
                lvlload_code_patch = 0x2C15C;
                objbank_addr_patch = 0x2CCF4;
                objbank_code_patch = 0x2CC2C;
                break;

            case Version.USA_v2:
                levelptr_table = 0x90ACC;
                objbank_table = 0x74384;
                mempatch1 = 0x18A44;
                mempatch2 = 0x57B30;
                loadercode = Properties.Resources.level_ovl_init_USAv2;
                lvl_ovlid_table = 0x742B4;
                unload_patch = 0x2CE14;
                unload_branchop = ARM_BL(0x0202CE14, 0x02145758);
                ovltable_addr_patch[0] = 0x17DE4; ovltable_addr_patch[1] = 0x17E84;
                ovltable_addr_patch[2] = 0x17F20; ovltable_addr_patch[3] = 0x18050;
                ovltable_size_patch[0] = 0x17DD4; ovltable_size_patch[1] = 0x17E24;
                ovltable_size_patch[2] = 0x17EC4; ovltable_size_patch[3] = 0x18018;
                lvlload_addr_patch = 0x2C810;
                lvlload_code_patch = 0x2C46C;
                objbank_addr_patch = 0x2D008;
                objbank_code_patch = 0x2CF40;
                break;
            }

            // tweak the root heap start address to gain more overlay space :)
            m_FileStream.Position = mempatch1;
            uint lvl_start = m_BinReader.ReadUInt32();

            uint lvl_end = lvl_start + LEVEL_OVERLAY_SIZE;
            m_FileStream.Position = mempatch1;
            m_BinWriter.Write(lvl_end);
            if (mempatch2 != 0xFFFFFFFF)
            {
                m_FileStream.Position = mempatch2;
                m_BinWriter.Write(lvl_end);
            }

            // patch level overlay unloading
            m_FileStream.Position = unload_patch;
            m_BinWriter.Write(unload_branchop);

            // patch more stuff
            foreach (uint addr in ovltable_addr_patch)
            {
                m_FileStream.Position = addr;
                m_BinWriter.Write((uint)(0x02000000 + levelptr_table));
            }
            foreach (uint addr in ovltable_size_patch)
            {
                m_FileStream.Position = addr;
                uint op = m_BinReader.ReadUInt32();
                op = (op & 0xFFFFFF00) | 0x0D;
                m_FileStream.Position = addr;
                m_BinWriter.Write(op);
            }

            lazyman.ReportProgress(201);

            // for each level, create a new overlay with the loader code and level data
            uint dataoffset = (uint)((loadercode.Length + 3) & ~3);

            for (int i = 0; i < 52; i++)
            {
                uint overlayid = AddOverlay(lvl_start);

                m_FileStream.Position = lvl_ovlid_table + (i*4);
                uint old_overlayid = m_BinReader.ReadUInt32();

                m_FileStream.Position = lvl_ovlid_table + (i*4);
                m_BinWriter.Write(overlayid);

                NitroOverlay ovl = new NitroOverlay(this, overlayid);
                ovl.SetInitializer(lvl_start, 4);
                ovl.WriteBlock(0, loadercode);

                // write the object bank settings
                m_FileStream.Position = objbank_table + (i * 7);
                byte[] objbanks = m_BinReader.ReadBytes(7);
                ovl.WriteBlock(dataoffset, objbanks);

                ovl.Write8(dataoffset + 0x7, 0x00);
                ovl.Write32(dataoffset + 0x8, (uint)(i + 1));

                // copy level data
                {
                    NitroOverlay oldovl = new NitroOverlay(this, old_overlayid);
                    uint oldbase = oldovl.GetRAMAddr();

                    m_FileStream.Position = levelptr_table + (i*4);
                    uint header_offset = m_BinReader.ReadUInt32() - oldbase;

                    uint curoffset = dataoffset + 0x40;

                    // CLPS (collision behaviors) chunk
                    {
                        uint clps_addr = oldovl.ReadPointer(header_offset);
                        ushort clps_num = oldovl.Read16(clps_addr + 0x06);
                        uint clps_size = (uint)(8 + (clps_num * 8));
                        byte[] clps = oldovl.ReadBlock(clps_addr, clps_size);
                        ovl.WriteBlock(curoffset, clps);

                        ovl.WritePointer(dataoffset + 0x0C, curoffset);
                        curoffset += clps_size;
                    }

                    // misc objects table list
                    {
                        uint tables_addr = oldovl.ReadPointer(header_offset + 0x4);
                        ushort tables_num = oldovl.Read16(tables_addr);
                        tables_addr = oldovl.ReadPointer(tables_addr + 0x4);

                        ovl.WritePointer(dataoffset + 0x10, curoffset);
                        ovl.Write16(curoffset, tables_num);
                        ovl.WritePointer(curoffset + 0x04, curoffset + 0x8);
                        curoffset += 0x8;

                        uint objdata_offset = (uint)(curoffset + (tables_num * 8));
                        for (ushort t = 0; t < tables_num; t++)
                        {
                            uint tbl_part1 = oldovl.Read32(tables_addr);
                            uint tbl_addr = oldovl.ReadPointer(tables_addr + 0x4);

                            ovl.Write32(curoffset, tbl_part1);
                            ovl.WritePointer(curoffset + 0x4, objdata_offset);

                            tables_addr += 8;
                            curoffset += 8;

                            byte tbltype = (byte)(tbl_part1 & 0x1F);
                            byte numentries = (byte)((tbl_part1 >> 8) & 0xFF);

                            if ((tbltype == 13) || (tbltype > 14))
                                throw new Exception(String.Format("Wrong object table type {0} (0x{0:X2}) in level data", tbltype));

                            int[] datasizes = {16, 16, 6, 6, 14, 8, 8, 8, 8, 12, 14, 2, 2, 0, 4};
                            int datasize = numentries * datasizes[tbltype];

                            byte[] data = oldovl.ReadBlock(tbl_addr, (uint)datasize);
                            ovl.WriteBlock(objdata_offset, data);

                            objdata_offset += (uint)((datasize + 3) & ~3);
                        }

                        curoffset = objdata_offset;
                    }

                    // main object table lists
                    // and that stuff about texture scrolling
                    {
                        uint tlists_addr = oldovl.ReadPointer(header_offset + 0x10);
                        byte tlists_num = oldovl.Read8(header_offset + 0x14);

                        uint tlists_new_addr = curoffset;
                        curoffset += (uint)(tlists_num * 12);

                        ovl.WritePointer(dataoffset + 0x1C, tlists_new_addr);
                        ovl.Write8(dataoffset + 0x20, tlists_num);

                        for (byte tl = 0; tl < tlists_num; tl++)
                        {
                            uint tables_addr = oldovl.ReadPointer(tlists_addr);
                            uint texanm_addr = oldovl.ReadPointer(tlists_addr + 0x4);

                            if (tables_addr != 0xFFFFFFFF)
                            {
                                ovl.WritePointer(tlists_new_addr, curoffset);

                                ushort tables_num = oldovl.Read16(tables_addr);
                                tables_addr = oldovl.ReadPointer(tables_addr + 0x4);

                                ovl.Write16(curoffset, tables_num);
                                ovl.WritePointer(curoffset + 0x04, curoffset + 0x8);
                                curoffset += 0x8;

                                uint objdata_offset = (uint)(curoffset + (tables_num * 8));
                                for (ushort t = 0; t < tables_num; t++)
                                {
                                    uint tbl_part1 = oldovl.Read32(tables_addr);
                                    uint tbl_addr = oldovl.ReadPointer(tables_addr + 0x4);

                                    ovl.Write32(curoffset, tbl_part1);
                                    ovl.WritePointer(curoffset + 0x4, objdata_offset);

                                    tables_addr += 8;
                                    curoffset += 8;

                                    byte tbltype = (byte)(tbl_part1 & 0x1F);
                                    byte numentries = (byte)((tbl_part1 >> 8) & 0xFF);

                                    if ((tbltype == 13) || (tbltype > 14))
                                        throw new Exception(String.Format("Wrong object table type {0} (0x{0:X2}) in level data", tbltype));

                                    int[] datasizes = { 16, 16, 6, 6, 14, 8, 8, 8, 8, 12, 14, 2, 2, 0, 4 };
                                    int datasize = numentries * datasizes[tbltype];

                                    byte[] data = oldovl.ReadBlock(tbl_addr, (uint)datasize);
                                    ovl.WriteBlock(objdata_offset, data);

                                    objdata_offset += (uint)((datasize + 3) & ~3);
                                }

                                curoffset = objdata_offset;
                            }
                            else
                                ovl.Write32(tlists_new_addr, 0);

                            if (texanm_addr != 0xFFFFFFFF)//If not null
                            {
                                ovl.WritePointer(tlists_new_addr + 0x4, curoffset);

                                uint texanm_new_addr = curoffset;
                                curoffset += 0x18;

                                uint textures_addr = oldovl.ReadPointer(texanm_addr + 0x14);
                                uint numscale = 0, numrot = 0, numtrans = 0;
                                uint numtextures = oldovl.Read32(texanm_addr + 0x10);

                                ovl.Write32(texanm_new_addr, oldovl.Read32(texanm_addr));
                                ovl.Write32(texanm_new_addr + 0x10, numtextures);

                                uint textures_new_addr = curoffset;
                                curoffset += (numtextures * 0x1C);

                                ovl.WritePointer(texanm_new_addr + 0x14, textures_new_addr);
                                for (uint t = 0; t < numtextures; t++)
                                {
                                    uint tex_old_addr = textures_addr + (t * 0x1C);
                                    uint tex_new_addr = textures_new_addr + (t * 0x1C);

                                    ushort tex_scalenum = oldovl.Read16(tex_old_addr + 0x0C);
                                    ushort tex_scalestart = oldovl.Read16(tex_old_addr + 0x0E);
                                    ushort tex_rotnum = oldovl.Read16(tex_old_addr + 0x10);
                                    ushort tex_rotstart = oldovl.Read16(tex_old_addr + 0x12);
                                    ushort tex_transnum = oldovl.Read16(tex_old_addr + 0x14);
                                    ushort tex_transstart = oldovl.Read16(tex_old_addr + 0x16);

                                    if ((tex_scalestart + tex_scalenum) > numscale)
                                        numscale = (uint)(tex_scalestart + tex_scalenum);
                                    if ((tex_rotstart + tex_rotnum) > numrot)
                                        numrot = (uint)(tex_rotstart + tex_rotnum);
                                    if ((tex_transstart + tex_transnum) > numtrans)
                                        numtrans = (uint)(tex_transstart + tex_transnum);

                                    ovl.Write32(tex_new_addr, oldovl.Read32(tex_old_addr));
                                    ovl.WritePointer(tex_new_addr + 0x4, curoffset);
                                    ovl.Write32(tex_new_addr + 0x8, oldovl.Read32(tex_old_addr + 0x8));
                                    ovl.Write16(tex_new_addr + 0xC, tex_scalenum);
                                    ovl.Write16(tex_new_addr + 0xE, tex_scalestart);
                                    ovl.Write16(tex_new_addr + 0x10, tex_rotnum);
                                    ovl.Write16(tex_new_addr + 0x12, tex_rotstart);
                                    ovl.Write16(tex_new_addr + 0x14, tex_transnum);
                                    ovl.Write16(tex_new_addr + 0x16, tex_transstart);
                                    ovl.Write32(tex_new_addr + 0x18, oldovl.Read32(tex_old_addr + 0x18));

                                    string tex_matname = oldovl.ReadString(oldovl.ReadPointer(tex_old_addr + 0x4), 0);
                                    ovl.WriteString(curoffset, tex_matname, 0);
                                    curoffset += (uint)((tex_matname.Length + 3) & ~3);
                                }

                                uint scale_addr = oldovl.ReadPointer(texanm_addr + 0x4);
                                ovl.WritePointer(texanm_new_addr + 0x4, curoffset);
                                for (uint v = 0; v < numscale; v++)
                                {
                                    ovl.Write32(curoffset, oldovl.Read32(scale_addr + (v * 4)));
                                    curoffset += 4;
                                }

                                uint rot_addr = oldovl.ReadPointer(texanm_addr + 0x8);
                                ovl.WritePointer(texanm_new_addr + 0x8, curoffset);
                                for (uint v = 0; v < numrot; v++)
                                {
                                    ovl.Write16(curoffset, oldovl.Read16(rot_addr + (v * 2)));
                                    curoffset += 2;
                                }
                                curoffset = (uint)((curoffset + 3) & ~3);

                                uint trans_addr = oldovl.ReadPointer(texanm_addr + 0xC);
                                ovl.WritePointer(texanm_new_addr + 0xC, curoffset);
                                for (uint v = 0; v < numtrans; v++)
                                {
                                    ovl.Write32(curoffset, oldovl.Read32(trans_addr + (v * 4)));
                                    curoffset += 4;
                                }
                            }
                            else
                                ovl.Write32(tlists_new_addr + 0x4, 0);

                            ovl.Write32(tlists_new_addr + 0x8, oldovl.Read32(tlists_addr + 0x8));
                            tlists_new_addr += 12;
                            tlists_addr += 12;
                        }
                    }

                    // misc header pieces
                    ovl.Write32(dataoffset + 0x14, oldovl.Read32(header_offset + 0x8));
                    ovl.Write32(dataoffset + 0x18, oldovl.Read32(header_offset + 0xC));
                    ovl.Write16(dataoffset + 0x22, oldovl.Read16(header_offset + 0x16));
                    ovl.Write8(dataoffset + 0x24, oldovl.Read8(header_offset + 0x18));
                }

                ovl.SaveChanges();
                lazyman.ReportProgress(201 + (int)((98f / 54f) * i));
            }

            // fix the level loader as to load from the new overlays
            m_FileStream.Position = lvlload_addr_patch;
            m_BinWriter.Write((uint)(lvl_start + dataoffset + 0xC));

            m_FileStream.Position = lvlload_code_patch;
            m_BinWriter.Write((uint)0xE1A00000);

            // refill the old level header address table
            // to adapt it to its new usages
            m_FileStream.Position = levelptr_table;
            for (uint i = 0; i < 13; i++)
            {
                m_BinWriter.Write((uint)0xFFFFFFFF);
                m_BinWriter.Write((uint)0x00000000);
                m_BinWriter.Write((uint)0x00000000);
            }
            m_BinWriter.Write((uint)0x00000000);
            m_BinWriter.Write((uint)0x00000000);

            // fix the object banks thingy
            m_FileStream.Position = objbank_addr_patch;
            m_BinWriter.Write((uint)(levelptr_table + 0x0200009C));

            m_FileStream.Position = objbank_code_patch;
            m_BinWriter.Write((uint)0xE1A07001);

            // phew! what a goddamn long thing
            lazyman.ReportProgress(299);
        }