Nugget
Bare-metal libraries and examples for the original PlayStation
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 // Raw sectors stored as-is, such as an STR movie with interleaved
143 // XA audio, meant to be streamed rather than loaded. The entry's
144 // first 32-bit field holds the sector count instead of the
145 // decompressed size and padding. `readFile` refuses these.
146 STREAM = 3,
147 COUNT = 4,
148 };
156 // Return the decompressed size of the file in bytes.
157 uint32_t getDecompSize() const { return entry.get<DecompSizeField>(); }
158 // Return the padding size in bytes. This is only relevant for
159 // compressed files. The padding is used at the beginning of the
160 // compressed data to align it to a 2048 byte boundary, and
161 // allows in-place decompression of the data.
162 uint32_t getPadding() const { return entry.get<PaddingField>(); }
163 // Return the offset from the beginning of the archive to the compressed data in sectors.
164 // This includes the index sectors at the beginning of the archive.
165 uint32_t getSectorOffset() const { return entry.get<SectorOffsetField>(); }
166 // Return the size of the compressed data in sectors. For uncompressed
167 // data, this is the same as the size of the data in bytes, just rounded
168 // up to the next 2048 byte boundary.
169 uint32_t getCompressedSize() const { return entry.get<CompressedSizeField>(); }
170 // Return the compression method used to compress the data.
172 // Return the number of sectors of a STREAM entry.
173 uint32_t getStreamSectors() const { return asArray[2]; }
174 uint32_t asArray[4];
175 struct {
176 uint64_t hash;
178 };
179 };
180
193 void initialize(eastl::string_view archiveName, ISO9660Parser &parser, eastl::function<void(bool)> &&callback) {
194 setupInitQueue(archiveName, parser, eastl::move(callback));
195 m_queueInitFilename.run();
196 }
197 psyqo::TaskQueue::Task scheduleInitialize(eastl::string_view archiveName, ISO9660Parser &parser) {
198 setupInitQueue(archiveName, parser, {});
199 return m_queueInitFilename.schedule();
200 }
201 InitAwaiterWithFilename initialize(eastl::string_view archiveName, ISO9660Parser &parser) {
202 return {archiveName, parser, *this};
203 }
204 void initialize(uint32_t LBA, CDRom &device, eastl::function<void(bool)> &&callback) {
205 setupInitQueue(LBA, device, eastl::move(callback));
206 m_queue.run();
207 }
209 setupInitQueue(LBA, device, {});
210 return m_queue.schedule();
211 }
212 InitAwaiter initialize(uint32_t LBA, CDRom &device) { return {LBA, device, *this}; }
213
226 if (m_index.size() == 0) {
227 return nullptr;
228 }
229 return &m_index[1];
230 }
231
241 uint32_t getIndexCount() const { return m_index[0].asArray[2] & 0xffffff; }
242
252 uint32_t getSafeMarginLength() const { return ((m_index[0].asArray[2] >> 24) + 1) * 16; }
253
267 const IndexEntry *getIndexEntry(eastl::string_view path) const;
268 template <unsigned S>
269 const IndexEntry *getIndexEntry(const char (&path)[S]) const {
270 return getIndexEntry(djb::hash<uint64_t>(path));
271 }
272 const IndexEntry *getIndexEntry(uint64_t hash) const;
273
280 uint32_t getIndexEntrySectorStart(const IndexEntry *entry) const {
281 return m_archiveDirentry.LBA + entry->getSectorOffset();
282 }
283
295 void setBuffer(Buffer<uint8_t> &&buffer) { m_data = eastl::move(buffer); }
296
307 template <unsigned S>
308 void readFile(const char (&path)[S], CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
309 setupQueue(getIndexEntry(path), device, eastl::move(callback));
310 m_queue.run();
311 }
312 void readFile(eastl::string_view path, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
313 setupQueue(getIndexEntry(path), device, eastl::move(callback));
314 m_queue.run();
315 }
316 void readFile(uint64_t hash, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
317 setupQueue(getIndexEntry(hash), device, eastl::move(callback));
318 m_queue.run();
319 }
320 void readFile(const IndexEntry *entry, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
321 setupQueue(entry, device, eastl::move(callback));
322 m_queue.run();
323 }
324 template <unsigned S>
325 psyqo::TaskQueue::Task scheduleReadFile(const char (&path)[S], CDRom &device) {
326 setupQueue(getIndexEntry(path), device, {});
327 return m_queue.schedule();
328 }
329 psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, CDRom &device) {
330 setupQueue(getIndexEntry(path), device, {});
331 return m_queue.schedule();
332 }
334 setupQueue(getIndexEntry(hash), device, {});
335 return m_queue.schedule();
336 }
338 setupQueue(entry, device, {});
339 return m_queue.schedule();
340 }
341 template <unsigned S>
342 ReadFileAwaiter readFile(const char (&path)[S], CDRom &device) {
343 return {getIndexEntry(path), device, *this};
344 }
345 ReadFileAwaiter readFile(eastl::string_view path, CDRom &device) { return {getIndexEntry(path), device, *this}; }
346 ReadFileAwaiter readFile(uint64_t hash, CDRom &device) { return {getIndexEntry(hash), device, *this}; }
347 ReadFileAwaiter readFile(const IndexEntry *entry, CDRom &device) { return {entry, device, *this}; }
348
364 s_decompressors[toUnderlying(IndexEntry::Method::UCL_NRV2E)] = &ArchiveManager::decompressUCL_NRV2E;
365 }
367 s_decompressors[toUnderlying(IndexEntry::Method::LZ4)] = &ArchiveManager::decompressLZ4;
368 }
369
382
383 private:
384 eastl::function<void(bool)> m_initCallback;
385 eastl::function<void(Buffer<uint8_t> &&)> m_callback;
386 psyqo::TaskQueue m_queueInitFilename;
387 psyqo::TaskQueue m_queue;
388 Buffer<uint8_t> m_data;
389 Buffer<IndexEntry> m_index;
390 ISO9660Parser::DirEntry m_archiveDirentry;
391 CDRom::ReadRequest m_request;
392 bool m_pending = false;
393 bool m_success = false;
394
395 void setupInitQueue(eastl::string_view archiveName, ISO9660Parser &parser, eastl::function<void(bool)> &&callback);
396 void setupInitQueue(uint32_t LBA, CDRom &device, eastl::function<void(bool)> &&callback);
397 void setupQueue(const IndexEntry *entry, CDRom &device, eastl::function<void(Buffer<uint8_t> &&)> &&callback);
398 uint32_t getIndexSectorCount() const {
399 static_assert(sizeof(IndexEntry) == 16, "IndexEntry size is not 16 bytes");
400 uint32_t indexSize = (getIndexCount() + 1) * sizeof(IndexEntry);
401 return (indexSize + 2047) / 2048;
402 }
403 static eastl::array<void (ArchiveManager::*)(const IndexEntry *), toUnderlying(IndexEntry::Method::COUNT)>
404 s_decompressors;
405 void decompressUCL_NRV2E(const IndexEntry *entry);
406 void decompressLZ4(const IndexEntry *entry);
407};
408
409} // 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:333
void initialize(eastl::string_view archiveName, ISO9660Parser &parser, eastl::function< void(bool)> &&callback)
Asynchronous initialization of the archive manager.
Definition archive-manager.hh:193
InitAwaiterWithFilename initialize(eastl::string_view archiveName, ISO9660Parser &parser)
Definition archive-manager.hh:201
void readFile(const IndexEntry *entry, CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Definition archive-manager.hh:320
uint32_t getIndexCount() const
Get the number of entries in the index.
Definition archive-manager.hh:241
void setBuffer(Buffer< uint8_t > &&buffer)
Set the Buffer object for the next read operation.
Definition archive-manager.hh:295
static void registerLZ4Decompressor()
Definition archive-manager.hh:366
psyqo::TaskQueue::Task scheduleInitialize(eastl::string_view archiveName, ISO9660Parser &parser)
Definition archive-manager.hh:197
void readFile(eastl::string_view path, CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Definition archive-manager.hh:312
void readFile(uint64_t hash, CDRom &device, eastl::function< void(Buffer< uint8_t > &&)> &&callback)
Definition archive-manager.hh:316
const IndexEntry * getFirstIndexEntry() const
Get the First IndexEntry object.
Definition archive-manager.hh:225
static void registerAllDecompressors()
Register all decompressors.
Definition archive-manager.hh:378
static void registerUCL_NRV2EDecompressor()
Register a decompressor for a specific compression method.
Definition archive-manager.hh:363
ReadFileAwaiter readFile(const IndexEntry *entry, CDRom &device)
Definition archive-manager.hh:347
psyqo::TaskQueue::Task scheduleInitialize(uint32_t LBA, CDRom &device)
Definition archive-manager.hh:208
psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, CDRom &device)
Definition archive-manager.hh:329
uint32_t getIndexEntrySectorStart(const IndexEntry *entry) const
Get the LBA of the first sector of the file in the archive.
Definition archive-manager.hh:280
InitAwaiter initialize(uint32_t LBA, CDRom &device)
Definition archive-manager.hh:212
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:308
const IndexEntry * getIndexEntry(const char(&path)[S]) const
Definition archive-manager.hh:269
ReadFileAwaiter readFile(const char(&path)[S], CDRom &device)
Definition archive-manager.hh:342
void initialize(uint32_t LBA, CDRom &device, eastl::function< void(bool)> &&callback)
Definition archive-manager.hh:204
psyqo::TaskQueue::Task scheduleReadFile(const char(&path)[S], CDRom &device)
Definition archive-manager.hh:325
ReadFileAwaiter readFile(uint64_t hash, CDRom &device)
Definition archive-manager.hh:346
psyqo::TaskQueue::Task scheduleReadFile(const IndexEntry *entry, CDRom &device)
Definition archive-manager.hh:337
ReadFileAwaiter readFile(eastl::string_view path, CDRom &device)
Definition archive-manager.hh:345
const IndexEntry * getIndexEntry(eastl::string_view path) const
Get the IndexEntry object for a given path.
Definition archive-manager.cpp:92
uint32_t getSafeMarginLength() const
Get the safe margin length for decompression.
Definition archive-manager.hh:252
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:274
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:151
uint32_t getStreamSectors() const
Definition archive-manager.hh:173
Utilities::BitSpan< Method, 3 > MethodField
Definition archive-manager.hh:153
uint32_t getDecompSize() const
Definition archive-manager.hh:157
Method
Definition archive-manager.hh:138
CompressedEntry entry
Definition archive-manager.hh:177
uint32_t getCompressedSize() const
Definition archive-manager.hh:169
uint32_t getSectorOffset() const
Definition archive-manager.hh:165
Utilities::BitSpan< uint32_t, 10 > CompressedSizeField
Definition archive-manager.hh:152
Utilities::BitField< DecompSizeField, PaddingField, SectorOffsetField, CompressedSizeField, MethodField > CompressedEntry
Definition archive-manager.hh:155
uint64_t hash
Definition archive-manager.hh:176
Utilities::BitSpan< uint32_t, 21 > DecompSizeField
Definition archive-manager.hh:149
uint32_t getPadding() const
Definition archive-manager.hh:162
Method getCompressionMethod() const
Definition archive-manager.hh:171
Utilities::BitSpan< uint32_t, 11 > PaddingField
Definition archive-manager.hh:150
uint32_t asArray[4]
Definition archive-manager.hh:174