libeditwl
Lightweight C++ library for Nintendo DS(i) formats
Loading...
Searching...
No Matches
fs_File.hpp
1
2#pragma once
3#include <twl/twl_Include.hpp>
4#include <twl/util/util_Compression.hpp>
5#include <twl/util/util_Align.hpp>
6#include <cstdio>
7#include <cstring>
8
9namespace twl::fs {
10
11 enum class FileMode : u8 {
12 Invalid,
13 Read,
14 Write,
15 Update
16 };
17
18 inline constexpr bool CanReadWithMode(const FileMode mode) {
19 return mode == FileMode::Read;
20 }
21
22 inline constexpr bool CanWriteWithMode(const FileMode mode) {
23 return (mode == FileMode::Write) || (mode == FileMode::Update);
24 }
25
26 enum class Whence : u8 {
27 Begin,
28 Current
29 };
30
31 enum class FileCompression : u8 {
32 Invalid,
33 None,
34 LZ77
35 };
36
38 public:
39 virtual Result ReadBuffer(void *read_buf, const size_t read_size) = 0;
40 virtual Result WriteBuffer(const void *write_buf, const size_t write_size) = 0;
41 virtual Result SetOffset(const ssize_t offset, const Whence whence) = 0;
42 virtual Result GetOffset(size_t &out_offset) = 0;
43 virtual Result GetSize(size_t &out_size) = 0;
44
45 inline Result SetAbsoluteOffset(const size_t offset) {
46 TWL_R_TRY(this->SetOffset(offset, Whence::Begin));
47 TWL_R_SUCCEED();
48 }
49
50 inline Result MoveOffset(const ssize_t offset) {
51 TWL_R_TRY(this->SetOffset(offset, Whence::Current));
52 TWL_R_SUCCEED();
53 }
54
55 template<typename T>
56 inline Result Read(T &out_t) {
57 TWL_R_TRY(this->ReadBuffer(std::addressof(out_t), sizeof(T)));
58 TWL_R_SUCCEED();
59 }
60
61 template<typename T>
62 inline Result Write(const T &t) {
63 TWL_R_TRY(this->WriteBuffer(std::addressof(t), sizeof(T)));
64 TWL_R_SUCCEED();
65 }
66
67 template<typename T>
68 inline Result ReadLEB128(T &out_t) {
69 out_t = {};
70 while(true) {
71 u8 v;
72 TWL_R_TRY(this->Read(v));
73
74 out_t = (out_t << 7) | (v & 0x7F);
75 if(!(v & 0x80)) {
76 break;
77 }
78 }
79
80 TWL_R_SUCCEED();
81 }
82
83 template<typename C>
84 inline Result ReadNullTerminatedString(std::basic_string<C> &out_str, const size_t tmp_buf_size = 0x200) {
85 // Note: this approach is significantly faster than the classic read-char-by-char approach, specially when reading hundreds of strings ;)
86 out_str.clear();
87
88 size_t old_offset;
89 TWL_R_TRY(this->GetOffset(old_offset));
90 size_t f_size;
91 TWL_R_TRY(this->GetSize(f_size));
92 const auto available_size = f_size - old_offset;
93 if(available_size == 0) {
94 TWL_R_FAIL(ResultEndOfData);
95 }
96 const auto r_size = std::min(tmp_buf_size, available_size);
97 auto buf = new C[r_size]();
98 ScopeGuard on_exit_cleanup([&]() {
99 delete[] buf;
100 });
101
102 size_t read_size;
103 TWL_R_TRY(this->ReadBuffer(buf, r_size));
104
105 for(size_t i = 0; i < r_size; i++) {
106 if(buf[i] == static_cast<C>(0)) {
107 TWL_R_TRY(this->SetAbsoluteOffset(old_offset + i + 1));
108 out_str = std::basic_string<C>(buf, i);
109 TWL_R_SUCCEED();
110 }
111 }
112
113 TWL_R_TRY(this->SetAbsoluteOffset(old_offset));
114 return ReadNullTerminatedString(out_str, tmp_buf_size * 2);
115 }
116
117 template<typename C>
118 inline Result WriteString(const std::basic_string<C> &str) {
119 return this->WriteBuffer(str.c_str(), str.length() * sizeof(C));
120 }
121
122 inline Result WriteCString(const char *str) {
123 const auto str_len = std::strlen(str);
124 return this->WriteBuffer(str, str_len);
125 }
126
127 inline Result WriteEnsureAlignmentPadding(const size_t align, size_t &out_pad_size) {
128 if(align == 0) {
129 TWL_R_SUCCEED();
130 }
131
132 size_t cur_offset;
133 TWL_R_TRY(this->GetOffset(cur_offset));
134 out_pad_size = util::AlignUp(cur_offset, align) - cur_offset;
135
136 auto zero_buf = new u8[out_pad_size]();
137 ScopeGuard cleanup([&]() {
138 delete[] zero_buf;
139 });
140
141 TWL_R_TRY(this->WriteBuffer(zero_buf, out_pad_size));
142
143 TWL_R_SUCCEED();
144 }
145
146 inline Result WriteEnsureAlignment(const size_t align) {
147 size_t dummy_size;
148 TWL_R_TRY(this->WriteEnsureAlignmentPadding(align, dummy_size));
149 TWL_R_SUCCEED();
150 }
151
152 template<typename T>
153 inline Result WriteVector(const std::vector<T> &vec) {
154 return this->WriteBuffer(vec.data(), vec.size() * sizeof(T));
155 }
156
157 template<typename C>
158 inline Result WriteNullTerminatedString(const std::basic_string<C> &str) {
159 TWL_R_TRY(this->WriteString(str));
160 TWL_R_TRY(this->Write(static_cast<C>(0)));
161
162 TWL_R_SUCCEED();
163 }
164 };
165
166 class BufferReaderWriter : public AbstractReaderWriter {
167 private:
168 void *buf;
169 size_t buf_size;
170 size_t offset;
171
172 public:
173 constexpr BufferReaderWriter() : buf(nullptr), buf_size(0), offset(0) {}
174
175 inline BufferReaderWriter(const size_t buf_size) : buf(nullptr), buf_size(0), offset(0) {
176 this->CreateAllocate(buf_size);
177 }
178
179 inline BufferReaderWriter(void *buf, const size_t buf_size, const bool transfer_ownership = true) : buf(nullptr), buf_size(0), offset(0) {
180 this->CreateFrom(buf, buf_size, transfer_ownership);
181 }
182
183 BufferReaderWriter(const BufferReaderWriter&) = delete;
184 BufferReaderWriter(BufferReaderWriter&&) = default;
185
186 void CreateAllocate(const size_t buf_size);
187 void CreateFrom(void *buf, const size_t buf_size, const bool transfer_ownership = true);
188
189 inline void Dispose() {
190 if(this->buf != nullptr) {
191 auto del_buf = reinterpret_cast<u8*>(this->buf);
192 delete[] del_buf;
193 this->buf = nullptr;
194 }
195
196 this->buf_size = 0;
197 this->offset = 0;
198 }
199
200 inline bool IsValid() {
201 return (this->buf != nullptr) && (this->buf_size > 0);
202 }
203
204 inline void *GetBuffer() {
205 return this->buf;
206 }
207
208 inline size_t GetBufferSize() {
209 return this->buf_size;
210 }
211
212 Result SetOffset(const ssize_t offset, const Whence whence) override;
213
214 inline size_t GetBufferOffset() {
215 return this->offset;
216 }
217
218 inline Result GetOffset(size_t &out_offset) override {
219 out_offset = this->GetBufferOffset();
220 TWL_R_SUCCEED();
221 }
222
223 inline Result GetSize(size_t &out_size) override {
224 out_size = this->GetBufferSize();
225 TWL_R_SUCCEED();
226 }
227
228 Result ReadBuffer(void *read_buf, const size_t read_size) override;
229 Result WriteBuffer(const void *write_buf, const size_t write_size) override;
230 };
231
232 class File : public AbstractReaderWriter {
233 protected:
234 FileMode mode;
235
236 private:
237 bool opened;
238 FileCompression comp;
239 util::LzVersion lz_ver;
240 BufferReaderWriter decomp_rw;
241
242 Result DecompressRead();
243 Result CompressWrite();
244
245 Result Open(const FileMode mode, const FileCompression w_comp = FileCompression::Invalid);
246
247 public:
248 constexpr File() : mode(FileMode::Invalid), opened(false), comp(FileCompression::Invalid), lz_ver(util::LzVersion::Invalid), decomp_rw() {}
249
250 File(const File&) = delete;
251 File(File&&) = default;
252
253 inline bool IsOpened() {
254 return this->opened;
255 }
256
257 inline constexpr bool IsCompressed() {
258 return (this->comp != FileCompression::Invalid) && (this->comp != FileCompression::None);
259 }
260
261 virtual Result OpenImpl(const FileMode mode) = 0;
262 virtual Result GetSizeImpl(size_t &out_size) = 0;
263 virtual Result SetOffsetImpl(const size_t offset, const Whence whence) = 0;
264 virtual Result GetOffsetImpl(size_t &out_offset) = 0;
265 virtual Result ReadBufferImpl(void *read_buf, const size_t read_size) = 0;
266 virtual Result WriteBufferImpl(const void *write_buf, const size_t write_size) = 0;
267 virtual Result CloseImpl() = 0;
268
269 inline Result OpenRead() {
270 TWL_R_TRY(this->Open(fs::FileMode::Read));
271 TWL_R_SUCCEED();
272 }
273
274 inline Result OpenWrite(const FileCompression comp = FileCompression::None) {
275 TWL_R_TRY(this->Open(fs::FileMode::Write, comp));
276 TWL_R_SUCCEED();
277 }
278
279 inline Result OpenUpdate() {
280 TWL_R_TRY(this->Open(fs::FileMode::Update));
281 TWL_R_SUCCEED();
282 }
283
284 inline Result GetSize(size_t &out_size) override {
285 if(this->IsCompressed()) {
286 out_size = this->decomp_rw.GetBufferSize();
287 }
288 else {
289 TWL_R_TRY(this->GetSizeImpl(out_size));
290 }
291
292 TWL_R_SUCCEED();
293 }
294
295 Result SetOffset(const ssize_t offset, const Whence whence) override;
296 Result GetOffset(size_t &out_offset) override;
297 Result ReadBuffer(void *read_buf, const size_t read_size) override;
298 Result WriteBuffer(const void *write_buf, const size_t write_size) override;
299
300 Result Close();
301 };
302
303 class StdioFile : public File {
304 private:
305 std::string path;
306 FILE *file;
307
308 public:
309 inline StdioFile(const std::string &path) : File(), path(path), file(nullptr) {}
310
311 StdioFile(const StdioFile&) = delete;
312 StdioFile(StdioFile&&) = default;
313
314 Result OpenImpl(const FileMode mode) override;
315 Result GetSizeImpl(size_t &out_size) override;
316 Result SetOffsetImpl(const size_t offset, const Whence whence) override;
317 Result GetOffsetImpl(size_t &out_offset) override;
318 Result ReadBufferImpl(void *read_buf, const size_t read_size) override;
319 Result WriteBufferImpl(const void *write_buf, const size_t write_size) override;
320 Result CloseImpl() override;
321
322 inline std::string &GetPath() {
323 return this->path;
324 }
325 };
326
327 class BufferFile : public File {
328 private:
330
331 public:
332 constexpr BufferFile() : File(), rw() {}
333 inline BufferFile(size_t buf_size) : File(), rw(buf_size) {}
334 inline BufferFile(void *buf, size_t buf_size, const bool transfer_ownership = true) : File(), rw(buf, buf_size, transfer_ownership) {}
335
336 BufferFile(const BufferFile&) = delete;
337 BufferFile(BufferFile&&) = default;
338
339 inline void CreateAllocate(size_t buf_size) {
340 this->Close();
341 this->rw.CreateAllocate(buf_size);
342 }
343
344 inline void CreateFrom(void *buf, size_t buf_size, const bool transfer_ownership = true) {
345 this->Close();
346 this->rw.CreateFrom(buf, buf_size, transfer_ownership);
347 }
348
349 inline void Dispose() {
350 this->Close();
351 this->rw.Dispose();
352 }
353
354 inline bool IsValid() {
355 return this->rw.IsValid();
356 }
357
358 inline void *GetBuffer() {
359 return this->rw.GetBuffer();
360 }
361
362 inline size_t GetBufferSize() {
363 return this->rw.GetBufferSize();
364 }
365
366 Result OpenImpl(const FileMode mode) override;
367
368 inline Result GetSizeImpl(size_t &out_size) override {
369 out_size = this->rw.GetBufferSize();
370 TWL_R_SUCCEED();
371 }
372
373 Result SetOffsetImpl(const size_t offset, const Whence whence) override;
374
375 inline Result GetOffsetImpl(size_t &out_offset) override {
376 out_offset = this->rw.GetBufferOffset();
377 TWL_R_SUCCEED();
378 }
379
380 Result ReadBufferImpl(void *read_buf, const size_t read_size) override;
381 Result WriteBufferImpl(const void *write_buf, const size_t write_size) override;
382 Result CloseImpl() override;
383 };
384
385}
Definition twl_Include.hpp:22
Definition fs_File.hpp:37
Definition fs_File.hpp:166
Definition twl_Include.hpp:62