BOOL C4UpdatePackage::Optimize(C4Group *pGroup, const char *strTarget) { // Open target group C4GroupEx TargetGrp; if (!TargetGrp.Open(strTarget)) return FALSE; // Both groups must be packed if (!pGroup->IsPacked() || !TargetGrp.IsPacked()) { TargetGrp.Close(FALSE); return FALSE; } // update children char ItemFileName[_MAX_PATH]; pGroup->ResetSearch(); while (pGroup->FindNextEntry("*", ItemFileName)) if (!SEqual(ItemFileName, C4CFN_UpdateCore) && !SEqual(ItemFileName, C4CFN_UpdateEntries)) Optimize(pGroup, &TargetGrp, ItemFileName); // set header if (TargetGrp.HeadIdentical(*pGroup, true)) TargetGrp.SetHead(*pGroup); // save TargetGrp.Close(FALSE); // okay return TRUE; }
bool C4UpdatePackage::DoUpdate(C4Group *pGrpFrom, C4GroupEx *pGrpTo, const char *strFileName) { // group file? C4Group ItemGroupFrom; if (ItemGroupFrom.OpenAsChild(pGrpFrom, strFileName)) { // try to open target group C4GroupEx ItemGroupTo; char strTempGroup[_MAX_PATH+1]; strTempGroup[0] = 0; if (!ItemGroupTo.OpenAsChild(pGrpTo, strFileName, false, true)) return false; // update children char ItemFileName[_MAX_PATH]; ItemGroupFrom.ResetSearch(); while (ItemGroupFrom.FindNextEntry("*", ItemFileName)) if (!SEqual(ItemFileName, C4CFN_UpdateCore) && !SEqual(ItemFileName, C4CFN_UpdateEntries)) DoUpdate(&ItemGroupFrom, &ItemGroupTo, ItemFileName); if (GrpUpdate) { DoGrpUpdate(&ItemGroupFrom, &ItemGroupTo); // write group (do not change any headers set by DoGrpUpdate!) ItemGroupTo.Close(false); // set core (C4Group::Save overwrites it) pGrpTo->SaveEntryCore(*pGrpFrom, strFileName); pGrpTo->SetSavedEntryCore(strFileName); // flag as no-resort pGrpTo->SetNoSort(strFileName); } else { // write group ItemGroupTo.Close(true); // temporary group? if (strTempGroup[0]) if (!pGrpTo->Move(strTempGroup, strFileName)) return false; } } else { #ifdef _WIN32 OutputDebugString(FormatString("updating %s\\%s\n", pGrpTo->GetFullName().getData(), strFileName).GetWideChar()); #elif defined(_DEBUG) printf("updating %s\\%s\n", pGrpTo->GetFullName().getData(), strFileName); #endif if (!C4Group_CopyEntry(pGrpFrom, pGrpTo, strFileName)) return false; // set core pGrpTo->SaveEntryCore(*pGrpFrom, strFileName); pGrpTo->SetSavedEntryCore(strFileName); } // ok return true; }
BOOL C4UpdatePackage::Optimize(C4Group *pGrpFrom, C4GroupEx *pGrpTo, const char *strFileName) { // group file? C4Group ItemGroupFrom; if (!ItemGroupFrom.OpenAsChild(pGrpFrom, strFileName)) return TRUE; // try to open target group C4GroupEx ItemGroupTo; char strTempGroup[_MAX_PATH + 1]; strTempGroup[0] = 0; if (!ItemGroupTo.OpenAsChild(pGrpTo, strFileName)) return TRUE; // update children char ItemFileName[_MAX_PATH]; ItemGroupFrom.ResetSearch(); while (ItemGroupFrom.FindNextEntry("*", ItemFileName)) Optimize(&ItemGroupFrom, &ItemGroupTo, ItemFileName); // set head if (ItemGroupTo.HeadIdentical(ItemGroupFrom, true)) ItemGroupTo.SetHead(ItemGroupFrom); // write group (do not change any headers set by DoGrpUpdate!) ItemGroupTo.Close(FALSE); // set core (C4Group::Save overwrites it) pGrpTo->SaveEntryCore(*pGrpFrom, strFileName); pGrpTo->SetSavedEntryCore(strFileName); return TRUE; }
BOOL C4UpdatePackage::MkUp(C4Group *pGrp1, C4Group *pGrp2, C4GroupEx *pUpGrp, BOOL *fModified) { // (CAUTION: pGrp1 may be NULL - that means that there is no counterpart for // Grp2 // in the base group) // compare headers if (!pGrp1 || pGrp1->GetCreation() != pGrp2->GetCreation() || pGrp1->GetOriginal() != pGrp2->GetOriginal() || !SEqual(pGrp1->GetMaker(), pGrp2->GetMaker()) || !SEqual(pGrp1->GetPassword(), pGrp2->GetPassword())) *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, NULL, NULL, !!strItemName[0])) { // add to entry list if (!!EntryList) EntryList.AppendChar('|'); EntryList.AppendFormat("%s=%d", strItemName, pGrp2->EntryTime(strItemName)); // no modification detected yet? then check order if (!*fModified) { if (!pGrp1->FindNextEntry("*", strItemName2, NULL, NULL, !!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 = NULL; } // 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) // SCopy(GetCfg()->AtTempPath("~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 times if (!pGrp1 || (pGrp1->EntryTime(strItemName) != pGrp2->EntryTime(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 (remove(strTempGroupName)) if (rmdir(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->EntryTime(strItemName) != pGrp2->EntryTime(strItemName) || 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, FALSE); MyFile.Close(); MemScramble((BYTE *)pData, iSize); MyFile.Create("UndSoAuch.txt", FALSE); MyFile.Write(pData, iSize, FALSE); 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 (!C4Group_GetFileCRC(strFile1, &GrpChks1[UpGrpCnt])) { WriteLog("Error: could not calc checksum for %s!\n", strFile1); return FALSE; } if (!C4Group_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"); remove(strUpdateFile); return FALSE; } WriteLog("Update package created.\n"); return TRUE; }
BOOL C4UpdatePackage::DoUpdate(C4Group *pGrpFrom, C4GroupEx *pGrpTo, const char *strFileName) { // group file? C4Group ItemGroupFrom; if (ItemGroupFrom.OpenAsChild(pGrpFrom, strFileName)) { // try to open target group C4GroupEx ItemGroupTo; char strTempGroup[_MAX_PATH + 1]; strTempGroup[0] = 0; if (!ItemGroupTo.OpenAsChild(pGrpTo, strFileName)) { // create (emtpy) temp dir // SCopy(GetCfg()->AtExePath("~tmp"), strTempGroup, //_MAX_PATH); MakeTempFilename(strTempGroup); // open/create it if (!ItemGroupTo.Open(strTempGroup, TRUE)) return FALSE; } // update children char ItemFileName[_MAX_PATH]; ItemGroupFrom.ResetSearch(); while (ItemGroupFrom.FindNextEntry("*", ItemFileName)) if (!SEqual(ItemFileName, C4CFN_UpdateCore) && !SEqual(ItemFileName, C4CFN_UpdateEntries)) DoUpdate(&ItemGroupFrom, &ItemGroupTo, ItemFileName); // set maker (always) ItemGroupTo.SetMaker(ItemGroupFrom.GetMaker()); if (GrpUpdate) { DoGrpUpdate(&ItemGroupFrom, &ItemGroupTo); // write group (do not change any headers set by DoGrpUpdate!) ItemGroupTo.Close(FALSE); // temporary group? if (strTempGroup[0]) if (!pGrpTo->Move(strTempGroup, strFileName)) return FALSE; // set core (C4Group::Save overwrites it) pGrpTo->SaveEntryCore(*pGrpFrom, strFileName); pGrpTo->SetSavedEntryCore(strFileName); // flag as no-resort pGrpTo->SetNoSort(strFileName); } else { // write group ItemGroupTo.Close(TRUE); // temporary group? if (strTempGroup[0]) if (!pGrpTo->Move(strTempGroup, strFileName)) return FALSE; } } else { char strMsg[1024]; sprintf(strMsg, "updating %s\\%s\n", pGrpTo->GetFullName().getData(), strFileName); #ifdef _MSC_VER OutputDebugString(strMsg); #elif _DEBUG puts(strMsg); #endif if (!C4Group_CopyEntry(pGrpFrom, pGrpTo, strFileName)) return FALSE; // set core pGrpTo->SaveEntryCore(*pGrpFrom, strFileName); pGrpTo->SetSavedEntryCore(strFileName); } // ok return TRUE; }
BOOL C4UpdatePackage::Execute(C4Group *pGroup) { // search target C4GroupEx TargetGrp; char strTarget[_MAX_PATH]; SCopy(DestPath, strTarget, _MAX_PATH); char *p = strTarget, *lp = strTarget; while (p = strchr(p + 1, '\\')) { *p = 0; if (!*(p + 1)) break; if (!SEqual(lp, "..")) if (TargetGrp.Open(strTarget)) { // packed? bool fPacked = TargetGrp.IsPacked(); // maker check (someone might try to unpack directories w/o asking user) if (fPacked) if (!SEqual(TargetGrp.GetMaker(), pGroup->GetMaker())) return FALSE; // Close Group TargetGrp.Close(TRUE); if (fPacked) // Unpack C4Group_UnpackDirectory(strTarget); } else { // GrpUpdate -> file must exist if (GrpUpdate) return FALSE; // create dir CreateDirectory(strTarget, NULL); } *p = '\\'; lp = p + 1; } // try to open it if (!TargetGrp.Open(strTarget, !GrpUpdate)) return FALSE; // check if the update is allowed if (GrpUpdate) { // maker must match /*if(!SEqual(TargetGrp.GetMaker(), pGroup->GetMaker())) - now allowing updates from different makers return FALSE;*/ // check checksum uint32_t iCRC32; if (!C4Group_GetFileCRC(TargetGrp.GetFullName().getData(), &iCRC32)) return FALSE; int i = 0; for (; i < UpGrpCnt; i++) if (iCRC32 == GrpChks1[i]) break; if (i >= UpGrpCnt) return FALSE; } else { // only allow Extra.c4g-Updates if (!SEqual2(DestPath, "Extra.c4g")) return FALSE; } // update children char ItemFileName[_MAX_PATH]; pGroup->ResetSearch(); while (pGroup->FindNextEntry("*", ItemFileName)) if (!SEqual(ItemFileName, C4CFN_UpdateCore) && !SEqual(ItemFileName, C4CFN_UpdateEntries)) DoUpdate(pGroup, &TargetGrp, ItemFileName); // do GrpUpdate if (GrpUpdate) DoGrpUpdate(pGroup, &TargetGrp); // close the group TargetGrp.Close(FALSE); if (GrpUpdate) { // check the result uint32_t iResChks; if (!C4Group_GetFileCRC(strTarget, &iResChks)) return FALSE; if (iResChks != GrpChks2) { #ifdef UPDATE_DEBUG char *pData; int iSize; CStdFile MyFile; MyFile.Load(strTarget, (BYTE **)&pData, &iSize, 0, TRUE); MyFile.Create("DiesesDingIstMist.txt", FALSE); MyFile.Write(pData, iSize, FALSE); MyFile.Close(); #endif return FALSE; } } return TRUE; }