Nugget
Loading...
Searching...
No Matches
archive-manager.hh
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2025 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29#include <EASTL/array.h>
30#include <EASTL/functional.h>
31#include <EASTL/string_view.h>
32#include <stdint.h>
33
34#include <coroutine>
35
37#include "common/util/djbhash.h"
38#include "psyqo/buffer.hh"
40#include "psyqo/task.hh"
42
43namespace psyqo::paths {
44
64 struct InitAwaiterWithFilename {
65 InitAwaiterWithFilename(eastl::string_view name, ISO9660Parser &parser, ArchiveManager &manager)
66 : m_name(name), m_parser(parser), m_manager(manager) {}
67 bool await_ready() const { return false; }
68 template <typename U>
69 void await_suspend(std::coroutine_handle<U> handle) {
70 m_manager.initialize(m_name, m_parser, [handle, this](bool success) {
71 m_success = success;
72 handle.resume();
73 });
74 }
75 bool await_resume() { return m_success; }
76
77 private:
78 eastl::string_view m_name;
79 ISO9660Parser &m_parser;
80 ArchiveManager &m_manager;
81 bool m_success;
82 };
83 struct InitAwaiter {
84 InitAwaiter(uint32_t LBA, CDRom &device, ArchiveManager &manager)
85 : m_LBA(LBA), m_device(device), m_manager(manager) {}
86 bool await_ready() const { return false; }
87 template <typename U>
88 void await_suspend(std::coroutine_handle<U> handle) {
89 m_manager.initialize(m_LBA, m_device, [handle, this](bool success) {
90 m_success = success;
91 handle.resume();
92 });
93 }
94 bool await_resume() { return m_success; }
95
96 private:
97 uint32_t m_LBA;
98 CDRom &m_device;
99 ArchiveManager &m_manager;
100 bool m_success;
101 };
102
103 public:
104 union IndexEntry;
105
106 private:
107 struct ReadFileAwaiter {
108 ReadFileAwaiter(const IndexEntry *entry, CDRom &device, ArchiveManager &manager)
109 : m_entry(entry), m_device(device), m_manager(manager) {}
110 constexpr bool await_ready() const { return false; }
111 template <typename U>
112 void await_suspend(std::coroutine_handle<U> handle) {
113 m_manager.readFile(m_entry, m_device, [handle, this](Buffer<uint8_t> &&data) {
114 m_data = eastl::move(data);
115 handle.resume();
116 });
117 }
118 Buffer<uint8_t> await_resume() { return eastl::move(m_data); }
119
120 private:
121 const IndexEntry *m_entry;
122 CDRom &m_device;
123 ArchiveManager &m_manager;
124 Buffer<uint8_t> m_data;
125 };
126
127 public:
138 enum class Method : uint32_t {
139 NONE = 0,
140 UCL_NRV2E = 1,
141 LZ4 = 2,
142 COUNT = 3,
143 };
151 // Return the decompressed size of the file in bytes.
153 // Return the padding size in bytes. This is only relevant for
154 // compressed files. The padding is used at the beginning of the
155 // compressed data to align it to a 2048 byte boundary, and
156 // allows in-place decompression of the data.
157 uint32_t getPadding() const { return entry.get<PaddingField>(); }
158 // Return the offset from the beginning of the archive to the compressed data in sectors.
159 // This includes the index sectors at the beginning of the archive.
161 // Return the size of the compressed data in sectors. For uncompressed
162 // data, this is the same as the size of the data in bytes, just rounded
163 // up to the next 2048 byte boundary.
165 // Return the compression method used to compress the data.
168 struct {
169 uint64_t hash;
171 };
172 };
173
186 void initialize(eastl::string_view archiveName, ISO9660Parser &parser, eastl::function<void(bool)> &&callback) {
187 setupInitQueue(archiveName, parser, eastl::move(callback));
188 m_queueInitFilename.run();
189 }
190 psyqo::TaskQueue::Task scheduleInitialize(eastl::string_view archiveName, ISO9660Parser &parser) {
191 setupInitQueue(archiveName, parser, {});
192 return m_queueInitFilename.schedule();
193 }
194 InitAwaiterWithFilename initialize(eastl::string_view archiveName, ISO9660Parser &parser) {
195 return {archiveName, parser, *this};
196 }
197 void initialize(uint32_t LBA, CDRom &device, eastl::function<void(bool)> &&callback) {
198 setupInitQueue(LBA, device, eastl::move(callback));
199 m_queue.run();
200 }
202 setupInitQueue(LBA, device, {});
203 return m_queue.schedule();
204 }
205 InitAwaiter initialize(uint32_t LBA, CDRom &device) { return {LBA, device, *this}; }
206
219 if (m_index.size() == 0) {
220 return nullptr;
221 }
222 return &m_index[1];
223 }
224
234 uint32_t getIndexCount() const { return m_index[0].asArray[2] & 0xffffff; }
235
245 uint32_t getSafeMarginLength() const { return ((m_index[0].asArray[2] >> 24) + 1) * 16; }
246
260 const IndexEntry *getIndexEntry(eastl::string_view path) const;
261 template <unsigned S>
262 const IndexEntry *getIndexEntry(const char (&path)[S]) const {
263 return getIndexEntry(djb::hash<uint64_t>(path));
264 }
265 const IndexEntry *getIndexEntry(uint64_t hash) const;
266
274 return m_archiveDirentry.LBA + entry->getSectorOffset();
275 }
276
288 void setBuffer(Buffer<uint8_t> &&buffer) { m_data = eastl::move(buffer); }
289
300 template <unsigned S>
301 void readFile(const char (&path)[S], CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
302 setupQueue(getIndexEntry(path), device, eastl::move(callback));
303 m_queue.run();
304 }
305 void readFile(eastl::string_view path, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
306 setupQueue(getIndexEntry(path), device, eastl::move(callback));
307 m_queue.run();
308 }
309 void readFile(uint64_t hash, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
310 setupQueue(getIndexEntry(hash), device, eastl::move(callback));
311 m_queue.run();
312 }
313 void readFile(const IndexEntry *entry, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
314 setupQueue(entry, device, eastl::move(callback));
315 m_queue.run();
316 }
317 template <unsigned S>
318 psyqo::TaskQueue::Task scheduleReadFile(const char (&path)[S], CDRom &device) {
319 setupQueue(getIndexEntry(path), device, {});
320 return m_queue.schedule();
321 }
322 psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, CDRom &device) {
323 setupQueue(getIndexEntry(path), device, {});
324 return m_queue.schedule();
325 }
327 setupQueue(getIndexEntry(hash), device, {});
328 return m_queue.schedule();
329 }
331 setupQueue(entry, device, {});
332 return m_queue.schedule();
333 }
334 template <unsigned S>
335 ReadFileAwaiter readFile(const char (&path)[S], CDRom &device) {
336 return {getIndexEntry(path), device, *this};
337 }
338 ReadFileAwaiter readFile(eastl::string_view path, CDRom &device) { return {getIndexEntry(path), device, *this}; }
339 ReadFileAwaiter readFile(uint64_t hash, CDRom &device) { return {getIndexEntry(hash), device, *this}; }
340 ReadFileAwaiter readFile(const IndexEntry *entry, CDRom &device) { return {entry, device, *this}; }
341
357 s_decompressors[toUnderlying(IndexEntry::Method::UCL_NRV2E)] = &ArchiveManager::decompressUCL_NRV2E;
358 }
360 s_decompressors[toUnderlying(IndexEntry::Method::LZ4)] = &ArchiveManager::decompressLZ4;
361 }
362
375
376 private:
377 eastl::function<void(bool)> m_initCallback;
378 eastl::function<void(Buffer<uint8_t> &&)> m_callback;
379 psyqo::TaskQueue m_queueInitFilename;
380 psyqo::TaskQueue m_queue;
381 Buffer<uint8_t> m_data;
382 Buffer<IndexEntry> m_index;
383 ISO9660Parser::DirEntry m_archiveDirentry;
384 CDRom::ReadRequest m_request;
385 bool m_pending = false;
386 bool m_success = false;
387
388 void setupInitQueue(eastl::string_view archiveName, ISO9660Parser &parser, eastl::function<void(bool)> &&callback);
389 void setupInitQueue(uint32_t LBA, CDRom &device, eastl::function<void(bool)> &&callback);
390 void setupQueue(const IndexEntry *entry, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback);
391 uint32_t getIndexSectorCount() const {
392 static_assert(sizeof(IndexEntry) == 16, "IndexEntry size is not 16 bytes");
393 uint32_t indexSize = (getIndexCount() + 1) * sizeof(IndexEntry);
394 return (indexSize + 2047) / 2048;
395 }
396 static eastl::array<void (ArchiveManager::*)(const IndexEntry *), toUnderlying(IndexEntry::Method::COUNT)> s_decompressors;
397 void decompressUCL_NRV2E(const IndexEntry *entry);
398 void decompressLZ4(const IndexEntry *entry);
399};
400
401} // namespace psyqo::paths
A class that manages a buffer of data.
Definition buffer.hh:63
The base CDRom class.
Definition cdrom.hh:46
An ISO9660 parser.
Definition iso9660-parser.hh:48
The Task class.
Definition task.hh:140
A task queue for processing tasks sequentially.
Definition task.hh:46
Task schedule()
Schedules the task queue to another task queue.
Definition task.cpp:68
void run()
Runs the task queue.
Definition task.cpp:61
This class manages the reading and decompression of files from an archive.
Definition archive-manager.hh:63
psyqo::TaskQueue::Task scheduleReadFile(uint64_t hash, CDRom &device)
Definition archive-manager.hh:326
void initialize(eastl::string_view archiveName, ISO9660Parser &parser, eastl::function< void(bool)> &&callback)
Asynchronous initialization of the archive manager.
Definition archive-manager.hh:186
InitAwaiterWithFilename initialize(eastl::string_view archiveName, ISO9660Parser &parser)
Definition archive-manager.hh:194
void readFile(const IndexEntry *entry, CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Definition archive-manager.hh:313
uint32_t getIndexCount() const
Get the number of entries in the index.
Definition archive-manager.hh:234
void setBuffer(Buffer< uint8_t > &&buffer)
Set the Buffer object for the next read operation.
Definition archive-manager.hh:288
static void registerLZ4Decompressor()
Definition archive-manager.hh:359
psyqo::TaskQueue::Task scheduleInitialize(eastl::string_view archiveName, ISO9660Parser &parser)
Definition archive-manager.hh:190
void readFile(eastl::string_view path, CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Definition archive-manager.hh:305
void readFile(uint64_t hash, CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Definition archive-manager.hh:309
const IndexEntry * getFirstIndexEntry() const
Get the First IndexEntry object.
Definition archive-manager.hh:218
static void registerAllDecompressors()
Register all decompressors.
Definition archive-manager.hh:371
static void registerUCL_NRV2EDecompressor()
Register a decompressor for a specific compression method.
Definition archive-manager.hh:356
ReadFileAwaiter readFile(const IndexEntry *entry, CDRom &device)
Definition archive-manager.hh:340
psyqo::TaskQueue::Task scheduleInitialize(uint32_t LBA, CDRom &device)
Definition archive-manager.hh:201
psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, CDRom &device)
Definition archive-manager.hh:322
uint32_t getIndexEntrySectorStart(const IndexEntry *entry) const
Get the LBA of the first sector of the file in the archive.
Definition archive-manager.hh:273
InitAwaiter initialize(uint32_t LBA, CDRom &device)
Definition archive-manager.hh:205
void readFile(const char(&path)[S], CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Read a file from the archive.
Definition archive-manager.hh:301
const IndexEntry * getIndexEntry(const char(&path)[S]) const
Definition archive-manager.hh:262
ReadFileAwaiter readFile(const char(&path)[S], CDRom &device)
Definition archive-manager.hh:335
void initialize(uint32_t LBA, CDRom &device, eastl::function< void(bool)> &&callback)
Definition archive-manager.hh:197
psyqo::TaskQueue::Task scheduleReadFile(const char(&path)[S], CDRom &device)
Definition archive-manager.hh:318
ReadFileAwaiter readFile(uint64_t hash, CDRom &device)
Definition archive-manager.hh:339
psyqo::TaskQueue::Task scheduleReadFile(const IndexEntry *entry, CDRom &device)
Definition archive-manager.hh:330
ReadFileAwaiter readFile(eastl::string_view path, CDRom &device)
Definition archive-manager.hh:338
const IndexEntry * getIndexEntry(eastl::string_view path) const
Get the IndexEntry object for a given path.
Definition archive-manager.cpp:91
uint32_t getSafeMarginLength() const
Get the safe margin length for decompression.
Definition archive-manager.hh:245
Definition archive-manager.hh:43
constexpr std::underlying_type_t< E > toUnderlying(E v)
Definition utility-polyfill.h:38
A bit field that can hold multiple bit field elements of different types.
Definition bitfield.hh:155
constexpr Field::Type get() const
Definition bitfield.hh:157
A bit field element to be used in a BitField.
Definition bitfield.hh:130
An asynchronous read request.
Definition cdrom.hh:78
An ISO9660 directory entry.
Definition iso9660-parser.hh:56
uint32_t LBA
Definition iso9660-parser.hh:57
static void * buffer
Definition syscalls.h:231
void void(ptr, size)
void uint32_t(classId, spec)
The IndexEntry struct represents an entry in the archive index.
Definition archive-manager.hh:137
Utilities::BitSpan< uint32_t, 19 > SectorOffsetField
Definition archive-manager.hh:146
Utilities::BitSpan< Method, 3 > MethodField
Definition archive-manager.hh:148
uint32_t getDecompSize() const
Definition archive-manager.hh:152
Method
Definition archive-manager.hh:138
CompressedEntry entry
Definition archive-manager.hh:170
uint32_t getCompressedSize() const
Definition archive-manager.hh:164
uint32_t getSectorOffset() const
Definition archive-manager.hh:160
Utilities::BitSpan< uint32_t, 10 > CompressedSizeField
Definition archive-manager.hh:147
Utilities::BitField< DecompSizeField, PaddingField, SectorOffsetField, CompressedSizeField, MethodField > CompressedEntry
Definition archive-manager.hh:150
uint64_t hash
Definition archive-manager.hh:169
Utilities::BitSpan< uint32_t, 21 > DecompSizeField
Definition archive-manager.hh:144
uint32_t getPadding() const
Definition archive-manager.hh:157
Method getCompressionMethod() const
Definition archive-manager.hh:166
Utilities::BitSpan< uint32_t, 11 > PaddingField
Definition archive-manager.hh:145
uint32_t asArray[4]
Definition archive-manager.hh:167