bool C4PlayerInfoCore::Load(C4Group &hGroup) { // New version StdStrBuf Source; if (hGroup.LoadEntryString(C4CFN_PlayerInfoCore,&Source)) { // Compile StdStrBuf GrpName = hGroup.GetFullName(); GrpName.Append(DirSep C4CFN_PlayerInfoCore); if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Source, GrpName.getData())) return false; // Pref for AutoContextMenus is still undecided: default by player's control style if (OldPrefAutoContextMenu == -1) OldPrefAutoContextMenu = OldPrefControlStyle; // Determine true color from indexed pref color if (!PrefColorDw) PrefColorDw = GetPrefColorValue(PrefColor); // Validate colors PrefColorDw &= 0xffffff; PrefColor2Dw &= 0xffffff; // Validate name C4Markup::StripMarkup(PrefName); // Success return true; } // Old version no longer supported - sorry return false; }
bool C4ParticleDef::Load(C4Group &group) { // store file Filename.Copy(group.GetFullName()); // load char *particle_source; if (group.LoadEntry(C4CFN_ParticleCore,&particle_source,NULL,1)) { if (!Compile(particle_source, Filename.getData())) { DebugLogF("invalid particle def at '%s'", group.GetFullName().getData()); delete [] particle_source; return false; } delete [] particle_source; // load graphics if (!Gfx.Load(group, C4CFN_DefGraphics, C4FCT_Full, C4FCT_Full, false, C4SF_MipMap)) { DebugLogF("particle %s has no valid graphics defined", Name.getData()); return false; } // set facet, if assigned - otherwise, assume full surface if (GfxFace.Wdt) Gfx.Set(Gfx.Surface, GfxFace.x, GfxFace.y, GfxFace.Wdt, GfxFace.Hgt); // set phase num int32_t Q; Gfx.GetPhaseNum(PhasesX, Q); Length = PhasesX * Q; if (!Length) { DebugLogF("invalid facet for particle '%s'", Name.getData()); return false; } // calc aspect Aspect=(float) Gfx.Hgt/Gfx.Wdt; // particle overloading C4ParticleDef *def_overload; if ((def_overload = Particles.definitions.GetDef(Name.getData(), this))) { if (Config.Graphics.VerboseObjectLoading >= 1) { char ostr[250]; sprintf(ostr,LoadResStr("IDS_PRC_DEFOVERLOAD"),def_overload->Name.getData(),"<particle>"); Log(ostr); } delete def_overload; } // success return true; } return false; }
BOOL C4PlayerInfoCore::Save(C4Group &hGroup) { StdStrBuf Source, Name = hGroup.GetFullName(); Name.Append(DirSep C4CFN_PlayerInfoCore); if(!DecompileToBuf_Log<StdCompilerINIWrite>(*this, &Source, Name.getData())) return FALSE; if (!hGroup.Add(C4CFN_PlayerInfoCore,Source,FALSE,TRUE)) return FALSE; hGroup.Delete("C4Player.c4b"); return TRUE; }
bool C4PlayerInfoCore::Save(C4Group &hGroup) { StdStrBuf Source, Name = hGroup.GetFullName(); Name.Append(DirSep C4CFN_PlayerInfoCore); if (!DecompileToBuf_Log<StdCompilerINIWrite>(*this, &Source, Name.getData())) return false; if (!hGroup.Add(C4CFN_PlayerInfoCore,Source,false,true)) return false; hGroup.Delete("C4Player.ocb"); return true; }
bool C4VectorFont::Init(C4Group &hGrp, const char *szFilename, C4Config &rCfg) { // name by file Name.Copy(GetFilenameOnly(szFilename)); #if defined(_WIN32) && !defined(HAVE_FREETYPE) // check whether group is directory or packed if (!hGrp.IsPacked()) { // it's open: use the file directly SCopy(hGrp.GetFullName().getData(), FileName, _MAX_PATH); AppendBackslash(FileName); SAppend(szFilename, FileName); if (!FileExists(FileName)) { *FileName=0; return false; } fIsTempFile = false; } else { // it's packed: extract to temp path SCopy(rCfg.AtTempPath(szFilename), FileName, _MAX_PATH); // make sure the filename is not in use, in case multiple instances of the engine are run if (FileExists(FileName)) { RemoveExtension(FileName); StdStrBuf sNewFilename; for (int i=0; i<1000; ++i) { sNewFilename.Format("%s%x", FileName, (int)rand()); if (*GetExtension(szFilename)) { sNewFilename.AppendChar('.'); sNewFilename.Append(GetExtension(szFilename)); } if (!FileExists(sNewFilename.getData())) break; } SCopy(sNewFilename.getData(), FileName, _MAX_PATH); } if (!hGrp.ExtractEntry(szFilename, FileName)) { *FileName=0; return false; } fIsTempFile = true; } // add the font resource //if (!AddFontResourceEx(FileName, FR_PRIVATE, NULL)) requires win2k if (!AddFontResource(FileName)) { if (fIsTempFile) EraseFile(FileName); *FileName='\0'; return false; } #else if (!hGrp.LoadEntry(szFilename, Data)) return false; #endif // success return true; }
bool C4Language::CloseGroup(const char *strPath) { // Check all open language packs C4Group *pPack; for (int iPack = 0; (pPack = Packs.GetGroup(iPack)); iPack++) if (ItemIdentical(strPath, pPack->GetFullName().getData())) { Packs.UnregisterGroup(iPack); return true; } // No pack of that path return false; }
BOOL C4ComponentHost::LoadEx(const char *szName, C4Group &hGroup, const char *szFilename, const char *szLanguage) { // Load from a group set containing the provided group and // alternative groups for cross-loading from a language pack C4GroupSet hGroups; hGroups.RegisterGroup( hGroup, false, 1000, C4GSCnt_Component); // Provided group gets highest priority hGroups.RegisterGroups(Languages.GetPackGroups(pConfig->AtExeRelativePath( hGroup.GetFullName().getData())), C4GSCnt_Language); // Load from group set return Load(szName, hGroups, szFilename, szLanguage); }
bool C4Shader::LoadSlices(C4GroupSet *pGroups, const char *szFile) { // Search for our shaders C4Group *pGroup = pGroups->FindEntry(szFile); if(!pGroup) return false; // Load it, save the path for later reloading StdStrBuf Shader; if(!pGroup->LoadEntryString(szFile, &Shader)) return false; // If it physically exists, save back creation time so we // can automatically reload it if it changes StdStrBuf Source = FormatString("%s" DirSep "%s", pGroup->GetFullName().getData(), szFile); int iSourceTime = 0; if(FileExists(Source.getData())) iSourceTime = FileTime(Source.getData()); // Load StdStrBuf What = FormatString("file %s", Config.AtRelativePath(Source.getData())); AddFragmentSlices(What.getData(), Shader.getData(), Source.getData(), iSourceTime); return true; }
bool C4MusicSystem::InitForScenario(C4Group & hGroup) { // check if the scenario contains music bool fLocalMusic = false; StdStrBuf MusicDir; if (GrpContainsMusic(hGroup)) { // clear global songs ClearSongs(); fLocalMusic = true; // add songs MusicDir.Take(Game.ScenarioFile.GetFullName()); LoadDir(MusicDir.getData()); // log LogF(LoadResStr("IDS_PRC_LOCALMUSIC"), MusicDir.getData()); } // check for music folders in group set C4Group *pMusicFolder = NULL; while ((pMusicFolder = Game.GroupSet.FindGroup(C4GSCnt_Music, pMusicFolder))) { if (!fLocalMusic) { // clear global songs ClearSongs(); fLocalMusic = true; } // add songs MusicDir.Take(pMusicFolder->GetFullName()); MusicDir.AppendChar(DirectorySeparator); MusicDir.Append(C4CFN_Music); LoadDir(MusicDir.getData()); // log LogF(LoadResStr("IDS_PRC_LOCALMUSIC"), MusicDir.getData()); } // no music? if (!SongCount) return false; // set play list SetPlayList(0); // ok return true; }
bool C4Def::LoadDefCore(C4Group &hGroup) { StdStrBuf Source; if (hGroup.LoadEntryString(C4CFN_DefCore,&Source)) { StdStrBuf Name = hGroup.GetFullName() + (const StdStrBuf &)FormatString("%cDefCore.txt", DirectorySeparator); if (!Compile(Source.getData(), Name.getData())) return false; Source.Clear(); // Check mass if (Mass < 0) { DebugLogF("WARNING: Def %s (%s) at %s has invalid mass!", GetName(), id.ToString(), hGroup.GetFullName().getData()); Mass = 0; } return true; } return false; }
bool C4Def::LoadSolidMask(C4Group &hGroup) { if (hGroup.FindEntry(C4CFN_SolidMask)) { pSolidMask = C4SolidMask::LoadMaskFromFile(hGroup, C4CFN_SolidMask); if (!pSolidMask) { DebugLogF(" Error loading SolidMask of %s (%s)", hGroup.GetFullName().getData(), id.ToString()); return false; } // check SolidMask size if (SolidMask.x<0 || SolidMask.y<0 || SolidMask.x + SolidMask.Wdt>pSolidMask->Wdt || SolidMask.y + SolidMask.Hgt>pSolidMask->Hgt) SolidMask.Default(); } else if (SolidMask.Wdt) { // Warning in case someone wants to define SolidMasks the old way (in the main graphics file) DebugLogF("WARNING: Definition %s (%s) defines SolidMask in DefCore but has no SolidMask file!", hGroup.GetFullName().getData(), id.ToString()); SolidMask.Default(); } return true; }
bool C4MaterialCore::Load(C4Group &hGroup, const char *szEntryName) { StdStrBuf Source; if (!hGroup.LoadEntryString(szEntryName,&Source)) return false; StdStrBuf Name = hGroup.GetFullName() + DirSep + szEntryName; if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Source, Name.getData())) return false; // adjust placement, if not specified if (!Placement) { if (DensitySolid(Density)) { Placement=30; if (!DigFree) Placement+=20; if (!BlastFree) Placement+=10; } else if (DensityLiquid(Density)) Placement=10; else Placement=5; } return true; }
bool C4DefGraphics::LoadBitmap(C4Group &hGroup, const char *szFilename, const char *szOverlay, const char *szNormal, bool fColorByOwner) { if (!szFilename) return false; Type = TYPE_Bitmap; // will be reset to TYPE_None in Clear() if loading fails Bmp.Bitmap = new C4Surface(); if (!Bmp.Bitmap->Load(hGroup, szFilename, false, true, C4SF_MipMap)) { Clear(); return false; } // Create owner color bitmaps if (fColorByOwner) { // Create additionmal bitmap Bmp.BitmapClr=new C4Surface(); // if overlay-surface is present, load from that if (szOverlay && Bmp.BitmapClr->Load(hGroup, szOverlay, false, false, C4SF_MipMap)) { // set as Clr-surface, also checking size if (!Bmp.BitmapClr->SetAsClrByOwnerOf(Bmp.Bitmap)) { DebugLogF(" Gfx loading error in %s: %s (%d x %d) doesn't match overlay %s (%d x %d) - invalid file or size mismatch", hGroup.GetFullName().getData(), szFilename, Bmp.Bitmap ? Bmp.Bitmap->Wdt : -1, Bmp.Bitmap ? Bmp.Bitmap->Hgt : -1, szOverlay, Bmp.BitmapClr->Wdt, Bmp.BitmapClr->Hgt); Clear(); return false; } } else { // otherwise, create by all blue shades if (!Bmp.BitmapClr->CreateColorByOwner(Bmp.Bitmap)) { Clear(); return false; } } fColorBitmapAutoCreated = true; } if (szNormal) { Bmp.BitmapNormal = new C4Surface(); if (Bmp.BitmapNormal->Load(hGroup, szNormal, false, true, C4SF_MipMap)) { // Normal map loaded. Sanity check and link. if(Bmp.BitmapNormal->Wdt != Bmp.Bitmap->Wdt || Bmp.BitmapNormal->Hgt != Bmp.Bitmap->Hgt) { DebugLogF(" Gfx loading error in %s: %s (%d x %d) doesn't match normal %s (%d x %d) - invalid file or size mismatch", hGroup.GetFullName().getData(), szFilename, Bmp.Bitmap ? Bmp.Bitmap->Wdt : -1, Bmp.Bitmap ? Bmp.Bitmap->Hgt : -1, szNormal, Bmp.BitmapNormal->Wdt, Bmp.BitmapNormal->Hgt); Clear(); return false; } Bmp.Bitmap->pNormalSfc = Bmp.BitmapNormal; if(Bmp.BitmapClr) Bmp.BitmapClr->pNormalSfc = Bmp.BitmapNormal; } else { // No normal map delete Bmp.BitmapNormal; Bmp.BitmapNormal = NULL; } } Type = TYPE_Bitmap; // success return true; }
C4GroupSet C4Language::GetPackGroups(C4Group & hGroup) { // Build a group set containing the provided group and // alternative groups for cross-loading from a language pack char strRelativePath[_MAX_PATH + 1]; char strTargetLocation[_MAX_PATH + 1]; char strPackPath[_MAX_PATH + 1]; char strPackGroupLocation[_MAX_PATH + 1]; char strAdvance[_MAX_PATH + 1]; // Store wanted target location SCopy(Config.AtRelativePath(hGroup.GetFullName().getData()), strRelativePath, _MAX_PATH); SCopy(strRelativePath, strTargetLocation, _MAX_PATH); // Adjust location by scenario origin if (Game.C4S.Head.Origin.getLength() && SEqualNoCase(GetExtension(Game.C4S.Head.Origin.getData()), "ocs")) { const char *szScenarioRelativePath = GetRelativePathS(strRelativePath, Config.AtRelativePath(Game.ScenarioFilename)); if (szScenarioRelativePath != strRelativePath) { // this is a path within the scenario! Change to origin. size_t iRestPathLen = SLen(szScenarioRelativePath); if (Game.C4S.Head.Origin.getLength() + 1 + iRestPathLen <= _MAX_PATH) { SCopy(Game.C4S.Head.Origin.getData(), strTargetLocation); if (iRestPathLen) { SAppendChar(DirectorySeparator, strTargetLocation); SAppend(szScenarioRelativePath, strTargetLocation); } } } } // Process all language packs (and their respective pack groups) C4Group *pPack, *pPackGroup; for (int iPack = 0; (pPack = Packs.GetGroup(iPack)) && (pPackGroup = PackGroups.GetGroup(iPack)); iPack++) { // Get current pack group position within pack SCopy(pPack->GetFullName().getData(), strPackPath, _MAX_PATH); GetRelativePath(pPackGroup->GetFullName().getData(), strPackPath, strPackGroupLocation); // Pack group is at correct position within pack: continue with next pack if (SEqualNoCase(strPackGroupLocation, strTargetLocation)) continue; // Try to backtrack until we can reach the target location as a relative child while ( strPackGroupLocation[0] && !GetRelativePath(strTargetLocation, strPackGroupLocation, strAdvance) && pPackGroup->OpenMother() ) { // Update pack group location GetRelativePath(pPackGroup->GetFullName().getData(), strPackPath, strPackGroupLocation); } // We can reach the target location as a relative child if (strPackGroupLocation[0] && GetRelativePath(strTargetLocation, strPackGroupLocation, strAdvance)) { // Advance pack group to relative child pPackGroup->OpenChild(strAdvance); } // Cannot reach by advancing: need to close and reopen (rewinding group file) else { // Close pack group (if it is open at all) pPackGroup->Close(); // Reopen pack group to relative position in language pack if possible pPackGroup->OpenAsChild(pPack, strTargetLocation); } } // Store new target location SCopy(strTargetLocation, PackGroupLocation, _MAX_FNAME); C4GroupSet r; // Provided group gets highest priority r.RegisterGroup(hGroup, false, 1000, C4GSCnt_Component); // register currently open pack groups r.RegisterGroups(PackGroups, C4GSCnt_Language); return r; }
bool C4ObjectInfo::Save(C4Group &hGroup, bool fStoreTiny, C4DefList *pDefs) { // Set group file name; rename if necessary char szTempGroup[_MAX_PATH+1]; SCopy(Name, szTempGroup, _MAX_PATH); MakeFilenameFromTitle(szTempGroup); SAppend(".oci",szTempGroup, _MAX_PATH); if (!SEqualNoCase(Filename, szTempGroup)) { if (!Filename[0]) { // first time creation of file - make sure it's not a duplicate SCopy(szTempGroup, Filename, _MAX_PATH); while (hGroup.FindEntry(Filename)) { // if a crew info of that name exists already, rename! RemoveExtension(Filename); int32_t iFinNum = GetTrailingNumber(Filename), iLen = SLen(Filename); while (iLen && Inside(Filename[iLen-1], '0', '9')) --iLen; if (iLen>_MAX_PATH-22) { LogF("Error generating unique filename for %s(%s): Path overflow", Name, hGroup.GetFullName().getData()); break; } snprintf(Filename+iLen, 22, "%d", iFinNum+1); EnforceExtension(Filename, "oci"); } } else { // Crew was renamed; file rename necessary, if the name is not blocked by another crew info if (!hGroup.FindEntry(szTempGroup)) { if (hGroup.Rename(Filename, szTempGroup)) SCopy(szTempGroup, Filename, _MAX_PATH); else { // could not rename. Not fatal; just use old file LogF("Error adjusting crew info for %s into %s: Rename error from %s to %s!", Name, hGroup.GetFullName().getData(), Filename, szTempGroup); } } } } // Open group C4Group hTemp; if (!hTemp.OpenAsChild(&hGroup, Filename, false, true)) return false; // custom rank image present? if (pDefs && !fStoreTiny) { C4Def *pDef = pDefs->ID2Def(id); if (pDef) { if (pDef->pRankSymbols) { C4FacetSurface fctRankSymbol; if (C4RankSystem::DrawRankSymbol(&fctRankSymbol, Rank, pDef->pRankSymbols, pDef->iNumRankSymbols, true)) { fctRankSymbol.GetFace().SavePNG(hTemp, C4CFN_ClonkRank); } } else { // definition does not have custom rank symbols: Remove any rank image from Clonk hTemp.Delete(C4CFN_ClonkRank); } } } // Save info to temp group if (!C4ObjectInfoCore::Save(hTemp, pDefs)) { hTemp.Close(); return false; } // Close temp group hTemp.Close(); // Success return true; }
bool C4Def::Load(C4Group &hGroup, StdMeshSkeletonLoader &loader, DWORD dwLoadWhat, const char *szLanguage, C4SoundSystem *pSoundSystem, C4DefGraphicsPtrBackup *gfx_backup ) { bool AddFileMonitoring = false; if (Game.pFileMonitor && !SEqual(hGroup.GetFullName().getData(),Filename) && !hGroup.IsPacked()) AddFileMonitoring = true; // Store filename SCopy(hGroup.GetFullName().getData(),Filename); // Verbose log filename if (Config.Graphics.VerboseObjectLoading>=3) Log(hGroup.GetFullName().getData()); if (AddFileMonitoring) Game.pFileMonitor->AddDirectory(Filename); // Pre-read all images and shader stuff because they ar eaccessed in unpredictable order during loading hGroup.PreCacheEntries(C4CFN_ShaderFiles); hGroup.PreCacheEntries(C4CFN_ImageFiles); LoadMeshMaterials(hGroup, gfx_backup); bool fSuccess = LoadParticleDef(hGroup); // Read DefCore if (fSuccess) fSuccess = LoadDefCore(hGroup); // Skip def: don't even read sounds! if (fSuccess && Game.C4S.Definitions.SkipDefs.GetIDCount(id, 1)) return false; // Read sounds, even if not a valid def (for pure ocd sound folders) if (dwLoadWhat & C4D_Load_Sounds) LoadSounds(hGroup, pSoundSystem); // cancel if not a valid definition if (!fSuccess) return false; // Read and parse SolidMask bitmap if (!LoadSolidMask(hGroup)) return false; // Read surface bitmap, meshes, skeletons if ((dwLoadWhat & C4D_Load_Bitmap) && !LoadGraphics(hGroup, loader)) return false; // Read string table C4Language::LoadComponentHost(&StringTable, hGroup, C4CFN_ScriptStringTbl, szLanguage); // Register ID with script engine ::ScriptEngine.RegisterGlobalConstant(id.ToString(), C4VPropList(this)); ParentKeyName = ::Strings.RegString(id.ToString()); // Read script if (dwLoadWhat & C4D_Load_Script) LoadScript(hGroup, szLanguage); // Read clonknames if (dwLoadWhat & C4D_Load_ClonkNames) LoadClonkNames(hGroup, pClonkNames, szLanguage); // Read clonkranks if (dwLoadWhat & C4D_Load_RankNames) LoadRankNames(hGroup, szLanguage); // Read rankfaces if (dwLoadWhat & C4D_Load_RankFaces) LoadRankFaces(hGroup); // Temporary flag if (dwLoadWhat & C4D_Load_Temporary) Temporary=true; return true; }
// Construct full path void C4ComponentHost::CopyFilePathFromGroup(const C4Group &hGroup) { SCopy(pConfig->AtExeRelativePath(hGroup.GetFullName().getData()), FilePath, _MAX_PATH - 1); SAppendChar(DirectorySeparator, FilePath); SAppend(Filename, FilePath, _MAX_PATH); }
BOOL C4Playback::Open(C4Group &rGrp) { // clean up Clear(); fLoadSequential = false; iLastSequentialFrame = 0; bool fStrip = false; // get text record file StdStrBuf TextBuf; if (rGrp.LoadEntryString(C4CFN_CtrlRecText, TextBuf)) { if (!ReadText(TextBuf)) return FALSE; } else { // open group? Then do some sequential reading for large files // Can't do this when a dump is forced, because the dump needs all data // Also can't do this when stripping is desired if (!rGrp.IsPacked()) if (!Game.RecordDumpFile.getLength()) if (!fStrip) fLoadSequential = true; // get record file if (fLoadSequential) { if (!rGrp.FindEntry(C4CFN_CtrlRec)) return FALSE; if (!playbackFile.Open( FormatString("%s%c%s", rGrp.GetFullName().getData(), (char)DirectorySeparator, (const char *)C4CFN_CtrlRec).getData())) return FALSE; // forcing first chunk to be read; will call ReadBinary currChunk = chunks.end(); if (!NextSequentialChunk()) { // empty replay??! LogFatal("Record: Binary read error."); return FALSE; } } else { // non-sequential reading: Just read as a whole StdBuf BinaryBuf; if (rGrp.LoadEntry(C4CFN_CtrlRec, BinaryBuf)) { if (!ReadBinary(BinaryBuf)) return FALSE; } else { // file too large? Try sequential loading and parsing /* size_t iSize; if (rGrp.AccessEntry(C4CFN_CtrlRec, &iSize)) { CStdFile fOut; fOut.Create(Game.RecordDumpFile.getData()); fLoadSequential = true; const size_t iChunkSize = 1024*1024*16; // 16M while (iSize) { size_t iLoadSize = Min<size_t>(iChunkSize, iSize); BinaryBuf.SetSize(iLoadSize); if (!rGrp.Read(BinaryBuf.getMData(), iLoadSize)) { LogFatal("Record: Binary load error!"); return FALSE; } iSize -= iLoadSize; if (!ReadBinary(BinaryBuf)) return FALSE; LogF("%d binary remaining", iSize); currChunk = chunks.begin(); if (fStrip) Strip(); StdStrBuf s(ReWriteText()); fOut.WriteString(s.getData()); LogF("Wrote %d text bytes (%d binary remaining)", s.getLength(), iSize); chunks.clear(); } fOut.Close(); fLoadSequential = false; } else*/ { // no control data? LogFatal("Record: No control data found!"); return FALSE; } } } } // rewrite record if (fStrip) Strip(); if (Game.RecordDumpFile.getLength()) { if (SEqualNoCase(GetExtension(Game.RecordDumpFile.getData()), "txt")) ReWriteText().SaveToFile(Game.RecordDumpFile.getData()); else ReWriteBinary().SaveToFile(Game.RecordDumpFile.getData()); } // reset status currChunk = chunks.begin(); Finished = false; // external debugrec file #if defined(DEBUGREC_EXTFILE) && defined(DEBUGREC) #ifdef DEBUGREC_EXTFILE_WRITE if (!DbgRecFile.Create(DEBUGREC_EXTFILE)) { LogFatal("DbgRec: Creation of external file \"" DEBUGREC_EXTFILE "\" failed!"); return FALSE; } else Log("DbgRec: Writing to \"" DEBUGREC_EXTFILE "\"..."); #else if (!DbgRecFile.Open(DEBUGREC_EXTFILE)) { LogFatal("DbgRec: Opening of external file \"" DEBUGREC_EXTFILE "\" failed!"); return FALSE; } else Log("DbgRec: Checking against \"" DEBUGREC_EXTFILE "\"..."); #endif #endif // ok return TRUE; }
int C4GameObjects::Load(C4Group &hGroup, bool fKeepInactive) { Clear(!fKeepInactive); // Load data component StdStrBuf Source; if (!hGroup.LoadEntryString(C4CFN_ScenarioObjects, Source)) return 0; // Compile StdStrBuf Name = hGroup.GetFullName() + DirSep C4CFN_ScenarioObjects; if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(*this, false), Source, Name.getData())) return 0; // Process objects C4ObjectLink *cLnk = nullptr; C4Object *pObj = nullptr; bool fObjectNumberCollision = false; int32_t iMaxObjectNumber = 0; for (cLnk = Last; cLnk; cLnk = cLnk->Prev) { C4Object *pObj = cLnk->Obj; // check object number collision with inactive list if (fKeepInactive) { for (C4ObjectLink *clnk = InactiveObjects.First; clnk; clnk = clnk->Next) if (clnk->Obj->Number == pObj->Number) fObjectNumberCollision = true; } // keep track of numbers iMaxObjectNumber = Max<long>(iMaxObjectNumber, pObj->Number); // add to list of backobjects if (pObj->Category & C4D_Background) Game.BackObjects.Add(pObj, C4ObjectList::stMain, this); // add to list of foreobjects if (pObj->Category & C4D_Foreground) Game.ForeObjects.Add(pObj, C4ObjectList::stMain, this); // Unterminate end } // Denumerate pointers // if object numbers collideded, numbers will be adjusted afterwards // so fake inactive object list empty meanwhile C4ObjectLink *pInFirst = nullptr; if (fObjectNumberCollision) { pInFirst = InactiveObjects.First; InactiveObjects.First = NULL; } // denumerate pointers Denumerate(); // update object enumeration index now, because calls like UpdateTransferZone // might create objects Game.ObjectEnumerationIndex = Max(Game.ObjectEnumerationIndex, iMaxObjectNumber); // end faking and adjust object numbers if (fObjectNumberCollision) { InactiveObjects.First = pInFirst; // simply renumber all inactive objects for (cLnk = InactiveObjects.First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) pObj->Number = ++Game.ObjectEnumerationIndex; } // special checks: // -contained/contents-consistency // -StaticBack-objects zero speed for (cLnk = First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) { // staticback must not have speed if (pObj->Category & C4D_StaticBack) { pObj->xdir = pObj->ydir = 0; } // contained must be in contents list if (pObj->Contained) if (!pObj->Contained->Contents.GetLink(pObj)) { DebugLogF( "Error in Objects.txt: Container of #%d is #%d, but not found in " "contents list!", pObj->Number, pObj->Contained->Number); pObj->Contained->Contents.Add(pObj, C4ObjectList::stContents); } // all contents must have contained set; otherwise, remove them! C4Object *pObj2; for (C4ObjectLink *cLnkCont = pObj->Contents.First; cLnkCont; cLnkCont = cLnkCont->Next) { // check double links if (pObj->Contents.GetLink(cLnkCont->Obj) != cLnkCont) { DebugLogF("Error in Objects.txt: Double containment of #%d by #%d!", cLnkCont->Obj->Number, pObj->Number); // this remove-call will only remove the previous (dobuled) link, so // cLnkCont should be save pObj->Contents.Remove(cLnkCont->Obj); // contents checked already continue; } // check contents/contained-relation if ((pObj2 = cLnkCont->Obj)->Status) if (pObj2->Contained != pObj) { DebugLogF( "Error in Objects.txt: Object #%d not in container #%d as " "referenced!", pObj2->Number, pObj->Number); pObj2->Contained = pObj; } } } // sort out inactive objects C4ObjectLink *cLnkNext; for (cLnk = First; cLnk; cLnk = cLnkNext) { cLnkNext = cLnk->Next; if (cLnk->Obj->Status == C4OS_INACTIVE) { if (cLnk->Prev) cLnk->Prev->Next = cLnkNext; else First = cLnkNext; if (cLnkNext) cLnkNext->Prev = cLnk->Prev; else Last = cLnk->Prev; if (cLnk->Prev = InactiveObjects.Last) InactiveObjects.Last->Next = cLnk; else InactiveObjects.First = cLnk; InactiveObjects.Last = cLnk; cLnk->Next = NULL; Mass -= pObj->Mass; } } { C4DebugRecOff DBGRECOFF; // - script callbacks that would kill // DebugRec-sync for runtime start // update graphics UpdateGraphics(false); // Update faces UpdateFaces(false); // Update ocf SetOCF(); } // make sure list is sorted by category - after sorting out inactives, because // inactives aren't sorted into the main list FixObjectOrder(); // Sectors.Dump(); // misc updates for (cLnk = First; cLnk; cLnk = cLnk->Next) if ((pObj = cLnk->Obj)->Status) { // add to plrview pObj->PlrFoWActualize(); // update flipdir (for old objects.txt with no flipdir defined) // assigns Action.DrawDir as well pObj->UpdateFlipDir(); } // Done return ObjectCount(); }