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]; }
235
249 const IndexEntry *getIndexEntry(eastl::string_view path) const;
250 template <unsigned S>
251 const IndexEntry *getIndexEntry(const char (&path)[S]) const {
252 return getIndexEntry(djb::hash<uint64_t>(path));
253 }
254 const IndexEntry *getIndexEntry(uint64_t hash) const;
255
263 return m_archiveDirentry.LBA + entry->getSectorOffset();
264 }
265
277 void setBuffer(Buffer<uint8_t> &&buffer) { m_data = eastl::move(buffer); }
278
289 template <unsigned S>
290 void readFile(const char (&path)[S], CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
291 setupQueue(getIndexEntry(path), device, eastl::move(callback));
292 m_queue.run();
293 }
294 void readFile(eastl::string_view path, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
295 setupQueue(getIndexEntry(path), device, eastl::move(callback));
296 m_queue.run();
297 }
298 void readFile(uint64_t hash, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
299 setupQueue(getIndexEntry(hash), device, eastl::move(callback));
300 m_queue.run();
301 }
302 void readFile(const IndexEntry *entry, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
303 setupQueue(entry, device, eastl::move(callback));
304 m_queue.run();
305 }
306 template <unsigned S>
307 psyqo::TaskQueue::Task scheduleReadFile(const char (&path)[S], CDRom &device) {
308 setupQueue(getIndexEntry(path), device, {});
309 return m_queue.schedule();
310 }
311 psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, CDRom &device) {
312 setupQueue(getIndexEntry(path), device, {});
313 return m_queue.schedule();
314 }
316 setupQueue(getIndexEntry(hash), device, {});
317 return m_queue.schedule();
318 }
320 setupQueue(entry, device, {});
321 return m_queue.schedule();
322 }
323 template <unsigned S>
324 ReadFileAwaiter readFile(const char (&path)[S], CDRom &device) {
325 return {getIndexEntry(path), device, *this};
326 }
327 ReadFileAwaiter readFile(eastl::string_view path, CDRom &device) { return {getIndexEntry(path), device, *this}; }
328 ReadFileAwaiter readFile(uint64_t hash, CDRom &device) { return {getIndexEntry(hash), device, *this}; }
329 ReadFileAwaiter readFile(const IndexEntry *entry, CDRom &device) { return {entry, device, *this}; }
330
346 s_decompressors[toUnderlying(IndexEntry::Method::UCL_NRV2E)] = &ArchiveManager::decompressUCL_NRV2E;
347 }
349 s_decompressors[toUnderlying(IndexEntry::Method::LZ4)] = &ArchiveManager::decompressLZ4;
350 }
351
364
365 private:
366 eastl::function<void(bool)> m_initCallback;
367 eastl::function<void(Buffer<uint8_t> &&)> m_callback;
368 psyqo::TaskQueue m_queueInitFilename;
369 psyqo::TaskQueue m_queue;
370 Buffer<uint8_t> m_data;
371 Buffer<IndexEntry> m_index;
372 ISO9660Parser::DirEntry m_archiveDirentry;
373 CDRom::ReadRequest m_request;
374 bool m_pending = false;
375 bool m_success = false;
376
377 void setupInitQueue(eastl::string_view archiveName, ISO9660Parser &parser, eastl::function<void(bool)> &&callback);
378 void setupInitQueue(uint32_t LBA, CDRom &device, eastl::function<void(bool)> &&callback);
379 void setupQueue(const IndexEntry *entry, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback);
380 uint32_t getIndexSectorCount() const {
381 static_assert(sizeof(IndexEntry) == 16, "IndexEntry size is not 16 bytes");
382 uint32_t indexSize = (getIndexCount() + 1) * sizeof(IndexEntry);
383 return (indexSize + 2047) / 2048;
384 }
385 static eastl::array<void (ArchiveManager::*)(const IndexEntry *), toUnderlying(IndexEntry::Method::COUNT)> s_decompressors;
386 void decompressUCL_NRV2E(const IndexEntry *entry);
387 void decompressLZ4(const IndexEntry *entry);
388};
389
390} // 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:315
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:302
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:277
static void registerLZ4Decompressor()
Definition archive-manager.hh:348
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:294
void readFile(uint64_t hash, CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Definition archive-manager.hh:298
const IndexEntry * getFirstIndexEntry() const
Get the First IndexEntry object.
Definition archive-manager.hh:218
static void registerAllDecompressors()
Register all decompressors.
Definition archive-manager.hh:360
static void registerUCL_NRV2EDecompressor()
Register a decompressor for a specific compression method.
Definition archive-manager.hh:345
ReadFileAwaiter readFile(const IndexEntry *entry, CDRom &device)
Definition archive-manager.hh:329
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:311
uint32_t getIndexEntrySectorStart(const IndexEntry *entry) const
Get the LBA of the first sector of the file in the archive.
Definition archive-manager.hh:262
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:290
const IndexEntry * getIndexEntry(const char(&path)[S]) const
Definition archive-manager.hh:251
ReadFileAwaiter readFile(const char(&path)[S], CDRom &device)
Definition archive-manager.hh:324
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:307
ReadFileAwaiter readFile(uint64_t hash, CDRom &device)
Definition archive-manager.hh:328
psyqo::TaskQueue::Task scheduleReadFile(const IndexEntry *entry, CDRom &device)
Definition archive-manager.hh:319
ReadFileAwaiter readFile(eastl::string_view path, CDRom &device)
Definition archive-manager.hh:327
const IndexEntry * getIndexEntry(eastl::string_view path) const
Get the IndexEntry object for a given path.
Definition archive-manager.cpp:91
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:230
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