• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PredatorCZ / PreCore / 576

13 Apr 2026 04:42PM UTC coverage: 51.76% (-0.01%) from 51.773%
576

push

github

PredatorCZ
add reset for 64bit pointer

4131 of 7981 relevant lines covered (51.76%)

11428.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/app/out_context.cpp
1
/*  Spike is universal dedicated module handler
2
    This souce contains data output context
3

4
    Copyright 2021-2023 Lukas Cone
5

6
    Licensed under the Apache License, Version 2.0 (the "License");
7
    you may not use this file except in compliance with the License.
8
    You may obtain a copy of the License at
9

10
        http://www.apache.org/licenses/LICENSE-2.0
11

12
    Unless required by applicable law or agreed to in writing, software
13
    distributed under the License is distributed on an "AS IS" BASIS,
14
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
    See the License for the specific language governing permissions and
16
    limitations under the License.
17
*/
18

19
#include "spike/app/out_context.hpp"
20
#include "spike/app/console.hpp"
21
#include "spike/app/texel.hpp"
22
#include "spike/crypto/crc32.hpp"
23
#include "spike/format/ZIP_istream.inl"
24
#include "spike/format/ZIP_ostream.inl"
25
#include "spike/io/binreader.hpp"
26
#include "spike/io/fileinfo.hpp"
27
#include "spike/io/stat.hpp"
28
#include <chrono>
29
#include <mutex>
30

31
void ZIPExtactContext::FinishZIP(cache_begin_cb cacheBeginCB) {
×
32
  FinishFile(true);
×
33

34
  auto entriesStr = std::move(entriesStream).str();
35
  bool forcex64 = false;
×
36
  const size_t dirOffset = records.Tell();
37
  size_t dirSize = entriesStr.size();
38

39
  auto SafeCast = [&](auto &where, auto &&what) {
40
    const uint64 limit =
41
        std::numeric_limits<std::decay_t<decltype(where)>>::max();
42
    if (what >= limit) {
×
43
      forcex64 = true;
×
44
      where = limit;
×
45
    } else {
46
      where = what;
×
47
    }
48
  };
49

50
  ZIPCentralDir zCentral{};
×
51
  zCentral.id = ZIPCentralDir::ID;
×
52
  SafeCast(zCentral.numDirEntries, numEntries);
53
  SafeCast(zCentral.numDiskEntries, numEntries);
54
  SafeCast(zCentral.dirOffset, dirOffset);
55

56
  records.WriteContainer(entriesStr);
57
  es::Dispose(entriesStr);
58

59
  if (cache) {
×
60
    records.Write<uint16>(0x4353);
×
61
    records.Write<uint16>(sizeof(CacheBaseHeader));
×
62
    cache->meta.zipCheckupOffset = records.Tell();
×
63
    records.Write(cache->meta);
×
64
    dirSize += sizeof(CacheBaseHeader) + 4;
×
65
  }
66

67
  SafeCast(zCentral.dirSize, dirSize);
68

69
  if (forcex64) {
×
70
    ZIP64CentralDir zCentral64{};
×
71
    zCentral64.id = ZIP64CentralDir::ID;
×
72
    zCentral64.madeBy = 10;
×
73
    zCentral64.extractVersion = 10;
×
74
    zCentral64.dirRecord = 0x2C;
×
75
    zCentral64.numDiskEntries = numEntries;
×
76
    zCentral64.numDirEntries = numEntries;
×
77
    zCentral64.dirSize = dirSize;
×
78
    zCentral64.dirOffset = dirOffset;
×
79

80
    const size_t centralOffset = records.Tell();
81
    records.Write(zCentral64);
×
82

83
    ZIP64CentralDirLocator zLoca{};
×
84
    zLoca.id = ZIP64CentralDirLocator::ID;
×
85
    zLoca.centralDirOffset = centralOffset;
×
86
    records.Write(zLoca);
87
  }
88

89
  records.Write(zCentral);
90
  records.BaseStream().flush();
×
91

92
  if (cache) {
×
93
    cacheBeginCB();
×
94
    cache->meta.zipSize = records.Tell();
×
95
    BinWritter cacheWr(outputFile + ".cache");
×
96
    cache->WaitAndWrite(cacheWr);
×
97
    records.Seek(cache->meta.zipCheckupOffset);
×
98
    records.Write(cache->meta);
×
99
  }
100
}
101

