///////////////////////////////////////////////////////////
    // Function type:  I/O
    // Contributors:   Pokedude
    // Last edit by:   Pokedude
    // Date of edit:   6/4/2016
    //
    ///////////////////////////////////////////////////////////
    bool WildPokemonArea::read(const qboy::Rom &rom, UInt32 offset, UInt32 amount)
    {
        rom.seek(offset); // exception-safe this time

        // Reads the probability and the pointer to the encounter array
        m_Probability = rom.readByte();
        rom.readBytes(3); // padding
        m_Offset =  rom.readPointer();


        // Determines whether the encounter array offset is valid
        if (!rom.seek(m_Offset))
            AME_THROW(WPST_ERROR_DATA, offset + 4);


        // Reads the encounter array
        for (UInt32 i = 0; i < amount; i++)
        {
            WildPokemonEncounter *entry = new WildPokemonEncounter;
            entry->min = rom.readByte();
            entry->max = rom.readByte();
            entry->id = rom.readHWord();
            m_Entries.push_back(entry);
        }


        // Loading successful
        return true;
    }
    ///////////////////////////////////////////////////////////
    // Function type:  I/O
    // Contributors:   Pokedude
    // Last edit by:   Pokedude
    // Date of edit:   6/11/2016
    //
    ///////////////////////////////////////////////////////////
    bool MapHeader::read(const qboy::Rom &rom, UInt32 offset)
    {
        if (!rom.seek(offset))
            AME_THROW(HDR_ERROR_OFFSET, rom.redirected());


        // Reads the map's dimensions
        m_Width = rom.readWord();
        m_Height = rom.readWord();

        // Reads all the pointers within the structure
        m_PtrBorder = rom.readPointerRef();
        m_PtrBlocks = rom.readPointer();
        m_PtrPrimary = rom.readPointerRef();
        m_PtrSecondary = rom.readPointerRef();

        // Determines whether these pointers are valid
        if (!rom.checkOffset(m_PtrBorder))
            AME_THROW(HDR_ERROR_BORDER, offset + 8);
        if (!rom.checkOffset(m_PtrBlocks))
            AME_THROW(HDR_ERROR_BLOCKS, offset + 12);
        if (!rom.checkOffset(m_PtrPrimary))
            AME_THROW(HDR_ERROR_PRIMARY, offset + 16);
        if (!rom.checkOffset(m_PtrSecondary))
            AME_THROW(HDR_ERROR_SECONDARY, offset + 20);


        // Retrieves the border (different between games!)
        if (rom.info().isFRLG())
        {
            QSize borderSize(rom.readByte(), rom.readByte());
            m_Border.read(rom, m_PtrBorder, borderSize);
        }
        else
        {
            m_Border.read(rom, m_PtrBorder, QSize(2, 2));
        }

        // Retrieves the map block data
        rom.seek(m_PtrBlocks);
        for (unsigned i = 0; i < m_Width * m_Height; i++)
        {
            MapBlock *block = new MapBlock;
            UInt16 data = rom.readHWord();

            block->block = (data & 0x3FF);
            block->permission = (data >> 0xA);
            m_Blocks.push_back(block);
        }

        // Loads the tilesets, if necessary
        if ((m_Primary = TilesetManager::get(m_PtrPrimary)) == NULL)
        {
            m_Primary = new Tileset;
            m_Primary->read(rom, m_PtrPrimary);
            TilesetManager::add(m_Primary);
        }
        if ((m_Secondary = TilesetManager::get(m_PtrSecondary)) == NULL)
        {
            m_Secondary = new Tileset;
            m_Secondary->read(rom, m_PtrSecondary);
            TilesetManager::add(m_Secondary);
        }


        // Loading successful
        return true;
    }
    ///////////////////////////////////////////////////////////
    // Member functions
    //
    ///////////////////////////////////////////////////////////
    const QString String::read(const qboy::Rom &rom, UInt32 offset)
    {
        // Firstly, determines whether the given rom is valid
        Q_ASSERT(rom.info().isLoaded() && rom.info().isValid());

        // Declares needed variables for the decoding process
        QList<UInt8> encoded;
        QString decoded;
        UInt8 readByte;

        // Declares the different dynamic tables (some are the same across all roms)
        QMap<UInt32, QString> *mapBuffers = NULL;
        QMap<UInt32, QString> *mapFunctions = NULL;

        // Determines the rom version and depending on that, loads the tables
        if (CONFIG(RomType) == RT_FRLG)
        {
            mapBuffers = &BufferSequencesFRLG;
            mapFunctions = &FunctionSequencesFRLG;
        }
        else if (CONFIG(RomType) == RT_RS)
        {
            mapBuffers = &BufferSequencesRSE;
            mapFunctions = &FunctionSequencesRS;
        }
        else
        {
            mapBuffers = &BufferSequencesRSE;
            mapFunctions = &FunctionSequencesE;
        }


        // Reads the whole Pokémon string, terminated by 0xFF
        if (!rom.seek(offset))
            Q_ASSERT(false);

        while ((readByte = rom.readByte()) != 0xFF)
            encoded.push_back(readByte);

        // Iterates through every encoded character and interprets it
        int length = encoded.size();
        for (int i = 0; i < length;)
        {
            // Fetches the char at the current position
            UInt8 currentChar = encoded.at(i++);
            if (currentChar == 0xF8 || currentChar == 0xF9)
            {
                // Character might be a symbol
                UInt8 arg1 = encoded[i++];
                UInt32 search = ((currentChar << 8) | arg1);

                // Searches for the sequence in the symbol-map
                auto searchResult = SymbolSequences.find(search);
                if (searchResult != SymbolSequences.end())
                    decoded.push_back(searchResult.value());
                else
                    decoded.push_back(convertRaw(search));
            }
            else if (currentChar == 0xFD)
            {
                // Character might be a buffer
                UInt8 arg1 = encoded.at(i++);
                UInt32 search = ((currentChar << 8) | arg1);

                // Searches for the sequence in the dynamic buffer-map
                auto searchResult = mapBuffers->find(search);
                if (searchResult != mapBuffers->end())
                    decoded.push_back(searchResult.value());
                else
                    decoded.push_back(convertRaw(search));
            }
            else if (currentChar == 0xFC)
            {
                // Character might be an escape sequence
                UInt8 arg1 = encoded.at(i++);
                UInt32 search = 0;
                if (arg1 >= 1 && arg1 <= 6)
                {
                    // Might be a multi-byte function
                    UInt8 arg2 = encoded[i++];
                    search = ((currentChar << 16) | (arg1 << 8) | arg2);
                }
                else
                {
                    // Might be a single-byte function
                    search = ((currentChar << 8) | arg1);
                }

                // Searches for the sequence in the dynamic escape-map
                auto searchResult = mapFunctions->find(search);
                if (searchResult != mapFunctions->end())
                    decoded.push_back(searchResult.value());
                else
                    decoded.push_back(convertRaw(search));
            }
            else
            {
                // Is a single character for sure (exception-safe!)
                decoded.push_back(SingleSequences.value(currentChar));
            }
        }

        // Finished
        return decoded;
    }
    ///////////////////////////////////////////////////////////
    // Function type:  I/O
    // Contributors:   Pokedude
    // Last edit by:   Pokedude
    // Date of edit:   6/15/2016
    //
    ///////////////////////////////////////////////////////////
    bool MapBank::read(const qboy::Rom &rom, UInt32 offset, UInt32 next)
    {
        if (!rom.seek(offset))
            AME_THROW(BNK_ERROR_OFFSET, rom.redirected());


        // User can choose whether to retrieve the map-count
        // on a fast or accurate way (horiz. slider)
        // Level 0: Checks for next bank and ptr validity +
        // Level 1: Checks for ptr validity within the map header +
        // Level 2: Checks for ptr validity within the layout header +
        // Level 3: Checks for ptr validity within the events +
        // Level 4: Checks certain properties in tilesets and events
        int accLevel = SETTINGS(MapAccuracyLevel);

        // Retrieves the map count through various checks
        while (true)
        {
            if (!rom.seek(offset + (++m_Count) * 4))
                AME_THROW(BNK_ERROR_WHILE, offset + m_Count * 4);

            // =============  LEVEL 0  =============
            // Checks if current offset is start of bank table
            if (rom.offset() == CONFIG(MapBanks))
                break;

            // Checks if current offset is next bank
            if (rom.offset() == next)
                break;

            // Checks if map pointer is valid
            unsigned map = rom.readPointer();
            if (!rom.seek(map))
                break;


            // =============  LEVEL 1  =============
            if (accLevel == 0)
                continue;

            // Checks if the layout header and event pointers are valid
            unsigned footer = rom.readPointer();
            if (!rom.checkOffset(footer))
                break;

            unsigned events = rom.readPointer();
            if (!rom.checkOffset(events))
                break;


            // =============  LEVEL 2  =============
            if (accLevel == 1)
                continue;

            // Checks if the border, blocks, primary and secondary pointers are valid
            rom.seek(footer);
            rom.readWord();
            rom.readWord();
            if (!rom.checkOffset(rom.readPointer()))
                break;

            if (!rom.checkOffset(rom.readPointer()))
                break;

            unsigned primary = rom.readPointer();
            if (!rom.checkOffset(primary))
                break;

            unsigned secondary = rom.readPointer();
            if (!rom.checkOffset(secondary))
                break;


            // =============  LEVEL 3  =============
            if (accLevel == 2)
                continue;

            // Checks if the npc, warp, trigger and sign pointers are valid
            // Events are not necessary for a map
            if (events)
            {
                rom.seek(events);
                rom.readWord();
                if (!rom.checkOffset(rom.readPointer()))
                    break;
    
                if (!rom.checkOffset(rom.readPointer()))
                    break;
    
                if (!rom.checkOffset(rom.readPointer()))
                    break;
    
                if (!rom.checkOffset(rom.readPointer()))
                    break;
            }


            // =============  LEVEL 4  =============
            if (accLevel == 3)
                continue;

            // Checks the first two tileset settings
            rom.seek(primary);
            if (rom.readByte() >= 2) // compression can only be 0/1
                break;
            if (rom.readByte() != 0) // must be primary tileset!
                break;

            rom.seek(secondary);
            if (rom.readByte() >= 2) // compression can only be 0/1
                break;
            if (rom.readByte() != 1) // must be secondary tileset!
                break;
        }


        // Now attempts to read all the maps
        for (int i = 0; i < m_Count; i++)
        {
            rom.seek(offset + i * 4);

            // Retrieves the pointer to the map
            Map *map = new Map;
            UInt32 mapOff = rom.readPointerRef();

            // Attempts to read the map
            if (!map->read(rom, mapOff))
            {
                delete map;
                return false;
            }

            m_Maps.push_back(map);
        }


        // Loading successful
        m_Offset = offset;
        return true;
    }
    ///////////////////////////////////////////////////////////
    // Function type:  I/O
    // Contributors:   Pokedude, Diegoisawesome
    // Last edit by:   Diegoisawesome
    // Date of edit:   7/3/2016
    //
    ///////////////////////////////////////////////////////////
    bool MapLayoutTable::read(const qboy::Rom &rom, UInt32 offset)
    {
        if (!rom.seek(offset))
            AME_THROW(LAY_ERROR_OFFSET, rom.redirected());


        // Checks for ptr validity within the layout header and
        // certain properties in tilesets

        // Retrieves the map count through various checks
        while (true)
        {
            if (!rom.seek(offset + (++m_Count) * 4))
                AME_THROW(LAY_ERROR_WHILE, offset + m_Count * 4);

            unsigned layout = rom.readPointer();
            if (layout == 0)
                continue;

            if (!rom.seek(layout))
                break;

            // Checks if the border, blocks, primary and secondary pointers are valid
            rom.readWord();
            rom.readWord();
            if (!rom.checkOffset(rom.readPointer()))
                break;

            if (!rom.checkOffset(rom.readPointer()))
                break;

            unsigned primary = rom.readPointer();
            if (!rom.checkOffset(primary))
                break;

            unsigned secondary = rom.readPointer();
            if (!rom.checkOffset(secondary))
                break;
/*
            // Checks the first two tileset settings
            rom.seek(primary);
            if (rom.readByte() >= 2) // compression can only be 0/1
                break;
            if (rom.readByte() != 0) // must be primary tileset!
                break;

            rom.seek(secondary);
            if (rom.readByte() >= 2) // compression can only be 0/1
                break;
            if (rom.readByte() != 1) // must be secondary tileset!
                break;*/
        }


        // Now attempts to read all the maps
        for (int i = 0; i < m_Count; i++)
        {
            rom.seek(offset + i * 4);

            // Retrieves the pointer to the map
            MapHeader *mapHeader = new MapHeader;
            UInt32 mapOff = rom.readPointerRef();

            // Attempts to read the map layout
            if (mapOff != 0 && !mapHeader->read(rom, mapOff))
            {
                delete mapHeader;
                return false;
            }

            m_MapHeaders.push_back(mapHeader);
        }


        // Loading successful
        m_Offset = offset;
        return true;
    }
