/////////////////////////////////////////////////////////// // 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; }
/////////////////////////////////////////////////////////// // 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; }