102
void ZIPExtactContext::FinishFile(bool final) {
×
103
  auto SafeCast = [&](auto &where, auto &&what) {
104
    const uint64 limit =
105
        std::numeric_limits<std::decay_t<decltype(where)>>::max();
106
    bool forcex64 = false;
107

108
    if (what >= limit) {
×
109
      forcex64 = true;
110
      where = limit;
×
111
    } else {
112
      where = what;
×
113
    }
114

115
    return forcex64;
116
  };
117

118
  const bool useLocalExtendedData =
119
      SafeCast(zLocalFile.uncompressedSize, curFileSize);
120
  zLocalFile.compressedSize = zLocalFile.uncompressedSize;
×
121

122
  if (useLocalExtendedData) {
×
123
    ZIP64Extra extra;
124
    extra.compressedSize = curFileSize;
×
125
    extra.uncompressedSize = curFileSize;
×
126
    records.Write(extra);
×
127
    zLocalFile.extraFieldSize = 20;
×
128
  }
129

130
  numEntries++;
×
131
  records.Push();
132
  records.Seek(curLocalFileOffset);
×
133
  records.Write(zLocalFile);
×
134
  const size_t fileDataBegin =
135
      records.Tell() + zLocalFile.extraFieldSize + zLocalFile.fileNameSize;
×
136

137
  if (cache) {
×
138
    if (curFileName.size() > 0) {
×
139
      cache->AddFile(curFileName, fileDataBegin, curFileSize);
×
140
      cache->meta.zipCRC =
×
141
          crc32b(cache->meta.zipCRC,
×
142
                 reinterpret_cast<const char *>(&zLocalFile.crc), 4);
×
143
    }
144
  } else {
145
    fileOffsets.push_back(fileDataBegin);
×
146
  }
147

148
  records.Pop();
149

150
  ZIPFile zFile{};
×
151
  zFile.id = ZIPFile::ID;
×
152
  zFile.madeBy = 10;
×
153
  zFile.extractVersion = 10;
×
154
  zFile.lastModFileDate = zLocalFile.lastModFileDate;
×
155
  zFile.lastModFileTime = zLocalFile.lastModFileTime;
×
156
  zFile.compression = ZIPCompressionMethod::Store;
157
  zFile.compressedSize = zLocalFile.compressedSize;
×
158
  zFile.uncompressedSize = zLocalFile.uncompressedSize;
×
159
  zFile.fileNameSize = zLocalFile.fileNameSize;
×
160
  zFile.crc = zLocalFile.crc;
×
161
  const bool useFileExtendedData =
162
      SafeCast(zFile.localHeaderOffset, curLocalFileOffset);
163

164
  const bool useFileExtra = useFileExtendedData || useLocalExtendedData;
×
165
  ZIP64Extra extra;
166

167
  if (useFileExtra) {
×
168
    zFile.extraFieldSize = 4;
×
169

170
    if (useLocalExtendedData) {
×
171
      extra.uncompressedSize = curFileSize;
×
172
      extra.compressedSize = curFileSize;
×
173
      zFile.extraFieldSize += 16;
×
174
    }
175

176
    if (useFileExtendedData) {
×
177
      extra.localHeaderOffset = curLocalFileOffset;
×
178
      zFile.extraFieldSize += 8;
×
179
    }
180
  }
181

182
  if (final && cache) {
×
183
    zFile.extraFieldSize += sizeof(CacheBaseHeader) + 4;
×
184
  }
185

186
  entries.Write(zFile);
×
187
  entries.WriteContainer(prefixPath);
188
  entries.WriteContainer(curFileName);
189

190
  if (useFileExtra) {
×
191
    entries.Write(extra);
192
  }
193
}
194

195
inline std::tm localtime(std::time_t t) {
196
#ifdef _MSC_VER
197
  return *std::localtime(&t);
198
#else
199
  std::tm temp;
200
  localtime_r(&t, &temp);
×
201
  return temp;
202
#endif
203
}
204

