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

PredatorCZ / RevilLib / 186

21 Apr 2026 08:16PM UTC coverage: 8.612%. Remained the same
186

push

github

PredatorCZ
update sdl

3 of 459 new or added lines in 1 file covered. (0.65%)

676 existing lines in 2 files now uncovered.

762 of 8848 relevant lines covered (8.61%)

4773.72 hits per line

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

0.0
/src/arc.cpp
1
/*  Revil Format Library
2
    Copyright(C) 2020-2026 Lukas Cone
3

4
    This program is free software : you can redistribute it and / or modify
5
    it under the terms of the GNU General Public License as published by
6
    the Free Software Foundation, either version 3 of the License, or
7
    (at your option) any later version.
8

9
    This program is distributed in the hope that it will be useful,
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12
    GNU General Public License for more details.
13

14
    You should have received a copy of the GNU General Public License
15
    along with this program.If not, see <https://www.gnu.org/licenses/>.
16
*/
17

18
#include "revil/arc.hpp"
19
#include "arc.hpp"
20
#include "hfs.hpp"
21
#include "revil/hashreg.hpp"
22
#include "spike/crypto/blowfish.h"
23
#include "spike/io/fileinfo.hpp"
24
#include "spike/master_printer.hpp"
25
#include <algorithm>
26
#include <set>
27

28
#include "lzx.h"
29
#include "mspack.h"
30
#include "zlib.h"
31

32
#pragma region XMemDecompress
33

34
struct mspack_file {
35
  uint8 *buffer;
36
  uint32 bufferSize;
37
  uint32 position;
38
  uint32 rest;
39
};
40

41
// https://github.com/gildor2/UEViewer/blob/master/Unreal/UnCoreCompression.cpp#L90
42
static int mspack_read(mspack_file *file, void *buffer, int bytes) {
UNCOV
43
  if (!file->rest) {
×
44
    // read block header
UNCOV
45
    if (file->buffer[file->position] == 0xFF) {
×
46
      // [0]   = FF
47
      // [1,2] = uncompressed block size
48
      // [3,4] = compressed block size
49
      file->rest = (file->buffer[file->position + 3] << 8) |
×
50
                   file->buffer[file->position + 4];
×
UNCOV
51
      file->position += 5;
×
52
    } else {
53
      // [0,1] = compressed size
54
      file->rest = (file->buffer[file->position + 0] << 8) |
×
55
                   file->buffer[file->position + 1];
×
UNCOV
56
      file->position += 2;
×
57
    }
58

59
    if (file->rest > file->bufferSize - file->position) {
×
UNCOV
60
      file->rest = file->bufferSize - file->position;
×
61
    }
62
  }
63

64
  if (bytes > file->rest) {
×
UNCOV
65
    bytes = file->rest;
×
66
  }
67

UNCOV
68
  if (bytes <= 0) {
×
69
    return 0;
70
  }
71

72
  memcpy(buffer, file->buffer + file->position, bytes);
×
73
  file->position += bytes;
×
UNCOV
74
  file->rest -= bytes;
×
75

UNCOV
76
  return bytes;
×
77
}
78

79
static int mspack_write(mspack_file *file, void *buffer, int bytes) {
UNCOV
80
  if (bytes <= 0) {
×
81
    return 0;
82
  }
83

84
  memcpy(file->buffer + file->position, buffer, bytes);
×
85
  file->position += bytes;
×
UNCOV
86
  return bytes;
×
87
}
88

89
static mspack_system mspackSystem{
90
    nullptr,                                                     // open
91
    nullptr,                                                     // close
92
    mspack_read,                                                 // read
93
    mspack_write,                                                // write
94
    nullptr,                                                     // seek
95
    nullptr,                                                     // tell
96
    nullptr,                                                     // message
97
    [](mspack_system *, size_t bytes) { return malloc(bytes); }, // alloc
98
    free,                                                        // free
99
    [](void *src, void *dst, size_t bytes) { memcpy(dst, src, bytes); }, // copy
100
};
101

102
static void DecompressLZX(char *inBuffer, uint32 compressedSize,
103
                          char *outBuffer, uint32 uncompressedSize,
104
                          uint32 wBits) {
105
  mspack_file inStream{};
×
106
  mspack_file outStream{};
×
107
  inStream.buffer = reinterpret_cast<uint8 *>(inBuffer);
×
108
  inStream.bufferSize = compressedSize;
×
109
  outStream.buffer = reinterpret_cast<uint8 *>(outBuffer);
×
UNCOV
110
  outStream.bufferSize = uncompressedSize;
×
111

UNCOV
112
  lzxd_stream *lzxd = lzxd_init(&mspackSystem, &inStream, &outStream, wBits, 0,
×
113
                                1 << wBits, uncompressedSize, false);
114

UNCOV
115
  int retVal = lzxd_decompress(lzxd, uncompressedSize);
×
116

117
  if (retVal != MSPACK_ERR_OK) {
×
118
    throw std::runtime_error("LZX decompression error " +
×
UNCOV
119
                             std::to_string(retVal));
×
120
  }
121

UNCOV
122
  lzxd_free(lzxd);
×
123
}
124