Beispiel #6
0
    ///////////////////////////////////////////////////////////
    // Function type:  I/O
    // Contributers:   Pokedude
    // Last edit by:   Pokedude
    // Date of edit:   6/5/2016
    //
    ///////////////////////////////////////////////////////////
    bool Tileset::read(const qboy::Rom &rom, UInt32 offset)
    {
        if (!rom.seek(offset))
            AME_THROW(SET_ERROR_OFFSET, rom.redirected());


        // Reads the first two properties for the following determination
        m_IsCompressed = rom.readByte();
        m_IsPrimary = rom.readByte();
        rom.readHWord(); // padding


        // Determines all differences between FRLG and RSE
        int countPal;
        int countBlock;
        int palAdjustment;
        int uncompSize;
        if (rom.info().isFRLG())
        {
            if (!m_IsPrimary) // = 0 in the games -.-
            {
                countPal = 7;
                countBlock = 640;
                palAdjustment = 0;
                uncompSize = 40960;
            }
            else
            {
                countPal = 6;
                countBlock = 384;
                palAdjustment = 224;
                uncompSize = 24576;
            }
        }
        else
        {
            if (!m_IsPrimary) // = 0 in the games -.-
            {
                countPal = 6;
                countBlock = 512;
                palAdjustment = 0;
                uncompSize = 32768;
            }
            else
            {
                countPal = 7;
                countBlock = 512;
                palAdjustment = 192;
                uncompSize = 32768;
            }
        }


        // Reads all pointers within the tileset structure
        m_PtrImage = rom.readPointer();
        m_PtrPalette = rom.readPointer();
        m_PtrBlocks = rom.readPointer();

        if (rom.info().isFRLG()) // pointers reversed
        {
            m_PtrAnimations = rom.readPointer();
            m_PtrBehaviour = rom.readPointer();
        }
        else
        {
            m_PtrBehaviour = rom.readPointer();
            m_PtrAnimations = rom.readPointer();
        }


        // Determines whether all read pointers are valid
        if (!rom.checkOffset(m_PtrImage))
            AME_THROW(SET_ERROR_IMAGE, offset + 4);
        if (!rom.checkOffset(m_PtrPalette))
            AME_THROW(SET_ERROR_PALETTE, offset + 8);
        if (!rom.checkOffset(m_PtrBlocks))
            AME_THROW(SET_ERROR_BLOCKS, offset + 12);
        if (!rom.checkOffset(m_PtrAnimations))
            AME_THROW(SET_ERROR_ANIM, offset + 16);
        if (!rom.checkOffset(m_PtrBehaviour))
            AME_THROW(SET_ERROR_PROP, offset + 20);


        // Attempts to load the image
        if (m_IsCompressed)
        {
            if (!m_Image->readCompressed(rom, m_PtrImage, 128, true))
                AME_THROW(SET_ERROR_IMGDATA, m_PtrImage);
        }
        else
        {
            if (!m_Image->readUncompressed(rom, m_PtrImage, uncompSize, 128, true))
                AME_THROW(SET_ERROR_IMGDATA, m_PtrImage);
        }

        // Attempts to load the palettes
        for (int i = 0; i < countPal; i++)
        {
            qboy::Palette *pal = new qboy::Palette;
            pal->readUncompressed(rom, m_PtrPalette + palAdjustment + i * 32, 16);
            m_Pals.push_back(pal);
        }

        // Attempts to load the blocks
        rom.seek(m_PtrBlocks);
        for (int i = 0; i < countBlock; i++)
        {
            Block *block = new Block;
            m_Blocks.push_back(block);

            // Each block has 2 layers á 4 tiles
            for (int j = 0; j < 8; j++)
            {
                Tile tile;
                UInt16 data = rom.readHWord();

                // Extracts information from the hword
                tile.tile = (data & 0x3FF);
                tile.palette = ((data & 0xF800) >> 0xC);
                tile.flipX = ((data & 0x400) == 0x400);
                tile.flipY = ((data & 0x800) == 0x800);
                block->tiles[j] = tile;
            }
        }


        // Attempts to load the block properties
        m_Properties->read(rom, m_PtrBehaviour, countBlock);


        // Loading successful
        m_Offset = offset;
        return true;
    }