205
void ZIPExtactContext::NewFile(const std::string &path) {
×
206
  if (path.empty()) [[unlikely]] {
×
207
    throw es::RuntimeError("NewFile path is empty");
×
208
  }
209
  AFileInfo pathInfo(path);
×
210
  auto pathSv = pathInfo.GetFullPath();
211
  if (!curFileName.empty()) {
×
212
    FinishFile();
×
213
  }
214

215
  time_t curTime =
216
      std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
×
217
  std::tm ts = localtime(curTime);
×
218

219
  struct {
220
    uint16 day : 5, month : 4, year : 7;
221
  } dosDate{uint16(ts.tm_mday), uint16(ts.tm_mon + 1), uint16(ts.tm_year - 80)};
×
222

223
  struct {
224
    uint16 second : 5, minute : 6, hour : 5;
225
  } dosTime{uint16(ts.tm_sec / 2), uint16(ts.tm_min), uint16(ts.tm_hour)};
×
226

227
  zLocalFile.lastModFileDate = reinterpret_cast<uint16 &>(dosDate);
×
228
  zLocalFile.lastModFileTime = reinterpret_cast<uint16 &>(dosTime);
×
229
  zLocalFile.compression = ZIPCompressionMethod::Store;
×
230
  zLocalFile.fileNameSize = prefixPath.size() + pathSv.size();
×
231
  zLocalFile.crc = 0;
×
232
  curFileSize = 0;
×
233

234
  curFileName = pathSv;
×
235
  curLocalFileOffset = records.Tell();
×
236
  records.Write(zLocalFile);
×
237
  records.WriteContainer(prefixPath);
238
  records.WriteContainer(pathSv);
239

240
  if (forEachFile) {
×
241
    forEachFile();
242
  }
243
}
244

245
void ZIPExtactContext::SendData(std::string_view data) {
×
246
  curFileSize += data.size();
×
247
  zLocalFile.crc = crc32b(zLocalFile.crc, data.data(), data.size());
×
248
  records.WriteContainer(data);
249
}
250

251
bool ZIPExtactContext::RequiresFolders() const { return false; }
×
252

253
void ZIPExtactContext::AddFolderPath(const std::string &) {
×
254
  throw es::ImplementationError(
255
      "AddFolderPath not supported, use RequiresFolders to check.");
×
256
}
257

258
void ZIPExtactContext::GenerateFolders() {
×
259
  throw es::ImplementationError(
260
      "GenerateFolders not supported, use RequiresFolders to check.");
×
261
}
262

263
NewTexelContext *ZIPExtactContext::NewImage(const std::string &path,
×
264
                                            NewTexelContextCreate ctx) {
265
  auto newCtx = CreateTexelContext(ctx, this, path);
×
266

267
  if (texelContext) {
×
268
    static_cast<NewTexelContextImpl *>(texelContext.get())->Finish();
269
  }
270

271
  texelContext = std::move(newCtx);
272

273
  return texelContext.get();
×
274
}
275

276
void IOExtractContext::NewFile(const std::string &path) {
×
277
  Close_();
×
278
  if (path.empty()) [[unlikely]] {
×
279
    throw es::RuntimeError("NewFile path is empty");
×
280
  }
281
  AFileInfo cfleWrap(path);
×
282
  std::string cfle(cfleWrap.GetFullPath());
×
283
  try {
284
    Open(outDir + cfle);
×
285
  } catch (const es::FileInvalidAccessError &) {
×
286
    mkdirs(outDir + cfle);
×
287
    Open(outDir + cfle);
×
288
  }
×
289

290
  if (forEachFile) {
×
291
    forEachFile();
292
  }
293
}
294

295
void IOExtractContext::SendData(std::string_view data) { WriteContainer(data); }
×
296

297
bool IOExtractContext::RequiresFolders() const { return true; }
×
298

299
void IOExtractContext::AddFolderPath(const std::string &path) {
×
300
  AFileInfo cfleWrap(path);
×
301
  auto cfle = cfleWrap.GetFullPath();
302

303
  for (auto it = cfle.begin(); it != cfle.end(); it++) {
×
304
    if (*it == '/') {
×
305
      folderTree.emplace(cfle.begin(), it);
×
306
    }
307
  }
308

309
  folderTree.emplace(path);
310
}
311

312
void IOExtractContext::GenerateFolders() {
×
313
  for (auto &f : folderTree) {
×
314
    auto genFolder = outDir + f;
×
315
    es::mkdir(genFolder);
316
  }
317

318
  folderTree.clear();
319
}
320

