Esempio n. 1
0
BOOL CStdBitmap::Save(const char *szFileName) {
  CStdFile hFile;
  if (!Bits) return FALSE;
  int savesize = sizeof(Head) + sizeof(Info);
  if (Info.biBitCount == 8) savesize += 256 * sizeof(RGBQUAD);
  if (!hFile.Create(szFileName, FALSE) || !hFile.Write(this, savesize) ||
      !hFile.Write(Bits, Info.biSizeImage) || !hFile.Close())
    return FALSE;
  return TRUE;
}
Esempio n. 2
0
File: C4Log.cpp Progetto: ev1313/yaC
bool GetLogSection(size_t iStart, size_t iLength, StdStrBuf &rsOut) {
  if (!iLength) {
    rsOut.Clear();
    return true;
  }
  // read section from log file
  CStdFile LogFileRead;
  char *szBuf, *szBufOrig;
  size_t iSize;  // size exclusing terminator
  if (!LogFileRead.Load(sLogFileName.getData(), (BYTE **)&szBuf, (int *)&iSize,
                        1))
    return false;
  szBufOrig = szBuf;
  // reduce to desired buffer section
  if (iStart > iSize) iStart = iSize;
  if (iStart + iLength > iSize) iLength = iSize - iStart;
  szBuf += iStart;
  szBuf[iLength] = '\0';
  // strip timestamps; convert linebreaks to Clonk-linebreaks '|'
  char *szPosWrite = szBuf;
  const char *szPosRead = szBuf;
  while (*szPosRead) {
    // skip timestamp
    if (*szPosRead == '[')
      while (*szPosRead && *szPosRead != ']') {
        --iSize;
        ++szPosRead;
      }
    // skip whitespace behind timestamp
    if (!*szPosRead) break;
    szPosRead++;
    // copy data until linebreak
    size_t iLen = 0;
    while (*szPosRead && *szPosRead != 0x0d && *szPosRead != 0x0a) {
      ++szPosRead;
      ++iLen;
    }
    if (iLen && szPosRead - iLen != szPosWrite)
      memmove(szPosWrite, szPosRead - iLen, iLen);
    szPosWrite += iLen;
    // skip additional linebreaks
    while (*szPosRead == 0x0d || *szPosRead == 0x0a) ++szPosRead;
    // write a Clonk-linebreak
    if (*szPosRead) *szPosWrite++ = '|';
  }
  // done; create string buffer from data
  rsOut.Copy(szBuf, szPosWrite - szBuf);
  // old buf no longer used
  delete[] szBufOrig;
  // done, success
  return true;
}
Esempio n. 3
0
bool CSurface8::Save(const char *szFilename, CStdPalette *bpPalette)
{
	C4BMP256Info BitmapInfo;
	BitmapInfo.Set(Wdt,Hgt, bpPalette ? bpPalette : pPal);

	// Create file & write info
	CStdFile hFile;

	if ( !hFile.Create(szFilename)
	     || !hFile.Write(&BitmapInfo,sizeof(BitmapInfo)) )
		{ return false; }

	// Write lines
	char bpEmpty[4]; ZeroMem(bpEmpty, 4);
	const int iEmpty = DWordAligned(Wdt)-Wdt;
	for (int cnt=Hgt-1; cnt>=0; cnt--)
	{
		if (!hFile.Write(Bits+(Pitch*cnt),Wdt))
			{ return false; }
		if (iEmpty)
			if (!hFile.Write(bpEmpty,iEmpty))
				{ return false; }
	}

	// Close file
	hFile.Close();

	// Success
	return true;
}
Esempio n. 4
0
bool CSurface8::Save(const char *szFilename, BYTE *bpPalette) {
  CBitmap256Info BitmapInfo;
  BitmapInfo.Set(Wdt, Hgt, bpPalette ? bpPalette : pPal->Colors);

  // Create file & write info
  CStdFile hFile;

  if (!hFile.Create(szFilename) ||
      !hFile.Write(&BitmapInfo, sizeof(BitmapInfo))) {
    return FALSE;
  }

  // Write lines
  char bpEmpty[4];
  int iEmpty = DWordAligned(Wdt) - Wdt;
  for (int cnt = Hgt - 1; cnt >= 0; cnt--) {
    if (!hFile.Write(Bits + (Pitch * cnt), Wdt)) {
      return FALSE;
    }
    if (iEmpty)
      if (!hFile.Write(bpEmpty, iEmpty)) {
        return FALSE;
      }
  }

  // Close file
  hFile.Close();

  // Success
  return TRUE;
}
Esempio n. 5
0
bool C4TableGraph::DumpToFile(const StdStrBuf &rszFilename,
                              bool fAppend) const {
  assert(!!rszFilename);
  // nothing to write?
  if (!fWrapped && !iBackLogPos) return false;
  // try append if desired; create if unsuccessful
  CStdFile out;
  if (fAppend)
    if (!out.Append(rszFilename.getData())) fAppend = false;
  if (!fAppend) {
    if (!out.Create(rszFilename.getData())) return false;
    // print header
    out.WriteString("t\tv\n\r");
  }
  // write out current timeframe
  int iEndTime = GetEndTime();
  StdStrBuf buf;
  for (int iWriteTime = GetStartTime(); iWriteTime < iEndTime; ++iWriteTime) {
    buf.Format("%d\t%d\n\r", (int)iWriteTime, (int)GetValue(iWriteTime));
    out.WriteString(buf.getData());
  }
  return true;
}
Esempio n. 6
0
void C4Playback::Clear() {
  // free stuff
  for (chunks_t::iterator i = chunks.begin(); i != chunks.end(); i++)
    i->Delete();
  chunks.clear();
  currChunk = chunks.end();
  playbackFile.Close();
  sequentialBuffer.Clear();
  fLoadSequential = false;
#ifdef DEBUGREC
  C4IDPacket *pkt;
  while (pkt = DebugRec.firstPkt()) DebugRec.Delete(pkt);
#ifdef DEBUGREC_EXTFILE
  DbgRecFile.Close();
#endif
#endif
  // done
  Finished = TRUE;
}
Esempio n. 7
0
void C4Playback::Check(C4RecordChunkType eType, const uint8_t *pData,
                       int iSize) {
  // only if enabled
  if (DoNoDebugRec > 0) return;
  if (Game.FrameCounter < DEBUGREC_START_FRAME) return;

  C4PktDebugRec PktInReplay;
  bool fHasPacketFromHead = false;
#ifdef DEBUGREC_EXTFILE
#ifdef DEBUGREC_EXTFILE_WRITE
  // writing of external debugrec file
  DbgRecFile.Write(&eType, sizeof eType);
  int32_t iSize32 = iSize;
  DbgRecFile.Write(&iSize32, sizeof iSize32);
  DbgRecFile.Write(pData, iSize);
  return;
#else
  int32_t iSize32 = 0;
  C4RecordChunkType eTypeRec = RCT_Undefined;
  DbgRecFile.Read(&eTypeRec, sizeof eTypeRec);
  DbgRecFile.Read(&iSize32, sizeof iSize32);
  if (iSize32) {
    StdBuf buf;
    buf.SetSize(iSize32);
    DbgRecFile.Read(buf.getMData(), iSize32);
    PktInReplay = C4PktDebugRec(eTypeRec, buf);
  }
#endif
#else
  // check debug rec in list
  C4IDPacket *pkt;
  if (pkt = DebugRec.firstPkt()) {
    // copy from list
    PktInReplay = *static_cast<C4PktDebugRec *>(pkt->getPkt());
    DebugRec.Delete(pkt);
  } else {
    // special sync check skip...
    while (currChunk != chunks.end() && currChunk->Type == RCT_CtrlPkt) {
      C4IDPacket Packet(*currChunk->pPkt);
      C4ControlPacket *pCtrlPck =
          static_cast<C4ControlPacket *>(Packet.getPkt());
      assert(!pCtrlPck->Sync());
      Game.Control.ExecControlPacket(Packet.getPktType(), pCtrlPck);
      NextChunk();
    }
    // record end?
    if (currChunk == chunks.end() || currChunk->Type == RCT_End || Finished) {
      Log("DebugRec end: All in sync!");
      ++DoNoDebugRec;
      return;
    }
    // unpack directly from head
    if (currChunk->Type != eType) {
      DebugRecError(FormatString("Playback type %x, this type %x",
                                 currChunk->Type, eType).getData());
      return;
    }
    PktInReplay = *currChunk->pDbg;
    fHasPacketFromHead = true;
  }
#endif  // DEBUGREC_EXTFILE
  // record end?
  if (PktInReplay.getType() == RCT_End) {
    Log("DebugRec end: All in sync (2)!");
    ++DoNoDebugRec;
    return;
  }
  // replay packet is unpacked to PktInReplay now; check it
  if (PktInReplay.getType() != eType) {
    DebugRecError(FormatString("Type %s != %s",
                               GetRecordChunkTypeName(PktInReplay.getType()),
                               GetRecordChunkTypeName(eType)).getData());
    return;
  }
  if (PktInReplay.getSize() != iSize) {
    DebugRecError(FormatString("Size %d != %d", (int)PktInReplay.getSize(),
                               (int)iSize).getData());
  }
  // check packet data
  if (memcmp(PktInReplay.getData(), pData, iSize)) {
    StdStrBuf sErr;
    sErr.Format("DbgRecPkt Type %s, size %d", GetRecordChunkTypeName(eType),
                iSize);
    sErr.Append(" Replay: ");
    StdBuf replay(PktInReplay.getData(), PktInReplay.getSize());
    sErr.Append(GetDbgRecPktData(eType, replay));
    sErr.Append(" Here: ");
    StdBuf here(pData, iSize);
    sErr.Append(GetDbgRecPktData(eType, here));
    DebugRecError(sErr.getData());
  }
  // packet is fine, jump over it
  if (fHasPacketFromHead) NextChunk();
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
bool C4UpdateDlg::ApplyUpdate(const char *strUpdateFile, bool fDeleteUpdate, C4GUI::Screen *pScreen)
{
	// Apply update: If the update file is a .ocu, it will extract c4group and apply the update.
	// If the update file is an installer, it will just launch that installer.
	StdStrBuf strUpdateProgEx, strUpdateArgs;
	bool fIsGroupUpdate = SEqualNoCase(GetExtension(strUpdateFile), C4CFN_UpdateGroupExtension+1);
	// Is this an update executable or an update group?
	if (fIsGroupUpdate)
	{
		// This is an update group (.ocu). Extract c4group and run it.
		// Find a place to extract the update
		Config.MakeTempUpdateFolder();
		// Determine name of update program
		StdStrBuf strUpdateProg;
		strUpdateProg.Copy(C4CFN_UpdateProgram);
		// Windows: manually append extension because ExtractEntry() cannot properly glob and Extract() doesn't return failure values
#ifdef _WIN32
		strUpdateProg += ".exe";
#endif
		// Determine name of local extract of update program
		strUpdateProgEx.Copy(Config.AtTempUpdatePath(strUpdateProg.getData()));

		// Extract update program (the update should be applied using the new version)
		C4Group UpdateGroup;
		if (!UpdateGroup.Open(strUpdateFile))
		{
			LogF("Error opening \"%s\": %s", strUpdateFile, UpdateGroup.GetError());
			return false;
		}
		// Look for update program at top level
		if (!UpdateGroup.ExtractEntry(strUpdateProg.getData(), strUpdateProgEx.getData()))
		{
			LogF("Error extracting \"%s\": %s", strUpdateProg.getData(), UpdateGroup.GetError());
			return false;
		}
		// Extract any needed library files
		UpdateGroup.Extract(C4CFN_UpdateProgramLibs, Config.AtTempUpdatePath(""), C4CFN_UpdateProgram);
		UpdateGroup.Close();
#ifdef _WIN32
		// Notice: even if the update program and update group are in the temp path, they must be executed in our working directory
		DWORD ProcessID = GetCurrentProcessId();
		//strUpdateArgs.Format("\"%s\" \"%s\" %s %lu", strUpdateProgEx.getData(), strUpdateFile, fDeleteUpdate ? "-yd" : "-y", (unsigned long)ProcessID);
		strUpdateArgs.Format("\"%s\" %s %lu", strUpdateFile, fDeleteUpdate ? "-yd" : "-y", (unsigned long)ProcessID);

#if 0 // debug code to reroute updating via batch file
		CStdFile f; - reroute via vi
		f.Create(Config.AtTempUpdatePath("update.bat"));
		f.WriteString(FormatString("%s %s\npause\n", strUpdateProgEx.getData(), strUpdateArgs.getData()).getData());
		f.Close();
		strUpdateProgEx.Copy(Config.AtTempUpdatePath("update.bat"));
		strUpdateArgs.Copy(strUpdateProgEx);
#endif
#endif
	}
	else
	{
		// This "update" is actually an installer. Just run it.
		strUpdateProgEx = strUpdateFile;
		strUpdateArgs = "";
		// if group was downloaded to temp path, delete it from there
		if (fDeleteUpdate) SCopy(strUpdateProgEx.getData(), Config.General.TempUpdatePath, CFG_MaxString);
	}

	// Execute update program
	Log(LoadResStr("IDS_PRC_LAUNCHINGUPDATE"));
	succeeded = true;

#ifdef _WIN32
	// Notice: even if the update program and update group are in the temp path, they must be executed in our working directory
	// the magic verb "runas" opens the update program in a shell requesting elevation
	int iError = (intptr_t)ShellExecute(NULL, L"runas", strUpdateProgEx.GetWideChar(), strUpdateArgs.GetWideChar(), Config.General.ExePath.GetWideChar(), SW_SHOW);
	if (iError <= 32) return false;

	// must quit ourselves for update program to work
	if (succeeded) Application.Quit();
#else
	if (pipe(c4group_output) == -1)
	{
		Log("Error creating pipe");
		return false;
	}
	switch (pid = fork())
	{
		// Error
	case -1:
		Log("Error creating update child process.");
		return false;
		// Child process
	case 0:
		// Close unused read end
		close(c4group_output[0]);
		// redirect stdout and stderr to the parent
		dup2(c4group_output[1], STDOUT_FILENO);
		dup2(c4group_output[1], STDERR_FILENO);
		if (c4group_output[1] != STDOUT_FILENO && c4group_output[1] != STDERR_FILENO)
			close(c4group_output[1]);
		if (fIsGroupUpdate)
			execl(C4CFN_UpdateProgram, C4CFN_UpdateProgram, "-v", strUpdateFile, (fDeleteUpdate ? "-yd" : "-y"), static_cast<char *>(0));
		else
			execl(strUpdateFile, strUpdateFile, static_cast<char *>(0));
		printf("execl failed: %s\n", strerror(errno));
		exit(1);
		// Parent process
	default:
		// Close unused write end
		close(c4group_output[1]);
		// disable blocking
		fcntl(c4group_output[0], F_SETFL, O_NONBLOCK);
		// Open the update log dialog (this will update itself automatically from c4group_output)
		pScreen->ShowRemoveDlg(new C4UpdateDlg());
		break;
	}
#endif
	// done
	return succeeded;
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
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;
}