bool FalloutESM::ParseTextureSets(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader record; ParseRecordHeader(substream, record); if (record.Tag != ESMTag::TXST) { mLoadMessages.push_back("Error: encountered a non-texture set record in the texture set group"); return false; } ESMStream recordStream(substream, record.Size); TextureSet set(record.Meta.AsRecord.FormID); if (set.Parse(recordStream) == false) { mLoadMessages.push_back("Error: encountered a malformed texture set record"); return false; } mTextures.insert(std::pair<FormIdentifier, TextureSet>(record.Meta.AsRecord.FormID, std::move(set))); } return true; }
bool FalloutESM::ParseStatics(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader header; ParseRecordHeader(substream, header); if (header.Tag != ESMTag::STAT) { mLoadMessages.push_back("Error: encountered an invalid record in the static object group"); return false; } ESMStream recordStream(substream, header.Size); StaticObject object(StaticObjectType::Static, header.Meta.AsRecord.FormID); if (object.Parse(recordStream) == false) { mLoadMessages.push_back("Error: error parsing a static object record"); return false; } mStaticObjects.insert(std::pair<FormIdentifier, StaticObject>(header.Meta.AsRecord.FormID, std::move(object))); mEntityTypeMap.insert(std::pair<FormIdentifier, ESMTag>(header.Meta.AsRecord.FormID, ESMTag::STAT)); } return true; }
bool FalloutESM::ParseWorldCellGroup(ESMStream& stream, FormIdentifier parentWorld) { auto worldItr = mWorldspaces.find(parentWorld); if (worldItr == mWorldspaces.end()) { mLoadMessages.push_back("Error: trying to add entites to a non-existent world"); return false; } Worldspace& world = (*worldItr).second; while(stream.IsValid() == true) { RecordHeader header; ParseRecordHeader(stream, header); switch(header.Tag) { case ESMTag::GRUP: { if (ParseWorldInnerGroup(stream, world, header, 0, 0) == false) { mLoadMessages.push_back("Error: unable to parse world cell group"); return false; } break; } case ESMTag::CELL: { ESMStream cellStream(stream, header.Size); Cell cell(header.Meta.AsRecord.FormID, -1, -1); if (cell.Parse(cellStream) == false) { mLoadMessages.push_back("Error: invalid cell record in the world group"); return false; } mCells.insert(std::pair<FormIdentifier, Cell>(header.Meta.AsRecord.FormID, std::move(cell))); world.GetCellsWritable().push_back(header.Meta.AsRecord.FormID); break; } default: mLoadMessages.push_back("Error: invalid tag inside the world cell group"); return false; } } return true; }
bool TextureSet::Parse(ESMStream& stream) { while(stream.IsValid() == true) { FieldHeader fieldHeader; stream.ReadFieldHeader(fieldHeader); switch(fieldHeader.Tag) { case ESMTag::EDID: FieldParser::ParseEDIDField(stream, fieldHeader.Size, mEditorId); break; case ESMTag::OBND: FieldParser::ParseOBDNField(stream, fieldHeader.Size, mBounds); break; case ESMTag::TX00: case ESMTag::TX01: case ESMTag::TX02: case ESMTag::TX03: case ESMTag::TX04: case ESMTag::TX05: case ESMTag::TX06: case ESMTag::TX07: { std::string textureName; FieldParser::ParseTXNNField(stream, fieldHeader.Size, textureName); mTextureNames.push_back(std::move(textureName)); break; } case ESMTag::DODT: FieldParser::ParseDODTField(stream, fieldHeader.Size, mDecalData); break; case ESMTag::DNAM: stream.Read16((uint16_t&)mFlags); break; default: return false; } } return true; }
bool FalloutScript::Parse(ESMStream& stream) { while(stream.IsValid() == true) { FieldHeader header; stream.ReadFieldHeader(header); switch(header.Tag) { case ESMTag::EDID: FieldParser::ParseEDIDField(stream, header.Size, mEditorID); break; case ESMTag::SCHR: FieldParser::ParseSCHRField(stream, header.Size, mScriptHeader); break; case ESMTag::SCDA: FieldParser::ParseBinaryField(stream, header.Size, mScriptBytecode); break; case ESMTag::SCTX: FieldParser::ParseEDIDField(stream, header.Size, mScriptSource); break; case ESMTag::SCRO: { FormIdentifier id = 0; FieldParser::ParseFormID(stream, header.Size, id); mReferences.push_back(id); break; } case ESMTag::SCVR: case ESMTag::SCRV: //TODO: This is undocumented, fixed uint32_t field case ESMTag::SLSD: //TODO: Handle these stream.Skip(header.Size); break; default: return false; } } return true; }
bool FalloutESM::ParseCellExteriorGroup(ESMStream& stream, Worldspace& world, int x, int y, bool isblock) { while(stream.IsValid() == true) { RecordHeader header; ParseRecordHeader(stream, header); switch(header.Tag) { case ESMTag::GRUP: { if (ParseWorldInnerGroup(stream, world, header, x, y) == false) { mLoadMessages.push_back("Error: unable to parse world exterior cell group"); return false; } break; } case ESMTag::CELL: { ESMStream recordStream(stream, header.Size); Cell cell(header.Meta.AsRecord.FormID, x, y, isblock); if (cell.Parse(recordStream) == false) { mLoadMessages.push_back("Error: invalid cell record in the world group"); return false; } mCells.insert(std::pair<FormIdentifier, Cell>(header.Meta.AsRecord.FormID, std::move(cell))); world.GetCellsWritable().push_back(header.Meta.AsRecord.FormID); break; } default: { mLoadMessages.push_back("Error: invalid tag inside a world exterior cell group"); break; } } } return true; }
bool FalloutESM::ParseWorlds(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader header; ParseRecordHeader(substream, header); switch(header.Tag) { case ESMTag::GRUP: { ESMStream groupStream(substream, header.Size - sizeof(RecordHeader)); if (ParseWorldCellGroup(groupStream, header.Meta.AsGroup.Label.AsFormIdentifier) == false) { mLoadMessages.push_back("Error: unable to parse world cell group"); return false; } break; } case ESMTag::WRLD: { ESMStream worldStream(substream, header.Size); Worldspace world(header.Meta.AsRecord.FormID); if (world.Parse(worldStream) == false) { mLoadMessages.push_back("Error: invalid world record"); return false; } mWorldspaces.insert(std::pair<FormIdentifier, Worldspace>(header.Meta.AsRecord.FormID, std::move(world))); break; } default: return false; } } return true; }
bool LandscapeTextureSet::Parse(ESMStream& stream) { while(stream.IsValid() == true) { FieldHeader header; stream.ReadFieldHeader(header); switch(header.Tag) { case ESMTag::EDID: FieldParser::ParseEDIDField(stream, header.Size, mEditorId); break; case ESMTag::ICON: FieldParser::ParseEDIDField(stream, header.Size, mLargeIconFilename); break; case ESMTag::MICO: FieldParser::ParseEDIDField(stream, header.Size, mSmallIconFilename); break; case ESMTag::TNAM: FieldParser::ParseFormID(stream, header.Size, mTextureIdentifier); break; case ESMTag::HNAM: FieldParser::ParseHavokData(stream, header.Size, mHavokData); break; case ESMTag::SNAM: FieldParser::ParseByteValue(stream, header.Size, mSpecularExponent); break; case ESMTag::GNAM: FieldParser::ParseFormID(stream, header.Size, mGrassIdentifier); break; default: return false; } } return true; }
bool FalloutESM::ParseCells(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader header; ParseRecordHeader(substream, header); if (header.Tag != ESMTag::GRUP) { mLoadMessages.push_back("Error: found a non-group record in the top level cell structure"); return false; } //These are all groups of type 2 (interior cell block) ESMStream groupStream(substream, header.Size - sizeof(RecordHeader)); if (ParseCellGroup(substream, -1, -1) == false) { return false; } } return true; }
bool FalloutESM::ParseCellGroup(ESMStream& stream, int block, int subblock) { while(stream.IsValid() == true) { RecordHeader header; ParseRecordHeader(stream, header); switch(header.Tag) { case ESMTag::GRUP: { if (ParseCellInnerGroup(stream, header, block, subblock) == false) { mLoadMessages.push_back("Error: unable to parse cell group"); return false; } break; } case ESMTag::CELL: { ESMStream cellStream(stream, header.Size); Cell cell(header.Meta.AsRecord.FormID, block, subblock); if (cell.Parse(cellStream) == false) { mLoadMessages.push_back("Error: invalid cell record"); return false; } mCells.insert(std::pair<FormIdentifier, Cell>(header.Meta.AsRecord.FormID, std::move(cell))); break; } default: return false; } } return true; }
bool FalloutESM::ParseScripts(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader record; ParseRecordHeader(substream, record); if (record.Tag != ESMTag::SCPT) { mLoadMessages.push_back("Error: encountered a non-script record in the script group"); return false; } ESMStream recordStream(substream, record.Size); FalloutScript script(record.Meta.AsRecord.FormID); if (script.Parse(recordStream) == false) { mLoadMessages.push_back("Error: malformed script record"); return false; } mScripts.insert(std::pair<FormIdentifier, FalloutScript>(record.Meta.AsRecord.FormID, std::move(script))); } return true; }
bool FalloutESM::ParseLandTextureSets(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader record; ParseRecordHeader(substream, record); if (record.Tag != ESMTag::LTEX) { mLoadMessages.push_back("Error: encountered a non-land-texture set record"); return false; } ESMStream recordStream(substream, record.Size); LandscapeTextureSet textureSet(record.Meta.AsRecord.FormID); if (textureSet.Parse(recordStream) == false) { mLoadMessages.push_back("Error: malformed land-texture set record"); return false; } mLandTextures.insert(std::pair<FormIdentifier, LandscapeTextureSet>(record.Meta.AsRecord.FormID, std::move(textureSet))); } return true; }
bool FalloutESM::ParseGameSettings(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader record; ParseRecordHeader(substream, record); if (record.Tag != ESMTag::GMST) { mLoadMessages.push_back("Error: encountered a non-GMST tag in the game settings group"); return false; } ESMStream recordStream(substream, record.Size); GameSetting setting(record.Meta.AsRecord.FormID); if (setting.Parse(recordStream) == false) { mLoadMessages.push_back("Error: malformed GMST record"); return false; } mSettings.insert(std::pair<FormIdentifier, GameSetting>(record.Meta.AsRecord.FormID, std::move(setting))); } return true; }
bool FalloutESM::ParseMessages(ESMStream& substream) { while(substream.IsValid() == true) { RecordHeader header; ParseRecordHeader(substream, header); if (header.Tag != ESMTag::MESG) { mLoadMessages.push_back("Error: encountered an invalid record in the messages group"); return false; } ESMStream recordStream(substream, header.Size); Message message(header.Meta.AsRecord.FormID); if (message.Parse(recordStream) == false) { mLoadMessages.push_back("Error: error parsing a message record"); return false; } mMessages.insert(std::pair<FormIdentifier, Message>(header.Meta.AsRecord.FormID, std::move(message))); } return true; }
bool FalloutESM::ParseCellChildren(FormIdentifier cellID, CellChildType childType, ESMStream& stream, int block, int subblock) { auto cellItr = mCells.find(cellID); // = mCells[cellID]; if (cellItr == mCells.end()) { mLoadMessages.push_back("Error: tried to add children for a non-existent cell"); return false; } Cell& cell = (*cellItr).second; while(stream.IsValid() == true) { RecordHeader header; ParseRecordHeader(stream, header); switch(header.Tag) { // Static objects case ESMTag::REFR: { ESMStream substream(stream, header.Size); WorldObject obj(WorldObjectType::Static, header.Meta.AsRecord.FormID); if (obj.Parse(substream) == false) { mLoadMessages.push_back("Error: invalid REFR record"); return false; } cell.AddObject(childType, obj); break; } // Creatures case ESMTag::ACRE: { ESMStream substream(stream, header.Size); WorldObject obj(WorldObjectType::Creature, header.Meta.AsRecord.FormID); if (obj.Parse(substream) == false) { mLoadMessages.push_back("Error: invalid ACRE record"); return false; } cell.AddObject(childType,obj); break; } // Grenades case ESMTag::PGRE: { ESMStream substream(stream, header.Size); substream.Skip(substream.GetSize()); break; } // Missiles case ESMTag::PMIS: { ESMStream substream(stream, header.Size); substream.Skip(substream.GetSize()); break; } // Characters case ESMTag::ACHR: { ESMStream substream(stream, header.Size); WorldObject obj(WorldObjectType::Character, header.Meta.AsRecord.FormID); if (obj.Parse(substream) == false) { mLoadMessages.push_back("Error: invalid ACHR record"); return false; } cell.AddObject(childType, obj); break; } case ESMTag::GRUP: { if (ParseCellChildrenGroup(stream, header, block, subblock) == false) { mLoadMessages.push_back("Error: unable to parse cell children"); return false; } break; } case ESMTag::NAVM: { ESMStream substream(stream, header.Size); substream.Skip(substream.GetSize()); break; } case ESMTag::LAND: { ESMStream substream(stream, header.Size); uint32_t decompSize; substream.Read32(decompSize); const size_t sourceSize = header.Size - 4; mBuffer.reserve(sourceSize); mDecompressedBuffer.reserve(decompSize); substream.ReadRaw((char *)&mBuffer[0], sourceSize); if (ESMUtility::ZlibDecompress(mBuffer, mDecompressedBuffer, sourceSize, decompSize) == false) { mLoadMessages.push_back("Error: unable to decompress zlib LAND data"); //TODO: There is a single land entry in FalloutNV.esm that appears corrupted, skip it break; } //This is fairly unpleasant but functions std::stringstream dataStream; dataStream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(&mDecompressedBuffer[0]), decompSize); ESMStream decompressedStream(stream, dataStream, decompSize); LandDefinition landDefinition(header.Meta.AsRecord.FormID); if (landDefinition.Parse(decompressedStream) == false) { mLoadMessages.push_back("Error: invalid LAND record"); return false; } cell.SetLandscapeID(header.Meta.AsRecord.FormID); mLandscapes.insert(std::pair<FormIdentifier, LandDefinition>(header.Meta.AsRecord.FormID, std::move(landDefinition))); break; } default: std::cout << "Found unhandled tag: " << ESMUtility::TagToString(header.Tag) << " " << header.Size << std::endl; break; } } return true; }