bool C4Record::AddFile(const char *szLocalFilename, const char *szAddAs, bool fDelete) { if (!fRecording) return false; // Streaming? if (fStreaming) { // Special stripping for streaming StdCopyStrBuf szFile(szLocalFilename); if (SEqualNoCase(GetExtension(szAddAs), "c4p")) { // Create a copy MakeTempFilename(&szFile); if (!CopyItem(szLocalFilename, szFile.getData())) return false; // Strip it if (!C4Player::Strip(szFile.getData(), true)) return false; } // Add to stream if (!StreamFile(szFile.getData(), szAddAs)) return false; // Remove temporary file if (szFile != szLocalFilename) EraseItem(szFile.getData()); } // Add to record group if (fDelete) { if (!RecordGrp.Move(szLocalFilename, szAddAs)) return false; } else { if (!RecordGrp.Add(szLocalFilename, szAddAs)) return false; } return true; }
bool C4GameSave::SaveCreateGroup(const char *szFilename, C4Group &hUseGroup) { // erase any previous item (2do: work in C4Groups?) EraseItem(szFilename); // copy from previous group? if (GetCopyScenario()) if (!ItemIdentical(Game.ScenarioFilename, szFilename)) if (!C4Group_CopyItem(Game.ScenarioFilename, szFilename)) { LogF(LoadResStr("IDS_CNS_SAVEASERROR"), szFilename); return false; } // open it if (!hUseGroup.Open(szFilename, !GetCopyScenario())) { EraseItem(szFilename); LogF(LoadResStr("IDS_CNS_SAVEASERROR"), szFilename); return false; } // done, success return true; }
C4ScenarioSection::~C4ScenarioSection() { // del following scenario sections while (pNext) { C4ScenarioSection *pDel = pNext; pNext = pNext->pNext; pDel->pNext = NULL; delete pDel; } // del temp file if (szTempFilename) { EraseItem(szTempFilename); delete szTempFilename; } // del filename if assigned if (szFilename) delete szFilename; // del name if owned if (szName != C4ScenSect_Main) delete szName; }
bool C4ScenarioSection::EnsureTempStore(bool fExtractLandscape, bool fExtractObjects) { // if it's temp store already, don't do anything if (szTempFilename) return true; // make temp filename char *szTmp = const_cast<char *>( Config.AtTempPath(szFilename ? GetFilename(szFilename) : szName)); MakeTempFilename(szTmp); // main section: extract section files from main scenario group (create group // as open dir) if (!szFilename) { if (!CreateDirectory(szTmp, NULL)) return false; C4Group hGroup; if (!hGroup.Open(szTmp, TRUE)) { EraseItem(szTmp); return false; } // extract all desired section files Game.ScenarioFile.ResetSearch(); char fn[_MAX_FNAME + 1]; *fn = 0; while (Game.ScenarioFile.FindNextEntry(C4FLS_Section, fn)) if (fExtractLandscape || !WildcardMatch(C4FLS_SectionLandscape, fn)) if (fExtractObjects || !WildcardMatch(C4FLS_SectionObjects, fn)) Game.ScenarioFile.ExtractEntry(fn, szTmp); hGroup.Close(); } else { // subsection: simply extract section from main group if (!Game.ScenarioFile.ExtractEntry(szFilename, szTmp)) return false; // delete undesired landscape/object files if (!fExtractLandscape || !fExtractObjects) { C4Group hGroup; if (hGroup.Open(szFilename)) { if (!fExtractLandscape) hGroup.Delete(C4FLS_SectionLandscape); if (!fExtractObjects) hGroup.Delete(C4FLS_SectionObjects); } } } // copy temp filename szTempFilename = new char[strlen(szTmp) + 1]; SCopy(szTmp, szTempFilename, _MAX_PATH); // done, success return true; }
bool C4Network2Res::GetStandalone(char *pTo, int32_t iMaxL, bool fSetOfficial, bool fAllowUnloadable, bool fSilent) { // already set? if (szStandalone[0]) { if (pTo) SCopy(szStandalone, pTo, iMaxL); return true; } // already tried and failed? No point in retrying if (fStandaloneFailed) return false; // not loadable? Wo won't be able to check the standalone as the core will lack the needed information. // the standalone won't be interesting in this case, anyway. if (!fSetOfficial && !Core.isLoadable()) return false; // set flag, so failure below will let future calls fail fStandaloneFailed = true; // lock file CStdLock FileLock(&FileCSec); // directory? SCopy(szFile, szStandalone, sizeof(szStandalone)-1); if (DirectoryExists(szFile)) { // size check for the directory, if allowed if (fAllowUnloadable) { uint32_t iDirSize; if (!DirSizeHelper::GetDirSize(szFile, &iDirSize, Config.Network.MaxLoadFileSize)) { if (!fSilent) LogF("Network: could not get directory size of %s!", szFile); szStandalone[0] = '\0'; return false; } if (iDirSize > uint32_t(Config.Network.MaxLoadFileSize)) { if (!fSilent) LogSilentF("Network: %s over size limit, will be marked unloadable!", szFile); szStandalone[0] = '\0'; return false; } } // log - this may take a few seconds if (!fSilent) LogF(LoadResStr("IDS_PRC_NETPACKING"), GetFilename(szFile)); // pack inplace? if (!fTempFile) { if (!pParent->FindTempResFileName(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not find free name for temporary file!"); szStandalone[0] = '\0'; return false; } if (!C4Group_PackDirectoryTo(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not pack directory!"); szStandalone[0] = '\0'; return false; } } else if (!C4Group_PackDirectory(szStandalone)) { if (!fSilent) Log("GetStandalone: could not pack directory!"); if (!SEqual(szFile, szStandalone)) EraseDirectory(szStandalone); szStandalone[0] = '\0'; return false; } // make sure directory is packed if (DirectoryExists(szStandalone)) { if (!fSilent) Log("GetStandalone: directory hasn't been packed!"); if (!SEqual(szFile, szStandalone)) EraseDirectory(szStandalone); szStandalone[0] = '\0'; return false; } // fallthru } // doesn't exist physically? if (!FileExists(szStandalone)) { // try C4Group (might be packed) if (!pParent->FindTempResFileName(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not find free name for temporary file!"); szStandalone[0] = '\0'; return false; } if (!C4Group_CopyItem(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not copy to temporary file!"); szStandalone[0] = '\0'; return false; } } // remains missing? give up. if (!FileExists(szStandalone)) { if (!fSilent) Log("GetStandalone: file not found!"); szStandalone[0] = '\0'; return false; } // do optimizations (delete unneeded entries) if (!OptimizeStandalone(fSilent)) { if (!SEqual(szFile, szStandalone)) EraseItem(szStandalone); szStandalone[0] = '\0'; return false; } // get file size size_t iSize = FileSize(szStandalone); // size limit if (fAllowUnloadable) if (iSize > uint32_t(Config.Network.MaxLoadFileSize)) { if (!fSilent) LogSilentF("Network: %s over size limit, will be marked unloadable!", szFile); szStandalone[0] = '\0'; return false; } // check if (!fSetOfficial && iSize != Core.getFileSize()) { // remove file if (!SEqual(szFile, szStandalone)) EraseItem(szStandalone); szStandalone[0] = '\0'; // sorry, this version isn't good enough :( return false; } // calc checksum uint32_t iCRC32; if (!GetFileCRC(szStandalone, &iCRC32)) { if (!fSilent) Log("GetStandalone: could not calculate checksum!"); return false; } // set / check if (!fSetOfficial && iCRC32 != Core.getFileCRC()) { // remove file, return if (!SEqual(szFile, szStandalone)) EraseItem(szStandalone); szStandalone[0] = '\0'; return false; } // we didn't fail fStandaloneFailed = false; // mark resource as loadable and safe file information Core.SetLoadable(iSize, iCRC32); // set up chunk data Chunks.SetComplete(Core.getChunkCnt()); // ok return true; }
bool C4UpdatePackage::MkUp(C4Group *pGrp1, C4Group *pGrp2, C4GroupEx *pUpGrp, bool *fModified) { // (CAUTION: pGrp1 may be nullptr - that means that there is no counterpart for Grp2 // in the base group) // compare headers if (!pGrp1 || pGrp1->EntryCRC32() != pGrp2->EntryCRC32()) *fModified = true; // set header pUpGrp->SetHead(*pGrp2); // compare entries char strItemName[_MAX_PATH], strItemName2[_MAX_PATH]; StdStrBuf EntryList; strItemName[0] = strItemName2[0] = 0; pGrp2->ResetSearch(); if (!*fModified) pGrp1->ResetSearch(); int iChangedEntries = 0; while (pGrp2->FindNextEntry("*", strItemName, nullptr, !! strItemName[0])) { // add to entry list if (!!EntryList) EntryList.AppendChar('|'); EntryList.AppendFormat("%s=%d", strItemName, pGrp2->EntryCRC32(strItemName)); // no modification detected yet? then check order if (!*fModified) { if (!pGrp1->FindNextEntry("*", strItemName2, nullptr, !! strItemName2[0])) *fModified = true; else if (!SEqual(strItemName, strItemName2)) *fModified = true; } // TODO: write DeleteEntries.txt // a child group? C4GroupEx ChildGrp2; if (ChildGrp2.OpenAsChild(pGrp2, strItemName)) { // open in Grp1 C4Group *pChildGrp1 = new C4GroupEx(); if (!pGrp1 || !pChildGrp1->OpenAsChild(pGrp1, strItemName)) { delete pChildGrp1; pChildGrp1 = nullptr; } // open group for update data C4GroupEx UpdGroup; char strTempGroupName[_MAX_FNAME + 1]; strTempGroupName[0] = 0; if (!UpdGroup.OpenAsChild(pUpGrp, strItemName)) { // create new group (may be temporary) if (C4Group_TempPath[0]) { SCopy(C4Group_TempPath,strTempGroupName,_MAX_FNAME); SAppend("~upd",strTempGroupName,_MAX_FNAME); } else SCopy("~upd",strTempGroupName,_MAX_FNAME); MakeTempFilename(strTempGroupName); if (!UpdGroup.Open(strTempGroupName, true)) { delete pChildGrp1; WriteLog("Error: could not create temp group\n"); return false; } } // do nested MkUp-search bool Modified = false; bool fSuccess = MkUp(pChildGrp1, &ChildGrp2, &UpdGroup, &Modified); // sort & close extern const char ** C4Group_SortList; UpdGroup.SortByList(C4Group_SortList, ChildGrp2.GetName()); UpdGroup.Close(false); // check entry crcs if (!pGrp1 || (pGrp1->EntryCRC32(strItemName) != pGrp2->EntryCRC32(strItemName))) Modified = true; // add group (if modified) if (fSuccess && Modified) { if (strTempGroupName[0]) if (!pUpGrp->Move(strTempGroupName, strItemName)) { WriteLog("Error: could not add modified group\n"); return false; } // copy core pUpGrp->SaveEntryCore(*pGrp2, strItemName); pUpGrp->SetSavedEntryCore(strItemName); // got a modification in a subgroup *fModified = true; iChangedEntries++; } else // delete group (do not remove groups that existed before!) if (strTempGroupName[0]) if (!EraseItem(strTempGroupName)) { WriteLog("Error: could not delete temporary directory\n"); return false; } delete pChildGrp1; } else { // compare them (size & crc32) if (!pGrp1 || pGrp1->EntrySize(strItemName) != pGrp2->EntrySize(strItemName) || pGrp1->EntryCRC32(strItemName) != pGrp2->EntryCRC32(strItemName)) { bool fCopied = false; // save core (EntryCRC32 might set additional fields) pUpGrp->SaveEntryCore(*pGrp2, strItemName); // already in update grp? if (pUpGrp->EntrySize(strItemName) != pGrp2->EntrySize(strItemName) || pUpGrp->EntryCRC32(strItemName) != pGrp2->EntryCRC32(strItemName)) { // copy it if (!C4Group_CopyEntry(pGrp2, pUpGrp, strItemName)) { WriteLog("Error: could not add changed entry to update group\n"); return false; } // set entry core pUpGrp->SetSavedEntryCore(strItemName); // modified... *fModified = true; fCopied = true; } iChangedEntries++; WriteLog("%s\\%s: update%s\n", pGrp2->GetFullName().getData(), strItemName, fCopied ? "" : " (already in group)"); } } } // write entries list (always) if (!pUpGrp->Add(C4CFN_UpdateEntries, EntryList, false, true)) { WriteLog("Error: could not save entry list!"); return false; } if (iChangedEntries > 0) WriteLog("%s: %d/%d changed (%s)\n", pGrp2->GetFullName().getData(), iChangedEntries, pGrp2->EntryCount(), *fModified ? "update" : "skip"); // success return true; }
bool C4UpdatePackage::MakeUpdate(const char *strFile1, const char *strFile2, const char *strUpdateFile, const char *strName) { #ifdef UPDATE_DEBUG char *pData; int iSize; CStdFile MyFile; MyFile.Load(strFile2, (BYTE **)&pData, &iSize, 0, true); MyFile.Create("SoIstRichtig.txt", false); MyFile.Write(pData, iSize); MyFile.Close(); MemScramble((BYTE *)pData, iSize); MyFile.Create("UndSoAuch.txt", false); MyFile.Write(pData, iSize); MyFile.Close(); #endif // open Log if (!Log.Create("Update.log")) return false; // begin message WriteLog("Source: %s\nTarget: %s\nOutput: %s\n\n", strFile1, strFile2, strUpdateFile); // open both groups C4Group Group1, Group2; if (!Group1.Open(strFile1)) { WriteLog("Error: could not open %s!\n", strFile1); return false; } if (!Group2.Open(strFile2)) { WriteLog("Error: could not open %s!\n", strFile2); return false; } // All groups to be compared need to be packed if (!Group1.IsPacked()) { WriteLog("Error: source group %s not packed!\n", strFile1); return false; } if (!Group2.IsPacked()) { WriteLog("Error: target group %s not packed!\n", strFile2); return false; } if (Group1.HasPackedMother()) { WriteLog("Error: source group %s must not have a packed mother group!\n", strFile1); return false; } if (Group2.HasPackedMother()) { WriteLog("Error: target group %s must not have a packed mother group!\n", strFile2); return false; } // create/open update-group C4GroupEx UpGroup; if (!UpGroup.Open(strUpdateFile, true)) { WriteLog("Error: could not open %s!\n", strUpdateFile); return false; } // may be continued update-file -> try to load core UpGrpCnt = 0; bool fContinued = C4UpdatePackageCore::Load(UpGroup); // save crc2 for later check unsigned int iOldChks2 = GrpChks2; // create core info if (strName) SCopy(strName, Name, C4MaxName); else sprintf(Name, "%s Update", GetFilename(strFile1)); SCopy(strFile1, DestPath, _MAX_PATH); GrpUpdate = true; if (!GetFileCRC(strFile1, &GrpChks1[UpGrpCnt])) { WriteLog("Error: could not calc checksum for %s!\n", strFile1); return false; } if (!GetFileCRC(strFile2, &GrpChks2)) { WriteLog("Error: could not calc checksum for %s!\n", strFile2); return false; } if (fContinued) { // continuation check: GrpChks2 matches? if (GrpChks2 != iOldChks2) // that would mess up the update result... { WriteLog("Error: could not add to update package - target groups don't match (checksum error)\n"); return false; } // already supported by this update? int i = 0; for (; i < UpGrpCnt; i++) if (GrpChks1[UpGrpCnt] == GrpChks1[i]) break; if (i < UpGrpCnt) { WriteLog("This update already supports the version of the source file.\n"); return false; } } UpGrpCnt++; // save core if (!C4UpdatePackageCore::Save(UpGroup)) { WriteLog("Could not save update package core!\n"); return false; } // compare groups, create update bool fModified = false; bool fSuccess = MkUp(&Group1, &Group2, &UpGroup, &fModified); // close (save) it UpGroup.Close(false); // error? if (!fSuccess) { WriteLog("Update package not created.\n"); EraseItem(strUpdateFile); return false; } WriteLog("Update package created.\n"); return true; }