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

PredatorCZ / PreCore / 559

25 Jul 2025 03:19PM UTC coverage: 53.212% (+0.006%) from 53.206%
559

push

github

PredatorCZ
fix file creation exception for newfile

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

331 existing lines in 4 files now uncovered.

4241 of 7970 relevant lines covered (53.21%)

10412.33 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 &) {
×
NEW
283
    mkdirs(outDir + cfle);
×
UNCOV
284
    Open(outDir + cfle);
×
285
  }
×
286

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

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

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

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

300
  for (auto it = cfle.begin(); it != cfle.end(); it++) {
×
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) {
×
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) {
320
  auto newCtx = CreateTexelContext(ctx, this, path);
×
321

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

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

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

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

349
    const size_t localHeaderOffset = zFile.localHeaderOffset;
×
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 {
361
        zFile.extraFieldSize += 8;
×
362
      }
363
    }
364

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

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

391
      if (zFile.localHeaderOffset == 0xffffffff) {
×
392
        if (newExtraField) {
×
393
          extra.localHeaderOffset = localHeaderOffset + filesSize;
×
394
        } else {
395
          localEntries.Read(extra.localHeaderOffset);
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();
407
  es::Dispose(other.records);
×
408
  BinReader rd(recordsFile);
×
409
  const size_t numBlocks = recordsSize / sizeof(buffer);
×
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

423
void ZIPMerger::FinishMerge(cache_begin_cb cacheBeginCB) {
×
424
  size_t entriesSize = entries.Tell();
×
425
  es::Dispose(entries);
×
426
  char buffer[0x80000];
427
  BinReader rd(entriesFile);
×
428
  const size_t numBlocks = entriesSize / sizeof(buffer);
×
429
  const size_t restBytes = entriesSize % sizeof(buffer);
×
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 {
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

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

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
464
    const size_t skipValue = std::min(entriesSize, size_t(0x11000));
×
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

471
    if (validCacheEntry) {
×
472
      foundLastEntry += offsetof(ZIPFile, extraFieldSize);
×
473
      char *data = buffer + foundLastEntry;
×
474
      uint16 extraFieldSize = *reinterpret_cast<uint16 *>(data);
×
475
      records.Push();
476
      records.Skip(-(skipValue - foundLastEntry));
×
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();
×
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;
×
493
    zCentral64.madeBy = 10;
×
494
    zCentral64.extractVersion = 10;
×
495
    zCentral64.numDiskEntries = numEntries;
×
496
    zCentral64.numDirEntries = numEntries;
×
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

515
  if (validCacheEntry) {
×
516
    cacheBeginCB();
×
517
    cache.meta.zipSize = records.Tell();
×
518
    BinWritter cacheWr(outFile + ".cache");
×
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