321
NewTexelContext *IOExtractContext::NewImage(const std::string &path,
×
322
                                            NewTexelContextCreate ctx) {
323
  auto newCtx = CreateTexelContext(ctx, this, path);
×
324

325
  if (texelContext) {
×
326
    static_cast<NewTexelContextImpl *>(texelContext.get())->Finish();
327
  }
328

329
  texelContext = std::move(newCtx);
330

331
  return texelContext.get();
×
332
}
333

334
static std::mutex ZIPLock;
335

336
void ZIPMerger::Merge(ZIPExtactContext &other, const std::string &recordsFile) {
×
337
  if (!other.curFileName.empty()) {
×
338
    other.FinishFile();
×
339
  }
340

341
  BinReaderRef localEntries(other.entriesStream);
×
342
  char buffer[0x80000];
343
  std::lock_guard<std::mutex> guard(ZIPLock);
344
  const size_t filesSize = records.Tell();
345

346
  numEntries += other.numEntries;
×
347

348
  for (auto o : other.fileOffsets) {
×
349
    ZIPFile zFile;
350
    localEntries.Read(zFile);
×
351

352
    const size_t localHeaderOffset = zFile.localHeaderOffset;
×
353
    const bool newExtraField = zFile.localHeaderOffset < 0xffffffff &&
×
354
                               localHeaderOffset + filesSize >= 0xffffffff;
×
355
    bool newExtra = false;
356

357
    if (newExtraField) {
358
      zFile.localHeaderOffset = 0xffffffff;
×
359

360
      if (!zFile.extraFieldSize) {
×
361
        zFile.extraFieldSize = 12;
×
362
        newExtra = true;
363
      } else {
364
        zFile.extraFieldSize += 8;
×
365
      }
366
    }
367

368
    if (zFile.localHeaderOffset < 0xffffffff) {
×
369
      zFile.localHeaderOffset += filesSize;
×
370
    }
371

372
    entries.Write(zFile);
×
373
    localEntries.ReadBuffer(buffer, zFile.fileNameSize);
×
374
    cache.AddFile({buffer, zFile.fileNameSize}, o + filesSize,
×
375
                  zFile.uncompressedSize);
×
376
    cache.meta.zipCRC = crc32b(cache.meta.zipCRC,
×
377
                               reinterpret_cast<const char *>(&zFile.crc), 4);
378
    entries.WriteBuffer(buffer, zFile.fileNameSize);
×
379

380
    if (newExtra) {
×
381
      ZIP64Extra extra{};
×
382
      extra.localHeaderOffset = localHeaderOffset + filesSize;
×
383
      entries.Write(extra);
384
    } else if (zFile.extraFieldSize) {
×
385
      ZIP64Extra extra{};
×
386
      localEntries.Read(extra.id);
387
      localEntries.Read(extra.size);
388

389
      if (zFile.compressedSize == 0xffffffff) {
×
390
        localEntries.Read(extra.compressedSize);
391
        localEntries.Read(extra.uncompressedSize);
392
      }
393

394
      if (zFile.localHeaderOffset == 0xffffffff) {
×
395
        if (newExtraField) {
×
396
          extra.localHeaderOffset = localHeaderOffset + filesSize;
×
397
        } else {
398
          localEntries.Read(extra.localHeaderOffset);
399
          extra.localHeaderOffset += filesSize;
×
400
        }
401
      }
402

403
      entries.Write(extra);
404
    }
405
  }
406

407
  es::Dispose(other.entriesStream);
×
408

409
  const size_t recordsSize = other.records.Tell();
410
  es::Dispose(other.records);
×
411
  BinReader rd(recordsFile);
×
412
  const size_t numBlocks = recordsSize / sizeof(buffer);
×
413
  const size_t restBytes = recordsSize % sizeof(buffer);
×
414

415
  for (size_t b = 0; b < numBlocks; b++) {
×
416
    rd.ReadBuffer(buffer, sizeof(buffer));
417
    records.WriteBuffer(buffer, sizeof(buffer));
418
  }
419

420
  if (restBytes) {
×
421
    rd.ReadBuffer(buffer, restBytes);
422
    records.WriteBuffer(buffer, restBytes);
423
  }
424
}
425

