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

PredatorCZ / RevilLib / 185

21 Apr 2026 07:59PM UTC coverage: 8.612% (-2.1%) from 10.756%
185

push

github

PredatorCZ
update sdl

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

2404 existing lines in 10 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 <set>
26

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

31
#pragma region XMemDecompress
32

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

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

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

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

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

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

75
  return bytes;
×
76
}
77

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

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

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

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

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

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

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

121
  lzxd_free(lzxd);
×
122
}
123

124
#pragma endregion
125

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

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

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

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

141
  enc.Decode(buffer, bufferSize);
×
142

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

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

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

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

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

UNCOV
178
  BlowfishEncoder enc;
×
179

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

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

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

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

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

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

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

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

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

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

233
      rd.Seek(f.offset);
×
234

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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