125
#pragma endregion
126

UNCOV
127
auto ReadARCC(BinReaderRef_e rd, BlowfishEncoder &enc) {
×
128
  ARC hdr;
129
  rd.Read(hdr);
×
UNCOV
130
  rd.Skip(-4);
×
131

132
  if (hdr.id != ARCCID) {
×
UNCOV
133
    throw es::InvalidHeaderError(hdr.id);
×
134
  }
135

136
  ARCFiles files;
×
UNCOV
137
  rd.ReadContainer(files, hdr.numFiles);
×
138

139
  auto buffer = reinterpret_cast<char *>(files.data());
UNCOV
140
  size_t bufferSize = sizeof(ARCFile) * hdr.numFiles;
×
141

UNCOV
142
  enc.Decode(buffer, bufferSize);
×
143

UNCOV
144
  return std::make_tuple(hdr, files);
×
145
}
146

UNCOV
147
void revil::EnumerateArchive(BinReaderRef_e rd, Platform platform,
×
148
                             std::string_view title,
149
                             std::function<AppExtractContext *()> demandContext,
150
                             const std::set<uint32> &classFilter,
151
                             bool filterIsBlackList) {
152
  uint32 id;
153
  rd.Push();
UNCOV
154
  rd.Read(id);
×
155
  rd.Pop();
156
  ARC hdr;
157

158
  std::stringstream backup;
×
159
  if (id == SFHID) {
×
160
    backup = ProcessHFS(rd);
×
UNCOV
161
    rd = BinReaderRef_e(backup);
×
162
    rd.Push();
UNCOV
163
    rd.Read(id);
×
164
    rd.Pop();
165
  }
166

UNCOV
167
  Platform autoPlatform = id == CRAID ? Platform::PS3 : Platform::Win32;
×
168

169
  if (platform != Platform::Auto) {
×
UNCOV
170
    if (revil::IsPlatformBigEndian(autoPlatform) !=
×
171
        revil::IsPlatformBigEndian(platform)) {
UNCOV
172
      printwarning("Platform setting mistmatch, using fallback platform: "
×
173
                   << (id == CRAID ? "PS3" : "Win32"));
174
    }
175
  } else {
UNCOV
176
    platform = autoPlatform;
×
177
  }
178

UNCOV
179
  BlowfishEncoder enc;
×
180

181
  auto WriteFiles = [&](auto &files) {
×
182
    auto ectx = demandContext();
×
183
    if (ectx->RequiresFolders()) {
×
184
      for (auto &f : files) {
×
185
        if (classFilter.size() > 0 &&
×
186
            classFilter.contains(f.typeHash) == filterIsBlackList) {
×
UNCOV
187
          continue;
×
188
        }
189

190
        AFileInfo inf(f.fileName);
×
UNCOV
191
        std::string cFolder(inf.GetFolder());
×
192
        std::transform(cFolder.begin(), cFolder.end(), cFolder.begin(),
193
                       [](char c) { return tolower(c); });
×
UNCOV
194
        ectx->AddFolderPath(cFolder);
×
195
      }
196

UNCOV
197
      ectx->GenerateFolders();
×
198
    }
199

200
    std::string inBuffer;
201
    std::string outBuffer;
UNCOV
202
    [&inBuffer, &outBuffer, &files] {
×
203
      size_t maxSize = 0;
204
      size_t maxSizeUnc = 0;
205

206
      for (auto &f : files) {
×
UNCOV
207
        if (f.uncompressedSize > maxSizeUnc) {
×
208
          maxSizeUnc = f.uncompressedSize;
209
        }
210

UNCOV
211
        if (f.compressedSize > maxSize) {
×
212
          maxSize = f.compressedSize;
213
        }
214
      }
215

UNCOV
216
      if (maxSizeUnc < 0x8000) {
×
217
        maxSizeUnc = 0x8000;
218
      }
219

220
      inBuffer.resize(maxSize);
221
      outBuffer.resize(maxSizeUnc);
UNCOV
222
    }();
×
223

224
    for (auto &f : files) {
×
225
      if (!f.compressedSize) {
×
UNCOV
226
        continue;
×
227
      }
228

229
      if (classFilter.size() > 0 &&
×
230
          classFilter.contains(f.typeHash) == filterIsBlackList) {
×
UNCOV
231
        continue;
×
232
      }
233

UNCOV
234
      rd.Seek(f.offset);
×
235

UNCOV
236
      if (platform == Platform::PS3 && f.compressedSize == f.uncompressedSize) {
×
237
        rd.ReadBuffer(&outBuffer[0], f.compressedSize);
238

239
        if (id == ARCCID) {
×
UNCOV
240
          enc.Decode(&outBuffer[0], f.compressedSize);
×
241
        }
242
      } else {
UNCOV
243
        rd.ReadBuffer(&inBuffer[0], f.compressedSize);
×
244

245
        if (id == ARCCID) {
×
UNCOV
246
          enc.Decode(&inBuffer[0], f.compressedSize);
×
247
        }
248

249
        if (hdr.IsLZX()) {
×
250
          DecompressLZX(&inBuffer[0], f.compressedSize, &outBuffer[0],
×
UNCOV
251
                        f.uncompressedSize, id == ARCID ? 17 : 15);
×
252
        } else {
253
          z_stream infstream;
254
          infstream.zalloc = Z_NULL;
×
255
          infstream.zfree = Z_NULL;
×
256
          infstream.opaque = Z_NULL;
×
257
          infstream.avail_in = f.compressedSize;
×
258
          infstream.next_in = reinterpret_cast<Bytef *>(&inBuffer[0]);
×
259
          infstream.avail_out = outBuffer.size();
×
260
          infstream.next_out = reinterpret_cast<Bytef *>(&outBuffer[0]);
×
261
          inflateInit(&infstream);
×
262
          int state = inflate(&infstream, Z_FINISH);
×
UNCOV
263
          inflateEnd(&infstream);
×
264

265
          if (state < 0) {
×
UNCOV
266
            throw std::runtime_error(infstream.msg);
×
267
          }
268
        }
269
      }
270

271
      auto ext = revil::GetExtension(f.typeHash, title, platform);
×
UNCOV
272
      std::string filePath = f.fileName;
×
273
      std::transform(filePath.begin(), filePath.end(), filePath.begin(),
274
                     [](char c) { return tolower(c); });
×
UNCOV
275
      filePath.push_back('.');
×
276

277
      if (ext.empty()) {
×
278
        char buffer[0x10]{};
×
UNCOV
279
        snprintf(buffer, sizeof(buffer), "%.8" PRIX32, f.typeHash);
×
280
        filePath += buffer;
281
      } else {
282
        filePath.append(ext);
283
      }
284

285
      ectx->NewFile(filePath);
×
UNCOV
286
      ectx->SendData({outBuffer.data(), f.uncompressedSize});
×
287
    }
288
  };
289

×
290
  auto ts = revil::GetTitleSupport(title, platform);
×
291

×
292
  if (ts->arc.flags & revil::DbArc_ExtendedPath) {
×
293
    ARCExtendedFiles files;
×
294
    std::tie(hdr, files) = ReadExtendedARC(rd);
×
UNCOV
295
    WriteFiles(files);
×
296
  } else {
297
    ARCFiles files;
298
    if (id == ARCCID) {
×
UNCOV
299
      std::string_view key(ts->arc.key);
×
300

301
      if (key.empty()) {
×
UNCOV
302
        throw es::RuntimeError(
×
303
            "Encrypted archives not supported for this title");
304
      }
UNCOV
305
      enc.SetKey(key);
×
306
      std::tie(hdr, files) = ReadARCC(rd, enc);
307
    } else {
308
      std::tie(hdr, files) = ReadARC(rd);
309
    }
UNCOV
310
    WriteFiles(files);
×
311
  }
312
}
313

314
size_t revil::CompressZlib(std::string_view inBuffer, std::string &outBuffer,
315
                           int windowSize, int level) {
316
  z_stream infstream;
317
  infstream.zalloc = Z_NULL;
318
  infstream.zfree = Z_NULL;
319
  infstream.opaque = Z_NULL;
320
  infstream.avail_in = inBuffer.size();
321
  infstream.next_in =
322
      const_cast<Bytef *>(reinterpret_cast<const Bytef *>(inBuffer.data()));
323
  infstream.avail_out = outBuffer.size();
324
  infstream.next_out = reinterpret_cast<Bytef *>(outBuffer.data());
325

326
  deflateInit2(&infstream, level, Z_DEFLATED, windowSize, 8,
327
               Z_DEFAULT_STRATEGY);
328
  int state = deflate(&infstream, Z_FINISH);
329
  deflateEnd(&infstream);
UNCOV
330

×
331
  if (state != Z_STREAM_END) {
332
    throw es::RuntimeError("Compression Error!");
×
333
  }
×
UNCOV
334

×
335
  return infstream.total_out;
336
}
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