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

PredatorCZ / PreCore / 560

31 Aug 2025 01:43PM UTC coverage: 52.372% (-0.8%) from 53.22%
560

push

github

PredatorCZ
update includes and types

1 of 1 new or added line in 1 file covered. (100.0%)

735 existing lines in 15 files now uncovered.

4128 of 7882 relevant lines covered (52.37%)

10524.15 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
    cache->AddFile(curFileName, fileDataBegin, curFileSize);
×
139
    cache->meta.zipCRC = crc32b(
×
140
        cache->meta.zipCRC, reinterpret_cast<const char *>(&zLocalFile.crc), 4);
×
141
  } else {
142
    fileOffsets.push_back(fileDataBegin);
×
143
  }
144

145
  records.Pop();
146

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

161
  const bool useFileExtra = useFileExtendedData || useLocalExtendedData;
×
162
  ZIP64Extra extra;
163

164
  if (useFileExtra) {
×
165
    zFile.extraFieldSize = 4;
×
166

167
    if (useLocalExtendedData) {
×
168
      extra.uncompressedSize = curFileSize;
×
169
      extra.compressedSize = curFileSize;
×
170
      zFile.extraFieldSize += 16;
×
171
    }
172

173
    if (useFileExtendedData) {
×
174
      extra.localHeaderOffset = curLocalFileOffset;
×
175
      zFile.extraFieldSize += 8;
×
176
    }
177
  }
178

179
  if (final && cache) {
×
180
    zFile.extraFieldSize += sizeof(CacheBaseHeader) + 4;
×
181
  }
182

183
  entries.Write(zFile);
×
184
  entries.WriteContainer(prefixPath);
185
  entries.WriteContainer(curFileName);
186

187
  if (useFileExtra) {
×
188
    entries.Write(extra);
189
  }
190
}
191

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

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

212
  time_t curTime =
213
      std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
×
214
  std::tm ts = localtime(curTime);
×
215

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

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

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

231
  curFileName = pathSv;
×
232
  curLocalFileOffset = records.Tell();
×
233
  records.Write(zLocalFile);
×
234
  records.WriteContainer(prefixPath);
235
  records.WriteContainer(pathSv);
236

237
  if (forEachFile) {
×
238
    forEachFile();
239
  }
240
}
241

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

248
bool ZIPExtactContext::RequiresFolders() const { return false; }
×
249

250
void ZIPExtactContext::AddFolderPath(const std::string &) {
×
251
  throw std::logic_error(
×
252
      "AddFolderPath not supported, use RequiresFolders to check.");
×
253
}
254

255
void ZIPExtactContext::GenerateFolders() {
×
256
  throw std::logic_error(
×
257
      "GenerateFolders not supported, use RequiresFolders to check.");
×
258
}
259

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

264
  if (texelContext) {
×
265
    static_cast<NewTexelContextImpl *>(texelContext.get())->Finish();
266
  }
267

268
  texelContext = std::move(newCtx);
269

270
  return texelContext.get();
×
271
}
272

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

UNCOV
287
  if (forEachFile) {
×
288
    forEachFile();
289
  }
290
}
291

292
void IOExtractContext::SendData(std::string_view data) { WriteContainer(data); }
×
293

UNCOV
294
bool IOExtractContext::RequiresFolders() const { return true; }
×
295

UNCOV
296
void IOExtractContext::AddFolderPath(const std::string &path) {
×
UNCOV
297
  AFileInfo cfleWrap(path);
×
298
  auto cfle = cfleWrap.GetFullPath();
299

300
  for (auto it = cfle.begin(); it != cfle.end(); it++) {
×
UNCOV
301
    if (*it == '/') {
×
302
      folderTree.emplace(cfle.begin(), it);
×
303
    }
304
  }
305

306
  folderTree.emplace(path);
307
}
308

309
void IOExtractContext::GenerateFolders() {
×
310
  for (auto &f : folderTree) {
×
UNCOV
311
    auto genFolder = outDir + f;
×
312
    es::mkdir(genFolder);
313
  }
314

315
  folderTree.clear();
316
}
317

318
NewTexelContext *IOExtractContext::NewImage(const std::string &path,
×
319
                                            NewTexelContextCreate ctx) {
UNCOV
320
  auto newCtx = CreateTexelContext(ctx, this, path);
×
321

UNCOV
322
  if (texelContext) {
×
323
    static_cast<NewTexelContextImpl *>(texelContext.get())->Finish();
324
  }
325

326
  texelContext = std::move(newCtx);
327

328
  return texelContext.get();
×
329
}
330

