Skip to content

Commit 82587ee

Browse files
authored
Merge pull request #1904 from grumpycoders/archive
Adding psyqo's archive system.
2 parents 7f8b62f + 806ed43 commit 82587ee

File tree

7 files changed

+978
-27
lines changed

7 files changed

+978
-27
lines changed

src/mips/psyqo-paths/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ TYPE = library
55

66
SRCS = \
77
src/cdrom-loader.cpp \
8+
src/archive-manager.cpp \
9+
../ucl-demo/n2e-d.S \
810

911
EXTRA_DEPS += $(PSYQOPATHSDIR)Makefile
1012

src/mips/psyqo-paths/archive-manager.hh

Lines changed: 390 additions & 0 deletions
Large diffs are not rendered by default.

src/mips/psyqo-paths/cdrom-loader.hh

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ SOFTWARE.
2727
#pragma once
2828

2929
#include <EASTL/string_view.h>
30-
#include <EASTL/vector.h>
3130
#include <stdint.h>
3231

3332
#include <coroutine>
3433

35-
#include "psyqo/gpu.hh"
34+
#include "psyqo/buffer.hh"
3635
#include "psyqo/iso9660-parser.hh"
3736
#include "psyqo/task.hh"
3837

@@ -44,7 +43,7 @@ namespace psyqo::paths {
4443
* @details This class provides a PSYQo path to read files from the CDRom.
4544
* The way to use it is to instantiate the class somewhere persistent, and
4645
* then call readFile() with a callback. The callback will be called with
47-
* the data of the file, or an empty vector if the file could not be read.
46+
* the data of the file, or an empty buffer if the file could not be read.
4847
* This is going to allocate memory in different places. Only one file can
4948
* be read at a time, but it is safe to call readFile() again from the
5049
* callback. If preferred, the loader can be cascaded into another `TaskQueue`.
@@ -54,59 +53,70 @@ namespace psyqo::paths {
5453

5554
class CDRomLoader {
5655
struct ReadFileAwaiter {
57-
ReadFileAwaiter(eastl::string_view path, GPU &gpu, ISO9660Parser &parser, CDRomLoader &loader)
58-
: m_path(path), m_gpu(gpu), m_parser(parser), m_loader(loader) {}
56+
ReadFileAwaiter(eastl::string_view path, ISO9660Parser &parser, CDRomLoader &loader)
57+
: m_path(path), m_parser(parser), m_loader(loader) {}
5958
~ReadFileAwaiter() {}
6059
constexpr bool await_ready() const { return false; }
6160
template <typename U>
6261
void await_suspend(std::coroutine_handle<U> handle) {
63-
m_loader.readFile(m_path, m_gpu, m_parser, [handle, this](eastl::vector<uint8_t> &&data) {
62+
m_loader.readFile(m_path, m_parser, [handle, this](Buffer<uint8_t> &&data) {
6463
m_data = eastl::move(data);
6564
handle.resume();
6665
});
6766
}
68-
eastl::vector<uint8_t> await_resume() { return eastl::move(m_data); }
67+
Buffer<uint8_t> await_resume() { return eastl::move(m_data); }
6968

7069
private:
7170
eastl::string_view m_path;
72-
GPU &m_gpu;
7371
ISO9660Parser &m_parser;
7472
CDRomLoader &m_loader;
75-
eastl::vector<uint8_t> m_data;
73+
Buffer<uint8_t> m_data;
7674
};
7775

7876
public:
77+
/**
78+
* @brief Set the Buffer object for the next read operation.
79+
*
80+
* @details This function sets the buffer to be used for the next read
81+
* operation. By default, the archive manager will allocate a buffer of the
82+
* appropriate size for the file being read. However, if the user wants to
83+
* use an already allocated buffer, they can use this function to set the buffer
84+
* to be used.
85+
*
86+
* @param buffer The buffer to be used for the next read operation.
87+
*/
88+
void setBuffer(Buffer<uint8_t> &&buffer) { m_data = eastl::move(buffer); }
89+
7990
/**
8091
* @brief Reads a file from the CDRom.
8192
*
8293
* @param path The path to the file to read. The view must be persistent
8394
* until the callback is called.
84-
* @param gpu The GPU class used by the application, in order to set timers.
8595
* @param parser The ISO9660Parser to use for reading the file.
8696
* @param callback The callback to call when the file is read. The callback
87-
* will be called with the data of the file, or an empty vector if the file
97+
* will be called with the data of the file, or an empty buffer if the file
8898
* could not be read.
8999
*/
90-
void readFile(eastl::string_view path, GPU &gpu, ISO9660Parser &parser,
91-
eastl::function<void(eastl::vector<uint8_t> &&)> &&callback) {
92-
setupQueue(path, gpu, parser, eastl::move(callback));
100+
void readFile(eastl::string_view path, ISO9660Parser &parser,
101+
eastl::function<void(Buffer<uint8_t> &&)> &&callback) {
102+
setupQueue(path, parser, eastl::move(callback));
93103
m_queue.run();
94104
}
95-
psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, GPU &gpu, ISO9660Parser &parser) {
96-
setupQueue(path, gpu, parser, {});
105+
psyqo::TaskQueue::Task scheduleReadFile(eastl::string_view path, ISO9660Parser &parser) {
106+
setupQueue(path, parser, {});
97107
return m_queue.schedule();
98108
}
99-
ReadFileAwaiter readFile(eastl::string_view path, GPU &gpu, ISO9660Parser &parser) {
100-
return {path, gpu, parser, *this};
109+
ReadFileAwaiter readFile(eastl::string_view path, ISO9660Parser &parser) {
110+
return {path, parser, *this};
101111
}
102112

103113
private:
104-
void setupQueue(eastl::string_view path, GPU &gpu, ISO9660Parser &parser,
105-
eastl::function<void(eastl::vector<uint8_t> &&)> &&callback);
106-
eastl::function<void(eastl::vector<uint8_t> &&)> m_callback;
114+
void setupQueue(eastl::string_view path, ISO9660Parser &parser,
115+
eastl::function<void(Buffer<uint8_t> &&)> &&callback);
116+
eastl::function<void(Buffer<uint8_t> &&)> m_callback;
107117
psyqo::TaskQueue m_queue;
108118
ISO9660Parser::ReadRequest m_request;
109-
eastl::vector<uint8_t> m_data;
119+
Buffer<uint8_t> m_data;
110120
bool m_pending = false;
111121
};
112122

src/mips/psyqo-paths/examples/cdrom-loader/cdrom-loader.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class CDRomLoaderExample final : public psyqo::Application {
4444
psyqo::CDRomDevice m_cdrom;
4545
psyqo::ISO9660Parser m_isoParser = psyqo::ISO9660Parser(&m_cdrom);
4646
psyqo::paths::CDRomLoader m_cdromLoader;
47-
eastl::vector<uint8_t> m_buffer;
47+
psyqo::Buffer<uint8_t> m_buffer;
4848
bool m_callbackCalled = false;
4949
};
5050

@@ -70,8 +70,8 @@ void CDRomLoaderExample::prepare() {
7070
void CDRomLoaderExample::createScene() {
7171
m_font.uploadSystemFont(gpu());
7272
pushScene(&cdromLoaderExampleScene);
73-
m_cdromLoader.readFile("SYSTEM.CNF;1", cdromLoaderExample.gpu(), cdromLoaderExample.m_isoParser,
74-
[this](eastl::vector<uint8_t>&& buffer) {
73+
m_cdromLoader.readFile("SYSTEM.CNF;1", cdromLoaderExample.m_isoParser,
74+
[this](psyqo::Buffer<uint8_t>&& buffer) {
7575
m_buffer = eastl::move(buffer);
7676
m_callbackCalled = true;
7777
});
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2025 PCSX-Redux authors
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
27+
#include "psyqo-paths/archive-manager.hh"
28+
29+
#include "EASTL/algorithm.h"
30+
#include "lz4/lz4.h"
31+
#include "psyqo/kernel.hh"
32+
#include "psyqo/utility-polyfill.h"
33+
#include "ucl-demo/n2e-d.h"
34+
35+
eastl::array<void (psyqo::paths::ArchiveManager::*)(const psyqo::paths::ArchiveManager::IndexEntry*), 3>
36+
psyqo::paths::ArchiveManager::s_decompressors = {nullptr};
37+
38+
void psyqo::paths::ArchiveManager::setupInitQueue(eastl::string_view archiveName, ISO9660Parser& parser,
39+
eastl::function<void(bool)>&& callback) {
40+
setupInitQueue(0, *parser.getCDRom(), eastl::move(callback));
41+
m_queueInitFilename.reset();
42+
m_queueInitFilename.startWith([](auto task) { task->resolve(); });
43+
if (!parser.initialized()) {
44+
m_queueInitFilename.then(parser.scheduleInitialize());
45+
}
46+
m_queueInitFilename.then(parser.scheduleGetDirentry(archiveName, &m_archiveDirentry))
47+
.then([this](auto task) {
48+
m_index.resize(2048 / sizeof(IndexEntry));
49+
m_request.LBA = m_archiveDirentry.LBA;
50+
m_request.count = 1;
51+
m_request.buffer = m_index.data();
52+
task->resolve();
53+
})
54+
.then(m_queue.schedule());
55+
}
56+
57+
void psyqo::paths::ArchiveManager::setupInitQueue(uint32_t LBA, CDRom& device, eastl::function<void(bool)>&& callback) {
58+
Kernel::assert(!m_pending, "Only one action can be performed at a time");
59+
m_queue.reset();
60+
m_pending = true;
61+
m_success = false;
62+
m_initCallback = eastl::move(callback);
63+
m_request.LBA = LBA;
64+
m_request.count = 1;
65+
m_request.buffer = m_index.data();
66+
m_queue.startWith(device.scheduleReadRequest(&m_request))
67+
.then([this](auto task) {
68+
eastl::string_view signature = {reinterpret_cast<const char*>(m_index.data()), 8};
69+
if (signature != "PSX-ARC1") {
70+
task->reject();
71+
return;
72+
}
73+
m_index.resize(getIndexSectorCount() * 2048 / sizeof(IndexEntry));
74+
m_request.count = getIndexSectorCount();
75+
m_request.buffer = m_index.data();
76+
task->resolve();
77+
})
78+
.then(device.scheduleReadRequest(&m_request))
79+
.then([this](auto task) {
80+
m_success = true;
81+
m_index.resize(getIndexCount() + 1);
82+
task->resolve();
83+
})
84+
.finally([this](auto queue) {
85+
m_pending = false;
86+
auto callback = eastl::move(m_initCallback);
87+
m_initCallback = nullptr;
88+
callback(m_success);
89+
});
90+
}
91+
92+
const psyqo::paths::ArchiveManager::IndexEntry* psyqo::paths::ArchiveManager::getIndexEntry(
93+
eastl::string_view path) const {
94+
uint64_t hash = djb::hash<uint64_t>(path.data(), path.size());
95+
return getIndexEntry(hash);
96+
}
97+
98+
const psyqo::paths::ArchiveManager::IndexEntry* psyqo::paths::ArchiveManager::getIndexEntry(uint64_t hash) const {
99+
const IndexEntry* first = &m_index[1];
100+
const IndexEntry* last = first + getIndexCount();
101+
const IndexEntry* entry =
102+
eastl::lower_bound(first, last, hash, [](const IndexEntry& e, uint64_t hash) { return e.hash < hash; });
103+
if (entry != last && entry->hash == hash) {
104+
return entry;
105+
}
106+
return nullptr;
107+
}
108+
109+
void psyqo::paths::ArchiveManager::setupQueue(const IndexEntry* entry, CDRom& device,
110+
eastl::function<void(Buffer<uint8_t>&&)>&& callback) {
111+
Kernel::assert(!m_pending, "Only one action can be performed at a time");
112+
m_queue.reset();
113+
m_pending = true;
114+
m_callback = eastl::move(callback);
115+
if (!entry) {
116+
m_queue.startWith([this](auto task) {
117+
m_data.clear();
118+
m_pending = false;
119+
auto callback = eastl::move(m_callback);
120+
m_callback = nullptr;
121+
callback(eastl::move(m_data));
122+
});
123+
return;
124+
}
125+
const auto method = entry->getCompressionMethod();
126+
const uint32_t sectorCount = entry->getCompressedSize();
127+
const uint32_t decompSize = entry->getDecompSize();
128+
m_request.LBA = m_archiveDirentry.LBA + entry->getSectorOffset();
129+
m_request.count = sectorCount;
130+
if (method == IndexEntry::Method::NONE) {
131+
m_data.resize(sectorCount * 2048);
132+
m_request.buffer = m_data.data();
133+
} else {
134+
uint32_t actualSize = eastl::max<uint32_t>(((decompSize + 3) & ~3) + 16, sectorCount * 2048);
135+
m_data.resize(actualSize);
136+
m_request.buffer = m_data.data() + actualSize - sectorCount * 2048;
137+
}
138+
m_queue.startWith(device.scheduleReadRequest(&m_request))
139+
.then([this, entry](auto task) {
140+
auto decompress = s_decompressors[toUnderlying(entry->getCompressionMethod())];
141+
if (decompress) (this->*decompress)(entry);
142+
uint32_t decompSize = entry->getDecompSize();
143+
if (decompSize) m_data.resize(decompSize);
144+
task->resolve();
145+
})
146+
.butCatch([this](auto task) { m_data.resize(0); })
147+
.finally([this, decompSize](auto queue) {
148+
m_pending = false;
149+
auto callback = eastl::move(m_callback);
150+
m_callback = nullptr;
151+
callback(eastl::move(m_data));
152+
});
153+
}
154+
155+
void psyqo::paths::ArchiveManager::decompressUCL_NRV2E(const IndexEntry* entry) {
156+
uint32_t padding = entry->getPadding();
157+
n2e_decompress(reinterpret_cast<uint8_t*>(m_request.buffer) + padding, m_data.data());
158+
}
159+
160+
void psyqo::paths::ArchiveManager::decompressLZ4(const IndexEntry* entry) {
161+
uint32_t padding = entry->getPadding();
162+
uint32_t srcSize = entry->getCompressedSize() * 2048 - padding;
163+
uint8_t* src = reinterpret_cast<uint8_t*>(m_request.buffer) + padding;
164+
lz4_decompress_block(src, src + srcSize, m_data.data());
165+
}

src/mips/psyqo-paths/src/cdrom-loader.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ SOFTWARE.
2828

2929
#include "psyqo/kernel.hh"
3030

31-
void psyqo::paths::CDRomLoader::setupQueue(eastl::string_view path, GPU& gpu, psyqo::ISO9660Parser& parser,
32-
eastl::function<void(eastl::vector<uint8_t>&&)>&& callback) {
31+
void psyqo::paths::CDRomLoader::setupQueue(eastl::string_view path, psyqo::ISO9660Parser& parser,
32+
eastl::function<void(Buffer<uint8_t>&&)>&& callback) {
3333
Kernel::assert(!m_pending, "Only one file can be read at a time");
3434
m_queue.reset();
3535
m_pending = true;

0 commit comments

Comments
 (0)