StdStrBuf StdCompilerConfigRead::ReadString()
{
	// Virtual key?
	if (pKey->Virtual)
		{ excNotFound("Could not read value %s! Parent key doesn't exist!", pKey->Name.getData()); return StdStrBuf(); }
	// Wrong type?
	if (pKey->Type != REG_SZ)
		{ excNotFound("Wrong value type!"); return StdStrBuf(); }
	// Get size of string
	DWORD iSize;
	if (RegQueryValueExW(pKey->Parent->Handle, pKey->Name.GetWideChar(),
	                    0, NULL,
	                    NULL,
	                    &iSize) != ERROR_SUCCESS)
		{ excNotFound("Could not read value %s!", pKey->Name.getData()); return StdStrBuf(); }
	// Allocate string
	StdBuf Result; Result.SetSize(iSize);
	// Read
	if (RegQueryValueExW(pKey->Parent->Handle, pKey->Name.GetWideChar(),
	                    0, NULL,
	                    reinterpret_cast<BYTE *>(Result.getMData()),
	                    &iSize) != ERROR_SUCCESS)
		{ excNotFound("Could not read value %s!", pKey->Name.getData()); return StdStrBuf(); }
	// Check size
	if (wcslen(getBufPtr<wchar_t>(Result)) + 1 != iSize / sizeof(wchar_t))
		{ excCorrupt("Wrong size of a string!"); return StdStrBuf(); }
	return StdStrBuf(getBufPtr<wchar_t>(Result));
}
size_t C4AulDebug::UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &addr)
{
	// Find line separation
	const char *pSep = reinterpret_cast<const char *>(memchr(rInBuf.getData(), '\n', rInBuf.getSize()));
	if (!pSep)
		return 0;
	// Check if it's windows-style separation
	int iSize = pSep - getBufPtr<char>(rInBuf) + 1,
	            iLength = iSize - 1;
	if (iLength && *(pSep - 1) == '\r')
		iLength--;
	// Copy the line
	StdStrBuf Buf; Buf.Copy(getBufPtr<char>(rInBuf), iLength);
	// Password line?
	if (fConnected)
	{
		ProcessLineResult result = ProcessLine(Buf);
		// Send answer
		SendLine(result.okay ? "OK" : "ERR", result.answer.length() > 0 ? result.answer.c_str() : NULL);
	}
	else if (!Password.getSize() || Password == Buf)
	{
		fConnected = true;
		SendLine("HLO", "This is " C4ENGINECAPTION ", " C4VERSION);
		Log("C4Aul debugger connected successfully!");
	}
	else
		C4NetIOTCP::Close(PeerAddr);
	// Consume line
	return iSize;
}
StdBuf C4NetpuncherPacketID<TYPE>::PackInto() const {
	StdBuf buf;
	auto id = GetID();
	buf.New(sizeof(id));
	buf.Write(&id, sizeof(id));
	return buf;
}
size_t C4Network2IRCClient::UnpackPacket(const StdBuf &rInBuf, const C4NetIO::addr_t &addr)
	{
	// Find line seperation
 	const char *pSep = reinterpret_cast<const char *>(memchr(rInBuf.getData(), '\n', rInBuf.getSize()));
	if(!pSep)
		return 0;
	// Check if it's actually correct seperation (rarely the case)
	int iSize = pSep - getBufPtr<char>(rInBuf) + 1,
			iLength = iSize - 1;
	if(iLength && *(pSep - 1) == '\r')
		iLength--;
	// Copy the line
	StdStrBuf Buf; Buf.Copy(getBufPtr<char>(rInBuf), iLength);
	// Ignore prefix
	const char *pMsg = Buf.getData();
	StdStrBuf Prefix;
	if(*pMsg == ':')
		{
		Prefix.CopyUntil(pMsg + 1, ' ');
		pMsg += Prefix.getLength() + 1;
		}
	// Strip whitespace
	while(*pMsg == ' ')
		pMsg++;
	// Ignore empty message
	if(!*pMsg)
		return iSize;
	// Get command
	StdStrBuf Cmd; Cmd.CopyUntil(pMsg, ' ');
	// Precess command
	const char *szParameters = SSearch(pMsg, " ");
	OnCommand(Prefix.getData(), Cmd.getData(), szParameters ? szParameters : "");
	// Consume the line
	return iSize;
	}
void StdCompilerINIRead::String(char **pszString, RawCompileType eType)
{
	// Get length
	size_t iLength = GetStringLength(eType);
	// Read data
	StdBuf Buf = ReadString(iLength, eType, true);
	// Set
	*pszString = reinterpret_cast<char *>(Buf.GrabPointer());
}
Beispiel #6
0
BOOL C4Scenario::Save(C4Group &hGroup, bool fSaveSection)
	{
	char *Buffer; int32_t BufferSize;
	if (!Decompile(&Buffer,&BufferSize, fSaveSection)) 
		return FALSE;
	if (!hGroup.Add(C4CFN_ScenarioCore,Buffer,BufferSize,FALSE,TRUE))
		{ StdBuf Buf; Buf.Take(Buffer, BufferSize); return FALSE; }
	return TRUE;
	}
void StdCompilerConfigWrite::WriteString(const char *szString)
{
	// Append or set the value
	if (LastString.getLength()) LastString.Append(szString); else LastString.Copy(szString);
	StdBuf v = LastString.GetWideCharBuf();
	if (RegSetValueExW(pKey->Parent->Handle, pKey->Name.GetWideChar(),
	                  0, REG_SZ, getBufPtr<BYTE>(v), v.getSize()) != ERROR_SUCCESS)
		excCorrupt("Could not write key %s!", pKey->Name.getData());
}
void StdCompilerINIRead::Raw(void *pData, size_t iSize, RawCompileType eType)
{
	// Read data
	StdBuf Buf = ReadString(iSize, eType, false);
	// Correct size?
	if (Buf.getSize() != iSize)
		Warn("got %u bytes raw data, but %u bytes expected!", Buf.getSize(), iSize);
	// Copy
	MemCopy(Buf.getData(), pData, iSize);
}
StdBuf C4NetpuncherPacketCReq::PackInto() const {
	StdBuf buf;
	auto sin6 = static_cast<sockaddr_in6>(addr.AsIPv6());
	auto port = addr.GetPort();
	buf.New(sizeof(port) + sizeof(sin6.sin6_addr));
	size_t offset = 0;
	buf.Write(&port, sizeof(port), offset);
	offset += sizeof(port);
	buf.Write(&sin6.sin6_addr, sizeof(sin6.sin6_addr), offset);
	static_assert(sizeof(sin6.sin6_addr) == 16, "expected sin6_addr to be 16 bytes");
	return buf;
}
void C4Network2IRCClient::PackPacket(const C4NetIOPacket &rPacket, StdBuf &rOutBuf)
	{
	// Enlarge buffer
	int iSize = rPacket.getSize(),
			iPos = rOutBuf.getSize();
	rOutBuf.Grow(iSize + 2);
	// Write packet
	rOutBuf.Write(rPacket, iPos);
	// Terminate
	uint8_t *pPos = getMBufPtr<uint8_t>(rOutBuf, iPos + iSize);
	*pPos = '\r'; *(pPos + 1) = '\n';
	}