426
void ZIPMerger::FinishMerge(cache_begin_cb cacheBeginCB) {
×
427
  size_t entriesSize = entries.Tell();
×
428
  es::Dispose(entries);
×
429
  char buffer[0x80000];
430
  BinReader rd(entriesFile);
×
431
  const size_t numBlocks = entriesSize / sizeof(buffer);
×
432
  const size_t restBytes = entriesSize % sizeof(buffer);
×
433
  bool forcex64 = false;
×
434
  const size_t dirOffset = records.Tell();
435

436
  auto SafeCast = [&](auto &where, auto &&what) {
437
    const uint64 limit =
438
        std::numeric_limits<std::decay_t<decltype(where)>>::max();
439
    if (what >= limit) {
×
440
      forcex64 = true;
×
441
      where = limit;
×
442
    } else {
443
      where = what;
×
444
    }
445
  };
446

447
  ZIPCentralDir zCentral{};
×
448
  zCentral.id = ZIPCentralDir::ID;
×
449
  SafeCast(zCentral.numDirEntries, numEntries);
450
  SafeCast(zCentral.numDiskEntries, numEntries);
451
  SafeCast(zCentral.dirOffset, dirOffset);
452

453
  for (size_t b = 0; b < numBlocks; b++) {
×
454
    rd.ReadBuffer(buffer, sizeof(buffer));
455
    records.WriteBuffer(buffer, sizeof(buffer));
456
  }
457

458
  if (restBytes) {
×
459
    rd.ReadBuffer(buffer, restBytes);
460
    records.WriteBuffer(buffer, restBytes);
461
  }
462

463
  bool validCacheEntry = false;
464

465
  {
466
    // Find last zipfile entry and modify extraFieldSize
467
    const size_t skipValue = std::min(entriesSize, size_t(0x11000));
×
468
    rd.Skip(-skipValue);
×
469
    rd.ReadBuffer(buffer, skipValue);
470
    std::string_view sv(buffer, skipValue);
471
    size_t foundLastEntry = sv.rfind("PK\x01\x02");
472
    validCacheEntry = foundLastEntry != sv.npos;
473

474
    if (validCacheEntry) {
×
475
      foundLastEntry += offsetof(ZIPFile, extraFieldSize);
×
476
      char *data = buffer + foundLastEntry;
×
477
      uint16 extraFieldSize = *reinterpret_cast<uint16 *>(data);
×
478
      records.Push();
479
      records.Skip(-(skipValue - foundLastEntry));
×
480
      records.Write<uint16>(extraFieldSize + 4 + sizeof(CacheBaseHeader));
×
481
      records.Pop();
482

483
      records.Write<uint16>(0x4353);
×
484
      records.Write<uint16>(sizeof(CacheBaseHeader));
×
485
      cache.meta.zipCheckupOffset = records.Tell();
×
486
      records.Write(cache.meta);
×
487
      entriesSize += sizeof(CacheBaseHeader) + 4;
×
488
    }
489
  }
490

491
  SafeCast(zCentral.dirSize, entriesSize);
492

493
  if (forcex64) {
×
494
    ZIP64CentralDir zCentral64{};
×
495
    zCentral64.id = ZIP64CentralDir::ID;
×
496
    zCentral64.madeBy = 10;
×
497
    zCentral64.extractVersion = 10;
×
498
    zCentral64.numDiskEntries = numEntries;
×
499
    zCentral64.numDirEntries = numEntries;
×
500
    zCentral64.dirSize = entriesSize;
×
501
    zCentral64.dirOffset = dirOffset;
×
502
    zCentral64.dirRecord = 0x2C;
×
503

504
    const size_t centralOffset = records.Tell();
505
    records.Write(zCentral64);
×
506

507
    ZIP64CentralDirLocator zLoca{};
×
508
    zLoca.id = ZIP64CentralDirLocator::ID;
×
509
    zLoca.centralDirOffset = centralOffset;
×
510
    records.Write(zLoca);
511
  }
512

513
  records.Write(zCentral);
514

515
  es::Dispose(rd);
×
516
  es::RemoveFile(entriesFile);
×
517

518
  if (validCacheEntry) {
×
519
    cacheBeginCB();
×
520
    cache.meta.zipSize = records.Tell();
×
521
    BinWritter cacheWr(outFile + ".cache");
×
522
    cache.WaitAndWrite(cacheWr);
×
523
    records.Seek(cache.meta.zipCheckupOffset);
×
524
    records.Write(cache.meta);
×
525
  }
526
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc