1#include "filedevice/seadArchiveFileDevice.h"
2#include "basis/seadRawPrint.h"
3#include "math/seadMathCalcCommon.h"
4#include "prim/seadPtrUtil.h"
5#include "resource/seadArchiveRes.h"
6
7namespace sead
8{
9struct ArchiveFileDevice::ArchiveFileHandle
10{
11 const u8* mFileData;
12 ArchiveRes::FileInfo mFileInfo;
13 u32 mPos;
14};
15
16ArchiveFileDevice::ArchiveFileDevice(ArchiveRes* res) : FileDevice("arc"), mArchive(res) {}
17
18u8* ArchiveFileDevice::tryLoadWithEntryID(s32 id, FileDevice::LoadArg& arg)
19{
20 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
21 if (!mPermission)
22 return nullptr;
23
24 return doLoadWithEntryID_(id, arg);
25}
26
27FileDevice* ArchiveFileDevice::tryOpenWithEntryID(FileHandle* handle, s32 id,
28 FileDevice::FileOpenFlag flag, u32 div_size)
29{
30 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
31 if (!mPermission)
32 return nullptr;
33
34 setFileHandleDivSize_(handle, divSize: div_size);
35 FileDevice* ret = doOpenWithEntryID_(handle, id, flag);
36 setHandleBaseFileDevice_(handle, device: ret);
37 return ret;
38}
39
40s32 ArchiveFileDevice::tryConvertPathToEntryID(const SafeString& path)
41{
42 return doConvertPathToEntryID_(path);
43}
44
45bool ArchiveFileDevice::setCurrentDirectory(const SafeString& dir)
46{
47 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
48 if (!mPermission)
49 return false;
50
51 return doSetCurrentDirectory_(path: dir);
52}
53
54bool ArchiveFileDevice::doGetFileSize_(u32* fileSize, const SafeString& path)
55{
56 if (!mArchive)
57 {
58 SEAD_ASSERT_MSG(false, "no archive mounted");
59 return false;
60 }
61
62 if (!path.cstr())
63 {
64 SEAD_ASSERT_MSG(false, "invalid path");
65 return false;
66 }
67
68 ArchiveRes::FileInfo info{};
69 if (!mArchive->getFile(file_path: path, info: &info))
70 return false;
71
72 *fileSize = info.mLength;
73 return true;
74}
75
76bool ArchiveFileDevice::doGetFileSize_(u32* fileSize, FileHandle* handle)
77{
78 if (!handle)
79 {
80 SEAD_ASSERT_MSG(false, "invalid handle");
81 return false;
82 }
83
84 if (!mArchive)
85 {
86 SEAD_ASSERT_MSG(false, "no archive mounted");
87 return false;
88 }
89
90 *fileSize = getArchiveFileHandle_(handle)->mFileInfo.mLength;
91 return true;
92}
93
94ArchiveFileDevice::ArchiveFileHandle*
95ArchiveFileDevice::getArchiveFileHandle_(FileHandle* handle) const
96{
97 return reinterpret_cast<ArchiveFileHandle*>(getHandleBaseHandleBuffer_(handle).getBufferPtr());
98}
99
100ArchiveFileDevice::ArchiveFileHandle*
101ArchiveFileDevice::constructArchiveFileHandle_(FileHandle* handle) const
102{
103 return new (getHandleBaseHandleBuffer_(handle).getBufferPtr()) ArchiveFileHandle;
104}
105
106bool ArchiveFileDevice::doIsExistFile_(bool* exists, const SafeString& path)
107{
108 if (!mArchive)
109 {
110 SEAD_ASSERT_MSG(false, "no archive mounted");
111 return false;
112 }
113
114 if (!path.cstr())
115 {
116 SEAD_ASSERT_MSG(false, "invalid path");
117 return false;
118 }
119
120 *exists = mArchive->isExistFile(path);
121 return true;
122}
123
124bool ArchiveFileDevice::doIsExistDirectory_(bool* exists, const SafeString& path)
125{
126 if (!mArchive)
127 {
128 SEAD_ASSERT_MSG(false, "no archive mounted");
129 return false;
130 }
131
132 if (!path.cstr())
133 {
134 SEAD_ASSERT_MSG(false, "invalid path");
135 return false;
136 }
137
138 *exists = false;
139 return true;
140}
141
142u8* ArchiveFileDevice::doLoadWithEntryID_(s32 entry_id, LoadArg& arg)
143{
144 if (entry_id == -1)
145 {
146 SEAD_ASSERT_MSG(false, "Invalid entry_id");
147 return nullptr;
148 }
149
150 if (arg.buffer_size_alignment % 32 != 0)
151 {
152 SEAD_ASSERT_MSG(false, "arg.buffer_size_alignment[%u] is not multipe of 32",
153 arg.buffer_size_alignment);
154 return nullptr;
155 }
156
157 if (arg.buffer || arg.heap)
158 {
159 FileHandle handle;
160 if (!tryOpenWithEntryID(handle: &handle, id: entry_id, flag: {}, div_size: arg.div_size))
161 return nullptr;
162
163 // Determine the buffer size.
164 u32 buffer_size = arg.buffer_size;
165 if (buffer_size == 0)
166 {
167 u32 file_size = 0;
168 if (!tryGetFileSize(size: &file_size, handle: &handle))
169 return nullptr;
170
171 SEAD_ASSERT(file_size != 0);
172
173 if (arg.buffer_size_alignment)
174 buffer_size = Mathu::roundUp(x: file_size, multNumber: arg.buffer_size_alignment);
175 else
176 buffer_size = Mathi::roundUpPow2(val: file_size, base: cBufferMinAlignment);
177 }
178
179 // Allocate the buffer if need be.
180 u8* buffer = arg.buffer;
181 bool buffer_allocated = false;
182 if (!buffer)
183 {
184 const s32 aligment_sign = Mathi::sign(value: arg.alignment);
185 const s32 alignment = std::max(a: Mathi::abs(x: arg.alignment), b: 32);
186 buffer = new (arg.heap, alignment * aligment_sign) u8[buffer_size];
187 buffer_allocated = true;
188 }
189
190 u32 bytes_read = 0;
191 if (!tryRead(bytesRead: &bytes_read, handle: &handle, outBuffer: buffer, bytesToRead: buffer_size) || !tryClose(handle: &handle))
192 {
193 // Clean up the allocation on failure.
194 if (buffer && buffer_allocated)
195 delete[] buffer;
196 return nullptr;
197 }
198
199 arg.read_size = bytes_read;
200 arg.need_unload = buffer_allocated;
201 arg.roundup_size = buffer_size;
202 return buffer;
203 }
204
205 ArchiveRes::FileInfo info{};
206 auto* ret = mArchive->getFileFast(entry_id, info: &info);
207 if (!ret)
208 return nullptr;
209
210 SEAD_ASSERT(arg.alignment == 0 || PtrUtil::isAligned(ret, Mathi::abs(arg.alignment)));
211 if (arg.buffer_size_alignment && info.mLength % arg.buffer_size_alignment != 0)
212 {
213 SEAD_WARN("archive file size[%u] is not multipe of arg.buffer_size_alignment[%u]",
214 info.mLength, arg.buffer_size_alignment);
215 return nullptr;
216 }
217
218 arg.read_size = info.mLength;
219 arg.roundup_size = info.mLength;
220 arg.need_unload = false;
221 return const_cast<u8*>(static_cast<const u8*>(ret));
222}
223
224u8* ArchiveFileDevice::doLoad_(LoadArg& arg)
225{
226 if (!mArchive)
227 {
228 SEAD_ASSERT_MSG(false, "no archive mounted");
229 return nullptr;
230 }
231
232 if (arg.buffer || arg.heap)
233 return FileDevice::doLoad_(arg);
234
235 ArchiveRes::FileInfo info{};
236 auto* ret = mArchive->getFile(file_path: arg.path, info: &info);
237 if (!ret)
238 return nullptr;
239
240 SEAD_ASSERT(arg.alignment == 0 || PtrUtil::isAligned(ret, Mathi::abs(arg.alignment)));
241 if (arg.buffer_size_alignment && info.mLength % arg.buffer_size_alignment != 0)
242 {
243 SEAD_WARN("archive file size[%u] is not multipe of arg.buffer_size_alignment[%u]",
244 info.mLength, arg.buffer_size_alignment);
245 return nullptr;
246 }
247
248 arg.read_size = info.mLength;
249 arg.roundup_size = info.mLength;
250 arg.need_unload = false;
251 return const_cast<u8*>(static_cast<const u8*>(ret));
252}
253
254FileDevice* ArchiveFileDevice::doOpen_(FileHandle* handle, const SafeString& path,
255 FileDevice::FileOpenFlag)
256{
257 if (!handle)
258 {
259 SEAD_ASSERT_MSG(false, "invalid handle");
260 return nullptr;
261 }
262
263 if (!mArchive)
264 {
265 SEAD_ASSERT_MSG(false, "no archive mounted");
266 return nullptr;
267 }
268
269 if (!path.cstr())
270 {
271 SEAD_ASSERT_MSG(false, "invalid filename");
272 return nullptr;
273 }
274
275 auto* inner = constructArchiveFileHandle_(handle);
276
277 auto* file_data = static_cast<const u8*>(mArchive->getFile(file_path: path, info: &inner->mFileInfo));
278 if (!file_data)
279 return nullptr;
280
281 inner->mFileData = file_data;
282 inner->mPos = 0;
283 return this;
284}
285
286FileDevice* ArchiveFileDevice::doOpenWithEntryID_(FileHandle* handle, s32 id,
287 FileDevice::FileOpenFlag)
288{
289 if (!handle)
290 {
291 SEAD_ASSERT_MSG(false, "invalid handle");
292 return nullptr;
293 }
294
295 if (!mArchive)
296 {
297 SEAD_ASSERT_MSG(false, "no archive mounted");
298 return nullptr;
299 }
300
301 auto* inner = constructArchiveFileHandle_(handle);
302
303 auto* file_data = static_cast<const u8*>(mArchive->getFileFast(entry_id: id, info: &inner->mFileInfo));
304 if (!file_data)
305 return nullptr;
306
307 inner->mFileData = file_data;
308 inner->mPos = 0;
309 return this;
310}
311
312s32 ArchiveFileDevice::doConvertPathToEntryID_(const SafeString& path)
313{
314 if (!mArchive)
315 {
316 SEAD_ASSERT_MSG(false, "no archive mounted");
317 return 0;
318 }
319 return mArchive->convertPathToEntryID(path);
320}
321
322bool ArchiveFileDevice::doClose_(FileHandle*)
323{
324 return true;
325}
326
327bool ArchiveFileDevice::doFlush_(FileHandle*)
328{
329 SEAD_ASSERT_MSG(false, "not supported");
330 return false;
331}
332
333bool ArchiveFileDevice::doRemove_(const SafeString&)
334{
335 SEAD_ASSERT_MSG(false, "not supported");
336 return false;
337}
338
339bool ArchiveFileDevice::doRead_(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead)
340{
341 ArchiveFileHandle* inner = getArchiveFileHandle_(handle);
342
343 u32 read_size;
344 if (inner->mPos + bytesToRead <= inner->mFileInfo.mLength)
345 read_size = bytesToRead;
346 else
347 read_size = inner->mFileInfo.mLength - inner->mPos;
348
349 MemUtil::copy(dest: outBuffer, src: inner->mFileData + inner->mPos, size: read_size);
350
351 inner->mPos += read_size;
352 if (bytesRead)
353 *bytesRead = read_size;
354
355 return true;
356}
357
358bool ArchiveFileDevice::doSeek_(FileHandle* handle, s32 offset, FileDevice::SeekOrigin origin)
359{
360 ArchiveFileHandle* inner = getArchiveFileHandle_(handle);
361 u32 new_position;
362 switch (origin)
363 {
364 case cSeekOrigin_Begin:
365 new_position = offset;
366 break;
367 case cSeekOrigin_Current:
368 new_position = inner->mPos + offset;
369 break;
370 case cSeekOrigin_End:
371 new_position = inner->mFileInfo.mLength + offset;
372 break;
373 default:
374 SEAD_ASSERT_MSG(false, "Unexpected origin");
375 return false;
376 }
377
378 if (new_position > inner->mFileInfo.mLength)
379 return false;
380
381 inner->mPos = new_position;
382 return true;
383}
384
385bool ArchiveFileDevice::doGetCurrentSeekPos_(u32* seekPos, FileHandle* handle)
386{
387 if (!handle)
388 {
389 SEAD_ASSERT_MSG(false, "invalid handle");
390 return false;
391 }
392
393 if (!mArchive)
394 {
395 SEAD_ASSERT_MSG(false, "no archive mounted");
396 return false;
397 }
398
399 ArchiveFileHandle* inner = getArchiveFileHandle_(handle);
400 *seekPos = inner->mPos;
401 return true;
402}
403
404FileDevice* ArchiveFileDevice::doOpenDirectory_(DirectoryHandle* handle, const SafeString& path)
405{
406 if (!handle)
407 {
408 SEAD_ASSERT_MSG(false, "invalid handle");
409 return nullptr;
410 }
411
412 if (!mArchive)
413 {
414 SEAD_ASSERT_MSG(false, "no archive mounted");
415 return nullptr;
416 }
417
418 if (!mArchive->openDirectory(handle: &getHandleBaseHandleBuffer_(handle), dir: path))
419 return nullptr;
420
421 return this;
422}
423
424bool ArchiveFileDevice::doCloseDirectory_(DirectoryHandle* handle)
425{
426 if (!mArchive)
427 {
428 SEAD_ASSERT_MSG(false, "no archive mounted");
429 return false;
430 }
431 return mArchive->closeDirectory(handle: &getHandleBaseHandleBuffer_(handle));
432}
433
434bool ArchiveFileDevice::doReadDirectory_(u32* entriesRead, DirectoryHandle* handle,
435 DirectoryEntry* entry, u32 entriesToRead)
436{
437 auto* archive = mArchive;
438 if (!archive)
439 {
440 SEAD_ASSERT_MSG(false, "no archive mounted");
441 return false;
442 }
443
444 auto* buffer = &getHandleBaseHandleBuffer_(handle);
445 SEAD_ASSERT(entry);
446 const u32 actual_read_count = archive->readDirectory(handle: buffer, entries: entry, num: entriesToRead);
447 if (entriesRead)
448 *entriesRead = actual_read_count;
449
450 return true;
451}
452
453bool ArchiveFileDevice::doSetCurrentDirectory_(const SafeString& path)
454{
455 if (!mArchive)
456 {
457 SEAD_ASSERT_MSG(false, "no archive mounted");
458 return false;
459 }
460
461 if (!path.cstr())
462 {
463 SEAD_ASSERT_MSG(false, "invalid filename");
464 return false;
465 }
466
467 return mArchive->setCurrentDirectory(path);
468}
469
470bool ArchiveFileDevice::doMakeDirectory_(const SafeString&, u32)
471{
472 return false;
473}
474
475s32 ArchiveFileDevice::doGetLastRawError_() const
476{
477 SEAD_ASSERT_MSG(false, "not impremented");
478 return 0;
479}
480} // namespace sead
481