331
static std::mutex ZIPLock;
332

UNCOV
333
void ZIPMerger::Merge(ZIPExtactContext &other, const std::string &recordsFile) {
×
UNCOV
334
  if (!other.curFileName.empty()) {
×
UNCOV
335
    other.FinishFile();
×
336
  }
337

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

343
  numEntries += other.numEntries;
×
344

UNCOV
345
  for (auto o : other.fileOffsets) {
×
346
    ZIPFile zFile;
UNCOV
347
    localEntries.Read(zFile);
×
348

UNCOV
349
    const size_t localHeaderOffset = zFile.localHeaderOffset;
×
UNCOV
350
    const bool newExtraField = zFile.localHeaderOffset < 0xffffffff &&
×
351
                               localHeaderOffset + filesSize >= 0xffffffff;
×
352
    bool newExtra = false;
353

354
    if (newExtraField) {
355
      zFile.localHeaderOffset = 0xffffffff;
×
356

357
      if (!zFile.extraFieldSize) {
×
358
        zFile.extraFieldSize = 12;
×
359
        newExtra = true;
360
      } else {
UNCOV
361
        zFile.extraFieldSize += 8;
×
362
      }
363
    }
364

365
    if (zFile.localHeaderOffset < 0xffffffff) {
×
366
      zFile.localHeaderOffset += filesSize;
×
367
    }
368

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

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

386
      if (zFile.compressedSize == 0xffffffff) {
×
387
        localEntries.Read(extra.compressedSize);
388
        localEntries.Read(extra.uncompressedSize);
389
      }
390

UNCOV
391
      if (zFile.localHeaderOffset == 0xffffffff) {
×
UNCOV
392
        if (newExtraField) {
×
UNCOV
393
          extra.localHeaderOffset = localHeaderOffset + filesSize;
×
394
        } else {
395
          localEntries.Read(extra.localHeaderOffset);
UNCOV
396
          extra.localHeaderOffset += filesSize;
×
397
        }
398
      }
399

400
      entries.Write(extra);
401
    }
402
  }
403

404
  es::Dispose(other.entriesStream);
×
405

406
  const size_t recordsSize = other.records.Tell();
UNCOV
407
  es::Dispose(other.records);
×
UNCOV
408
  BinReader rd(recordsFile);
×
UNCOV
409
  const size_t numBlocks = recordsSize / sizeof(buffer);
×
UNCOV
410
  const size_t restBytes = recordsSize % sizeof(buffer);
×
411

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

417
  if (restBytes) {
×
418
    rd.ReadBuffer(buffer, restBytes);
419
    records.WriteBuffer(buffer, restBytes);
420
  }
421
}
422

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

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

444
  ZIPCentralDir zCentral{};
×
445
  zCentral.id = ZIPCentralDir::ID;
×
446
  SafeCast(zCentral.numDirEntries, numEntries);
447
  SafeCast(zCentral.numDiskEntries, numEntries);
448
  SafeCast(zCentral.dirOffset, dirOffset);
449

UNCOV
450
  for (size_t b = 0; b < numBlocks; b++) {
×
451
    rd.ReadBuffer(buffer, sizeof(buffer));
452
    records.WriteBuffer(buffer, sizeof(buffer));
453
  }
454

UNCOV
455
  if (restBytes) {
×
456
    rd.ReadBuffer(buffer, restBytes);
457
    records.WriteBuffer(buffer, restBytes);
458
  }
459

460
  bool validCacheEntry = false;
461

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

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

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

488
  SafeCast(zCentral.dirSize, entriesSize);
489

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

501
    const size_t centralOffset = records.Tell();
502
    records.Write(zCentral64);
×
503

504
    ZIP64CentralDirLocator zLoca{};
×
505
    zLoca.id = ZIP64CentralDirLocator::ID;
×
506
    zLoca.centralDirOffset = centralOffset;
×
507
    records.Write(zLoca);
508
  }
509

510
  records.Write(zCentral);
511

512
  es::Dispose(rd);
×
513
  es::RemoveFile(entriesFile);
×
514

UNCOV
515
  if (validCacheEntry) {
×
UNCOV
516
    cacheBeginCB();
×
UNCOV
517
    cache.meta.zipSize = records.Tell();
×
UNCOV
518
    BinWritter cacheWr(outFile + ".cache");
×
UNCOV
519
    cache.WaitAndWrite(cacheWr);
×
520
    records.Seek(cache.meta.zipCheckupOffset);
×
521
    records.Write(cache.meta);
×
522
  }
523
}
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