void TextureManager::initialize() { VFS::IStreamPtr stream = VFS::Manager::get().open("PAL.PAL"); std::streamsize len = 0; if(stream && stream->seekg(0, std::ios_base::end)) { len = stream->tellg(); stream->seekg(0); } if(len == 776) { len -= 8; stream->ignore(8); } if(len != sizeof(mCurrentPalette)) throw std::runtime_error("Invalid palette size (expected 768 or 776 bytes)"); stream->read(reinterpret_cast<char*>(mCurrentPalette.data()), sizeof(mCurrentPalette)); }
DFAFile::DFAFile(const std::string &filename, const Palette &palette) : pixels() { VFS::IStreamPtr stream = VFS::Manager::get().open(filename.c_str()); Debug::check(stream != nullptr, "DFAFile", "Could not open \"" + filename + "\"."); stream->seekg(0, std::ios::end); const auto fileSize = stream->tellg(); stream->seekg(0, std::ios::beg); std::vector<uint8_t> srcData(fileSize); stream->read(reinterpret_cast<char*>(srcData.data()), srcData.size()); // Read DFA header data. const uint16_t imageCount = Bytes::getLE16(srcData.data()); const uint16_t unknown1 = Bytes::getLE16(srcData.data() + 2); const uint16_t unknown2 = Bytes::getLE16(srcData.data() + 4); const uint16_t width = Bytes::getLE16(srcData.data() + 6); const uint16_t height = Bytes::getLE16(srcData.data() + 8); const uint16_t compressedLength = Bytes::getLE16(srcData.data() + 10); // First frame. // Frame data with palette indices. std::vector<std::vector<uint8_t>> frames; // Uncompress the initial frame. frames.push_back(std::vector<uint8_t>(width * height)); Compression::decodeRLE(srcData.data() + 12, width * height, frames.at(0)); // Make copies of the original frame for each update chunk. for (int i = 1; i < imageCount; ++i) { frames.push_back(frames.at(0)); } // Offset to the beginning of the chunk data; advances as the chunk data is read. uint32_t offset = 12 + compressedLength; // Start reading chunks for each update group. Skip the first frame because // that's the full image. for (uint32_t frameIndex = 1; frameIndex < imageCount; ++frameIndex) { // Select the frame buffer at the current frame index. std::vector<uint8_t> &frame = frames.at(frameIndex); // Pointer to the beginning of the chunk data. Each update chunk // changes a group of pixels in a copy of the original image. const uint8_t *chunkData = srcData.data() + offset; const uint16_t chunkSize = Bytes::getLE16(chunkData); const uint16_t chunkCount = Bytes::getLE16(chunkData + 2); // Move the offset past the chunk header. offset += 4; for (uint32_t chunkIndex = 0; chunkIndex < chunkCount; ++chunkIndex) { const uint8_t *updateData = srcData.data() + offset; const uint16_t updateOffset = Bytes::getLE16(updateData); const uint16_t updateCount = Bytes::getLE16(updateData + 2); // Move the offset past the update header. offset += 4; for (uint32_t i = 0; i < updateCount; ++i) { frame.at(updateOffset + i) = *(srcData.begin() + offset); offset++; } } } this->width = width; this->height = height; // Finally, create 32-bit images using each frame's palette indices. for (const auto &frame : frames) { this->pixels.push_back(std::unique_ptr<uint32_t>( new uint32_t[this->width * this->height])); uint32_t *dstPixels = this->pixels.at(this->pixels.size() - 1).get(); std::transform(frame.begin(), frame.begin() + frame.size(), dstPixels, [&palette](uint8_t col) -> uint32_t { return palette[col].toARGB(); }); } }
void World::initialize(osgViewer::Viewer *viewer, osg::Group *sceneroot) { std::set<std::string> names = VFS::Manager::get().list("MAPNAMES.[0-9]*"); if(names.empty()) throw std::runtime_error("Failed to find any regions"); VFS::IStreamPtr stream; for(const std::string &name : names) { size_t pos = name.rfind('.'); if(pos == std::string::npos) continue; std::string regstr = name.substr(pos+1); unsigned long regnum = std::stoul(regstr, nullptr, 10); /* Get names */ stream = VFS::Manager::get().open(name.c_str()); if(!stream) throw std::runtime_error("Failed to open "+name); uint32_t mapcount = VFS::read_le32(*stream); if(mapcount == 0) continue; MapRegion region; region.mNames.resize(mapcount); for(std::string &mapname : region.mNames) { char mname[32]; if(!stream->read(mname, sizeof(mname)) || stream->gcount() != sizeof(mname)) throw std::runtime_error("Failed to read map names from "+name); mapname.assign(mname, sizeof(mname)); size_t end = mapname.find('\0'); if(end != std::string::npos) mapname.resize(end); } stream = nullptr; /* Get table data */ std::string fname = "MAPTABLE."+regstr; stream = VFS::Manager::get().open(fname.c_str()); if(!stream) throw std::runtime_error("Failed to open "+fname); region.mTable.resize(region.mNames.size()); for(MapTable &maptable : region.mTable) { maptable.mMapId = VFS::read_le32(*stream); maptable.mUnknown1 = stream->get(); maptable.mLongitudeType = VFS::read_le32(*stream); maptable.mLatitude = VFS::read_le16(*stream); maptable.mUnknown2 = VFS::read_le16(*stream); maptable.mUnknown3 = VFS::read_le32(*stream); } stream = nullptr; /* Get exterior data */ fname = "MAPPITEM."+regstr; stream = VFS::Manager::get().open(fname.c_str()); if(!stream) throw std::runtime_error("Failed to open "+fname); std::vector<uint32_t> extoffsets(region.mNames.size()); for(uint32_t &offset : extoffsets) offset = VFS::read_le32(*stream); std::streamoff extbase_offset = stream->tellg(); uint32_t *extoffset = extoffsets.data(); region.mExteriors.resize(extoffsets.size()); for(ExteriorLocation &extinfo : region.mExteriors) { stream->seekg(extbase_offset + *extoffset); extinfo.load(*stream); ++extoffset; } stream = nullptr; /* Get dungeon data */ fname = "MAPDITEM."+regstr; stream = VFS::Manager::get().open(fname.c_str()); if(!stream) throw std::runtime_error("Failed to open "+fname); DungeonHeader dheader; dheader.load(*stream); std::streamoff dbase_offset = stream->tellg(); DungeonHeader::Offset *doffset = dheader.mOffsets.data(); region.mDungeons.resize(dheader.mDungeonCount); for(DungeonInterior &dinfo : region.mDungeons) { stream->seekg(dbase_offset + doffset->mOffset); dinfo.load(*stream); if(dinfo.mExteriorLocationId != doffset->mExteriorLocationId) throw std::runtime_error("Dungeon exterior location id mismatch for "+std::string(dinfo.mLocationName)+": "+ std::to_string(dinfo.mExteriorLocationId)+" / "+std::to_string(doffset->mExteriorLocationId)); ++doffset; } stream = nullptr; if(regnum >= mRegions.size()) mRegions.resize(regnum+1); mRegions[regnum] = std::move(region); } loadPakList("CLIMATE.PAK", mClimates); loadPakList("POLITIC.PAK", mPolitics); mViewer = viewer; mSceneRoot = sceneroot; }