bool C4KeyboardInput::LoadCustomConfig()
{
    // load from INI file (2do: load from registry)
    C4Group GrpExtra;
    if (!GrpExtra.Open(C4CFN_Extra)) return false;
    StdBuf sFileContents;
    if (!GrpExtra.LoadEntry(C4CFN_KeyConfig, &sFileContents)) return false;
    StdStrBuf sFileContentsString((const char *) sFileContents.getData());
    if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, sFileContentsString, "Custom keys from" C4CFN_Extra DirSep C4CFN_KeyConfig))
        return false;
    LogF(LoadResStr("IDS_PRC_LOADEDKEYCONF"), C4CFN_Extra DirSep C4CFN_KeyConfig);
    return true;
}
Beispiel #12
0
BOOL C4SoundEffect::Load(const char *szFileName, C4Group &hGroup,
                         BOOL fStatic) {
  // Sound check
  if (!Config.Sound.RXSound) return FALSE;
  // Locate sound in file
  StdBuf WaveBuffer;
  if (!hGroup.LoadEntry(szFileName, WaveBuffer)) return FALSE;
  // load it from mem
  if (!Load((BYTE *)WaveBuffer.getData(), WaveBuffer.getSize(), fStatic))
    return FALSE;
  // Set name
  SCopy(szFileName, Name, C4MaxSoundName);
  return TRUE;
}
StdBuf StdCompilerINIRead::ReadString(size_t iLength, RawCompileType eRawType, bool fAppendNull)
{
	// Excpect valid position
	if (!pPos)
		{ notFound("String"); return StdBuf(); }
	// Skip whitespace
	SkipWhitespace();
	// Escaped? Go over '"'
	if (eRawType == RCT_Escaped && *pPos++ != '"')
		{ notFound("Escaped string"); return StdBuf(); }
	// Create buffer
	StdBuf OutBuf; OutBuf.New(iLength + (fAppendNull ? sizeof('\0') : 0));
	// Read
	char *pOut = getMBufPtr<char>(OutBuf);
	while (iLength && !TestStringEnd(eRawType))
	{
		// Read a character
		if (eRawType == RCT_Escaped)
			*pOut++ = ReadEscapedChar();
		else
			*pOut++ = *pPos++;
		// Count it
		iLength--;
	}
	// Escaped: Go over '"'
	if (eRawType == RCT_Escaped)
	{
		while (*pPos != '"')
		{
			if (!*pPos || *pPos == '\n' || *pPos == '\r')
			{
				Warn("string not terminated!");
				pPos--;
				break;
			}
			pPos++;
		}
		pPos++;
	}
	// Nothing read? Identifiers need to be non-empty
	if (pOut == OutBuf.getData() && (eRawType == RCT_Idtf || eRawType == RCT_ID))
		{ notFound("String"); return StdBuf(); }
	// Append null
	if (fAppendNull)
		*pOut = '\0';
	// Shrink, if less characters were read
	OutBuf.Shrink(iLength);
	// Done
	return OutBuf;
}
Beispiel #14
0
StdStrBuf GetDbgRecPktData(C4RecordChunkType eType, const StdBuf &RawData) {
  StdStrBuf r;
  switch (eType) {
    case RCT_AulFunc:
      r.Ref(reinterpret_cast<const char *>(RawData.getData()),
            RawData.getSize() - 1);
      break;
    default:
      for (int i = 0; i < RawData.getSize(); ++i)
        r.AppendFormat("%02x ", (uint32_t)((uint8_t *)RawData.getData())[i]);
      break;
  }
  return r;
}
Beispiel #15
0
bool C4Record::StreamFile(const char *szLocalFilename, const char *szAddAs) {
  // Load file into memory
  StdBuf FileData;
  if (!FileData.LoadFromFile(szLocalFilename)) return false;

  // Prepend name
  StdBuf Packed = DecompileToBuf<StdCompilerBinWrite>(
      mkInsertAdapt(StdStrBuf(szAddAs), FileData, false));

  // Add to stream
  C4RecordChunkHead Head = {0, RCT_File};
  Stream(Head, Packed);
  return true;
}
Beispiel #16
0
StdBuf C4Playback::ReWriteBinary() {
  const int OUTPUT_GROW = 16 * 1024;
  StdBuf Output;
  int iPos = 0;
  bool fFinished = false;
  uint32_t iFrame = 0;
  for (chunks_t::const_iterator i = chunks.begin();
       !fFinished && i != chunks.end(); i++) {
    // Check frame difference
    if (i->Frame - iFrame < 0 || i->Frame - iFrame > 0xff)
      LogF(
          "ERROR: Invalid frame difference between chunks (0-255 allowed)! "
          "Data will be invalid!");
    // Pack data
    StdBuf Chunk;
    try {
      switch (i->Type) {
        case RCT_Ctrl:
          Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pCtrl);
          break;
        case RCT_CtrlPkt:
          Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pPkt);
          break;
        case RCT_End:
          fFinished = true;
          break;
        default:  // debugrec
          if (i->pDbg) Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pDbg);
          break;
      }
    } catch (StdCompiler::Exception *pEx) {
      LogF("Record: Binary unpack error: %s", pEx->Msg.getData());
      delete pEx;
      return StdBuf();
    }
    // Grow output
    while (Output.getSize() - iPos <
           sizeof(C4RecordChunkHead) + Chunk.getSize())
      Output.Grow(OUTPUT_GROW);
    // Write header
    C4RecordChunkHead *pHead = getMBufPtr<C4RecordChunkHead>(Output, iPos);
    pHead->Type = i->Type;
    pHead->iFrm = i->Frame - iFrame;
    iPos += sizeof(C4RecordChunkHead);
    iFrame = i->Frame;
    // Write chunk
    Output.Write(Chunk, iPos);
    iPos += Chunk.getSize();
  }
  Output.SetSize(iPos);
  return Output;
}
Beispiel #17
0
bool C4Playback::NextSequentialChunk() {
  StdBuf BinaryBuf;
  size_t iRealSize;
  BinaryBuf.New(4096);
  // load data until a chunk could be filled
  for (;;) {
    iRealSize = 0;
    playbackFile.Read(BinaryBuf.getMData(), 4096, &iRealSize);
    if (!iRealSize) return false;
    BinaryBuf.SetSize(iRealSize);
    if (!ReadBinary(BinaryBuf)) return false;
    // okay, at least one chunk has been read!
    if (chunks.size()) {
      currChunk = chunks.begin();
      return true;
    }
  }
  // playback file reading failed - looks like we're done
  return false;
}
Beispiel #18
0
bool C4Record::Rec(int iFrame, const StdBuf &sBuf, C4RecordChunkType eType) {
  // filler chunks (this should never be necessary, though)
  while (iFrame > iLastFrame + 0xff)
    Rec(iLastFrame + 0xff, StdBuf(), RCT_Frame);
  // get frame difference
  uint32_t iFrameDiff = Max<uint32_t>(0, iFrame - iLastFrame);
  iLastFrame += iFrameDiff;
  // create head
  C4RecordChunkHead Head = {static_cast<uint8_t>(iFrameDiff),
                            static_cast<uint8_t>(eType)};
  // pack
  CtrlRec.Write(&Head, sizeof(Head));
  CtrlRec.Write(sBuf.getData(), sBuf.getSize());
#ifdef IMMEDIATEREC
  // immediate rec: always flush
  CtrlRec.Flush();
#endif
  // Stream
  if (fStreaming) Stream(Head, sBuf);
  return true;
}
bool SetRegistryString(const char *szSubKey,
                       const char *szValueName,
                       const char *szValue)
{

	long qerr;
	HKEY ckey;
	DWORD disposition;

	// Open the key
	if ((qerr=RegCreateKeyExW(HKEY_CURRENT_USER,
	                         GetWideChar(szSubKey),
	                         0,
	                         L"",
	                         REG_OPTION_NON_VOLATILE,
	                         KEY_ALL_ACCESS,
	                         NULL,
	                         &ckey,
	                         &disposition
	                        ))!=ERROR_SUCCESS) return false;

	// Set the value
	StdBuf v = GetWideCharBuf(szValue);
	if ((qerr=RegSetValueExW(ckey,
	                        GetWideChar(szValueName),
	                        0,
	                        REG_SZ,
	                        getBufPtr<BYTE>(v),
	                        v.getSize()
	                       ))!=ERROR_SUCCESS) { RegCloseKey(ckey); return false; }

	// Close the key
	RegCloseKey(ckey);

	return true;
}
Beispiel #20
0
bool C4Playback::StreamToRecord(const char *szStream, StdStrBuf *pRecordFile) {
  // Load data
  StdBuf CompressedData;
  Log("Reading stream...");
  if (!CompressedData.LoadFromFile(szStream)) return false;

  // Decompress
  unsigned long iStreamSize = CompressedData.getSize() * 5;
  StdBuf StreamData;
  StreamData.New(iStreamSize);
  while (true) {
    // Initialize stream
    z_stream strm;
    ZeroMem(&strm, sizeof strm);
    strm.next_in = getMBufPtr<BYTE>(CompressedData);
    strm.avail_in = CompressedData.getSize();
    strm.next_out = getMBufPtr<BYTE>(StreamData);
    strm.avail_out = StreamData.getSize();

    // Decompress
    if (inflateInit(&strm) != Z_OK) return false;
    int ret = inflate(&strm, Z_FINISH);
    if (ret == Z_STREAM_END) {
      inflateEnd(&strm);
      break;
    }
    if (ret != Z_BUF_ERROR) return false;

    // All input consumed?
    iStreamSize = strm.total_out;
    if (strm.avail_in == 0) {
      Log("Stream data incomplete, using as much data as possible");
      break;
    }

    // Larger buffer needed
    StreamData.Grow(CompressedData.getSize());
    iStreamSize = StreamData.getSize();
  }
  StreamData.SetSize(iStreamSize);

  // Parse
  C4Playback Playback;
  Playback.ReadBinary(StreamData);
  LogF("Got %d chunks from stream", Playback.chunks.size());

  // Get first chunk, which must contain the initial
  chunks_t::iterator chunkIter = Playback.chunks.begin();
  if (chunkIter == Playback.chunks.end() || chunkIter->Type != RCT_File)
    return false;

  // Get initial chunk, go over file name
  StdBuf InitialData = *chunkIter->pFileData;
  const char *szInitialFilename = chunkIter->Filename.getData();

  // Put to temporary file and unpack
  char szInitial[_MAX_PATH + 1] = "~initial.tmp";
  MakeTempFilename(szInitial);
  if (!InitialData.SaveToFile(szInitial) || !C4Group_UnpackDirectory(szInitial))
    return false;

  // Load Scenario.txt from Initial
  C4Group Grp;
  C4Scenario Initial;
  if (!Grp.Open(szInitial) || !Initial.Load(Grp) || !Grp.Close()) return false;

  // Copy original scenario
  const char *szOrigin = Initial.Head.Origin.getData();
  char szRecord[_MAX_PATH + 1];
  SCopy(szStream, szRecord, _MAX_PATH);
  if (GetExtension(szRecord)) *(GetExtension(szRecord) - 1) = 0;
  SAppend(".c4s", szRecord, _MAX_PATH);
  LogF("Original scenario is %s, creating %s.", szOrigin, szRecord);
  if (!C4Group_CopyItem(szOrigin, szRecord, false, false)) return false;

  // Merge initial
  if (!Grp.Open(szRecord) || !Grp.Merge(szInitial)) return false;

  // Process other files in stream
  chunkIter->Delete();
  chunkIter = Playback.chunks.erase(chunkIter);
  while (chunkIter != Playback.chunks.end())
    if (chunkIter->Type == RCT_File) {
      LogF("Inserting %s...", chunkIter->Filename.getData());
      StdStrBuf Temp;
      Temp.Copy(chunkIter->Filename);
      MakeTempFilename(&Temp);
      if (!chunkIter->pFileData->SaveToFile(Temp.getData())) return false;
      if (!Grp.Move(Temp.getData(), chunkIter->Filename.getData()))
        return false;
      chunkIter = Playback.chunks.erase(chunkIter);
    } else
      chunkIter++;

  // Write record data
  StdBuf RecordData = Playback.ReWriteBinary();
  if (!Grp.Add(C4CFN_CtrlRec, RecordData, false, true)) return false;

  // Done
  Log("Writing record file...");
  Grp.Close();
  pRecordFile->Copy(szRecord);
  return true;
}
Beispiel #21
0
void C4Record::Stream(const C4RecordChunkHead &Head, const StdBuf &sBuf) {
  if (!fStreaming) return;
  StreamingData.Append(&Head, sizeof(Head));
  StreamingData.Append(sBuf.getData(), sBuf.getSize());
}
Beispiel #22
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();
}
Beispiel #23
0
BOOL C4Playback::ReadBinary(const StdBuf &Buf) {
  // sequential reading: Take over rest from last buffer
  const StdBuf *pUseBuf;
  uint32_t iFrame = 0;
  if (fLoadSequential) {
    sequentialBuffer.Append(Buf);
    pUseBuf = &sequentialBuffer;
    iFrame = iLastSequentialFrame;
  } else
    pUseBuf = &Buf;
  // get buffer data
  size_t iPos = 0;
  bool fFinished = false;
  do {
    // unpack header
    if (pUseBuf->getSize() - iPos < sizeof(C4RecordChunkHead)) break;
    const C4RecordChunkHead *pHead =
        getBufPtr<C4RecordChunkHead>(*pUseBuf, iPos);
    // get chunk
    iPos += sizeof(C4RecordChunkHead);
    StdBuf Chunk = pUseBuf->getPart(iPos, pUseBuf->getSize() - iPos);
    // Create entry
    C4RecordChunk c;
    c.Frame = (iFrame += pHead->iFrm);
    c.Type = pHead->Type;
    // Unpack data
    try {
      // Initialize compiler
      StdCompilerBinRead Compiler;
      Compiler.setInput(Chunk.getRef());
      Compiler.Begin();
      // Read chunk
      switch (pHead->Type) {
        case RCT_Ctrl:
          Compiler.Value(mkPtrAdaptNoNull(c.pCtrl));
          break;
        case RCT_CtrlPkt:
          Compiler.Value(mkPtrAdaptNoNull(c.pPkt));
          break;
        case RCT_End:
          fFinished = true;
          break;
        case RCT_File:
          Compiler.Value(c.Filename);
          Compiler.Value(mkPtrAdaptNoNull(c.pFileData));
          break;
        default:
          // debugrec
          if (pHead->Type >= 0x80) Compiler.Value(mkPtrAdaptNoNull(c.pDbg));
      }
      // Advance over data
      Compiler.End();
      iPos += Compiler.getPosition();
    } catch (StdCompiler::EOFException *pEx) {
      // This is to be expected for sequential reading
      if (fLoadSequential) {
        iPos -= sizeof(C4RecordChunkHead);
        iFrame -= pHead->iFrm;
        delete pEx;
        break;
      }
      LogF("Record: Binary unpack error: %s", pEx->Msg.getData());
      c.Delete();
      delete pEx;
      return FALSE;
    } catch (StdCompiler::Exception *pEx) {
      LogF("Record: Binary unpack error: %s", pEx->Msg.getData());
      c.Delete();
      delete pEx;
      return FALSE;
    }
    // Add to list
    chunks.push_back(c);
    c.pPkt = NULL;
  } while (!fFinished);
  // erase everything but the trailing part from sequential buffer
  if (fLoadSequential) {
    if (iPos >= sequentialBuffer.getSize())
      sequentialBuffer.Clear();
    else if (iPos) {
      sequentialBuffer.Move(iPos, sequentialBuffer.getSize() - iPos);
      sequentialBuffer.Shrink(iPos);
    }
    iLastSequentialFrame = iFrame;
  }
  return TRUE;
}