diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 978e956dc5..9ee803fda3 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -8,6 +8,7 @@ set(SRCS core.cpp
system.cpp
arm/disassembler/arm_disasm.cpp
arm/disassembler/load_symbol_map.cpp
+ file_sys/archive_romfs.cpp
arm/interpreter/arm_interpreter.cpp
arm/interpreter/armcopro.cpp
arm/interpreter/armemu.cpp
@@ -75,7 +76,8 @@ set(HEADERS core.h
arm/interpreter/vfp/asm_vfp.h
arm/interpreter/vfp/vfp.h
arm/interpreter/vfp/vfp_helper.h
- file_sys/file_sys.h
+ file_sys/archive.h
+ file_sys/archive_romfs.h
hle/config_mem.h
hle/coprocessor.h
hle/hle.h
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index 63efe7c4d3..4e521903c4 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -162,6 +162,7 @@
+
@@ -211,7 +212,8 @@
-
+
+
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index 39a3cdc4bf..17829b8b13 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -176,6 +176,9 @@
hle\service
+
+ file_sys
+
@@ -205,9 +208,6 @@
arm\interpreter
-
- file_sys
-
hw
@@ -314,6 +314,12 @@
hle\service
+
+ file_sys
+
+
+ file_sys
+
diff --git a/src/core/file_sys/archive.h b/src/core/file_sys/archive.h
new file mode 100644
index 0000000000..ed2d836406
--- /dev/null
+++ b/src/core/file_sys/archive.h
@@ -0,0 +1,54 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+class Archive : NonCopyable {
+public:
+ /// Supported archive types
+ enum class IdCode : u32 {
+ RomFS = 0x00000003,
+ SaveData = 0x00000004,
+ ExtSaveData = 0x00000006,
+ SharedExtSaveData = 0x00000007,
+ SystemSaveData = 0x00000008,
+ SDMC = 0x00000009,
+ SDMCWriteOnly = 0x0000000A,
+ };
+
+ Archive() { }
+ virtual ~Archive() { }
+
+ /**
+ * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
+ * @return IdCode of the archive
+ */
+ virtual IdCode GetIdCode() const = 0;
+
+ /**
+ * Read data from the archive
+ * @param offset Offset in bytes to start reading archive from
+ * @param length Length in bytes to read data from archive
+ * @param buffer Buffer to read data into
+ * @return Number of bytes read
+ */
+ virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0;
+
+ /**
+ * Get the size of the archive in bytes
+ * @return Size of the archive in bytes
+ */
+ virtual size_t GetSize() const = 0;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.cpp b/src/core/file_sys/archive_romfs.cpp
new file mode 100644
index 0000000000..6fdb768d68
--- /dev/null
+++ b/src/core/file_sys/archive_romfs.cpp
@@ -0,0 +1,46 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive_romfs.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+Archive_RomFS::Archive_RomFS(Loader::AppLoader& app_loader) {
+ // Load the RomFS from the app
+ if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) {
+ WARN_LOG(FILESYS, "Unable to read RomFS!");
+ }
+}
+
+Archive_RomFS::~Archive_RomFS() {
+}
+
+/**
+ * Read data from the archive
+ * @param offset Offset in bytes to start reading archive from
+ * @param length Length in bytes to read data from archive
+ * @param buffer Buffer to read data into
+ * @return Number of bytes read
+ */
+size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
+ DEBUG_LOG(FILESYS, "called offset=%d, length=%d", offset, length);
+ memcpy(buffer, &raw_data[(u32)offset], length);
+ return length;
+}
+
+/**
+ * Get the size of the archive in bytes
+ * @return Size of the archive in bytes
+ */
+size_t Archive_RomFS::GetSize() const {
+ ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
+ return 0;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/archive_romfs.h b/src/core/file_sys/archive_romfs.h
new file mode 100644
index 0000000000..60af8ff0d3
--- /dev/null
+++ b/src/core/file_sys/archive_romfs.h
@@ -0,0 +1,50 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+#include "common/common_types.h"
+
+#include "core/file_sys/archive.h"
+#include "core/loader/loader.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// FileSys namespace
+
+namespace FileSys {
+
+/// File system interface to the RomFS archive
+class Archive_RomFS : public Archive {
+public:
+ Archive_RomFS(Loader::AppLoader& app_loader);
+ ~Archive_RomFS();
+
+ /**
+ * Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
+ * @return IdCode of the archive
+ */
+ IdCode GetIdCode() const { return IdCode::RomFS; };
+
+ /**
+ * Read data from the archive
+ * @param offset Offset in bytes to start reading archive from
+ * @param length Length in bytes to read data from archive
+ * @param buffer Buffer to read data into
+ * @return Number of bytes read
+ */
+ size_t Read(const u64 offset, const u32 length, u8* buffer) const;
+
+ /**
+ * Get the size of the archive in bytes
+ * @return Size of the archive in bytes
+ */
+ size_t GetSize() const;
+
+private:
+ std::vector raw_data;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/file_sys.h b/src/core/file_sys/file_sys.h
deleted file mode 100644
index bb8503e62f..0000000000
--- a/src/core/file_sys/file_sys.h
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) 2012- PPSSPP Project.
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 2.0 or later versions.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License 2.0 for more details.
-
-// A copy of the GPL 2.0 should have been included with the program.
-// If not, see http://www.gnu.org/licenses/
-
-// Official git repository and contact information can be found at
-// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
-
-#pragma once
-
-#include "common/common.h"
-#include "common/chunk_file.h"
-
-enum FileAccess {
- FILEACCESS_NONE=0,
- FILEACCESS_READ=1,
- FILEACCESS_WRITE=2,
- FILEACCESS_APPEND=4,
- FILEACCESS_CREATE=8
-};
-
-enum FileMove {
- FILEMOVE_BEGIN=0,
- FILEMOVE_CURRENT=1,
- FILEMOVE_END=2
-};
-
-enum FileType {
- FILETYPE_NORMAL=1,
- FILETYPE_DIRECTORY=2
-};
-
-
-class IHandleAllocator {
-public:
- virtual ~IHandleAllocator() {}
- virtual u32 GetNewHandle() = 0;
- virtual void FreeHandle(u32 handle) = 0;
-};
-
-class SequentialHandleAllocator : public IHandleAllocator {
-public:
- SequentialHandleAllocator() : handle_(1) {}
- virtual u32 GetNewHandle() { return handle_++; }
- virtual void FreeHandle(u32 handle) {}
-private:
- int handle_;
-};
-
-struct FileInfo {
- FileInfo()
- : size(0), access(0), exists(false), type(FILETYPE_NORMAL), isOnSectorSystem(false), startSector(0), numSectors(0) {}
-
- void DoState(PointerWrap &p) {
- auto s = p.Section("FileInfo", 1);
- if (!s)
- return;
-
- p.Do(name);
- p.Do(size);
- p.Do(access);
- p.Do(exists);
- p.Do(type);
- p.Do(atime);
- p.Do(ctime);
- p.Do(mtime);
- p.Do(isOnSectorSystem);
- p.Do(startSector);
- p.Do(numSectors);
- p.Do(sectorSize);
- }
-
- std::string name;
- s64 size;
- u32 access; //unix 777
- bool exists;
- FileType type;
-
- tm atime;
- tm ctime;
- tm mtime;
-
- bool isOnSectorSystem;
- u32 startSector;
- u32 numSectors;
- u32 sectorSize;
-};
-
-
-class IFileSystem {
-public:
- virtual ~IFileSystem() {}
-
- virtual void DoState(PointerWrap &p) = 0;
- virtual std::vector GetDirListing(std::string path) = 0;
- virtual u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) = 0;
- virtual void CloseFile(u32 handle) = 0;
- virtual size_t ReadFile(u32 handle, u8 *pointer, s64 size) = 0;
- virtual size_t WriteFile(u32 handle, const u8 *pointer, s64 size) = 0;
- virtual size_t SeekFile(u32 handle, s32 position, FileMove type) = 0;
- virtual FileInfo GetFileInfo(std::string filename) = 0;
- virtual bool OwnsHandle(u32 handle) = 0;
- virtual bool MkDir(const std::string &dirname) = 0;
- virtual bool RmDir(const std::string &dirname) = 0;
- virtual int RenameFile(const std::string &from, const std::string &to) = 0;
- virtual bool RemoveFile(const std::string &filename) = 0;
- virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0;
-};
-
-
-class EmptyFileSystem : public IFileSystem {
-public:
- virtual void DoState(PointerWrap &p) {}
- std::vector GetDirListing(std::string path) {std::vector vec; return vec;}
- u32 OpenFile(std::string filename, FileAccess access, const char *devicename=NULL) {return 0;}
- void CloseFile(u32 handle) {}
- size_t ReadFile(u32 handle, u8 *pointer, s64 size) {return 0;}
- size_t WriteFile(u32 handle, const u8 *pointer, s64 size) {return 0;}
- size_t SeekFile(u32 handle, s32 position, FileMove type) {return 0;}
- FileInfo GetFileInfo(std::string filename) {FileInfo f; return f;}
- bool OwnsHandle(u32 handle) {return false;}
- virtual bool MkDir(const std::string &dirname) {return false;}
- virtual bool RmDir(const std::string &dirname) {return false;}
- virtual int RenameFile(const std::string &from, const std::string &to) {return -1;}
- virtual bool RemoveFile(const std::string &filename) {return false;}
- virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;}
-};
-
-
diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp
index d7351e702b..f31c02ea91 100644
--- a/src/core/hle/kernel/archive.cpp
+++ b/src/core/hle/kernel/archive.cpp
@@ -4,6 +4,8 @@
#include "common/common_types.h"
+#include "core/file_sys/archive.h"
+#include "core/hle/service/service.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/archive.h"
@@ -12,6 +14,21 @@
namespace Kernel {
+// Command to access archive file
+enum class FileCommand : u32 {
+ Dummy1 = 0x000100C6,
+ Control = 0x040100C4,
+ OpenSubFile = 0x08010100,
+ Read = 0x080200C2,
+ Write = 0x08030102,
+ GetSize = 0x08040000,
+ SetSize = 0x08050080,
+ GetAttributes = 0x08060000,
+ SetAttributes = 0x08070040,
+ Close = 0x08080000,
+ Flush = 0x08090000,
+};
+
class Archive : public Object {
public:
const char* GetTypeName() const { return "Archive"; }
@@ -20,7 +37,37 @@ public:
static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
Kernel::HandleType GetHandleType() const { return HandleType::Archive; }
- std::string name; ///< Name of archive (optional)
+ std::string name; ///< Name of archive (optional)
+ FileSys::Archive* backend; ///< Archive backend interface
+
+ /**
+ * Synchronize kernel object
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result SyncRequest(bool* wait) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+ FileCommand cmd = static_cast(cmd_buff[0]);
+ switch (cmd) {
+
+ // Read from archive...
+ case FileCommand::Read:
+ {
+ u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
+ u32 length = cmd_buff[3];
+ u32 address = cmd_buff[5];
+ cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
+ break;
+ }
+
+ // Unknown command...
+ default:
+ ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
+ return -1;
+ }
+ cmd_buff[1] = 0; // No error
+ return 0;
+ }
/**
* Wait for kernel object to synchronize
@@ -29,32 +76,71 @@ public:
*/
Result WaitSynchronization(bool* wait) {
// TODO(bunnei): ImplementMe
- ERROR_LOG(OSHLE, "unimplemented function");
+ ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
return 0;
}
};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+std::map g_archive_map; ///< Map of file archives by IdCode
+
+/**
+ * Opens an archive
+ * @param id_code IdCode of the archive to open
+ * @return Handle to archive if it exists, otherwise a null handle (0)
+ */
+Handle OpenArchive(FileSys::Archive::IdCode id_code) {
+ auto itr = g_archive_map.find(id_code);
+ if (itr == g_archive_map.end()) {
+ return 0;
+ }
+ return itr->second;
+}
+
+/**
+ * Mounts an archive
+ * @param archive Pointer to the archive to mount
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result MountArchive(Archive* archive) {
+ FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
+ if (0 != OpenArchive(id_code)) {
+ ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
+ return -1;
+ }
+ g_archive_map[id_code] = archive->GetHandle();
+ INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName());
+ return 0;
+}
+
/**
* Creates an Archive
- * @param name Optional name of Archive
* @param handle Handle to newly created archive object
+ * @param backend File system backend interface to the archive
+ * @param name Optional name of Archive
* @return Newly created Archive object
*/
-Archive* CreateArchive(Handle& handle, const std::string& name) {
+Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
Archive* archive = new Archive;
handle = Kernel::g_object_pool.Create(archive);
archive->name = name;
+ archive->backend = backend;
+
+ MountArchive(archive);
+
return archive;
}
/**
* Creates an Archive
+ * @param backend File system backend interface to the archive
* @param name Optional name of Archive
* @return Handle to newly created Archive object
*/
-Handle CreateArchive(const std::string& name) {
+Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
Handle handle;
- Archive* archive = CreateArchive(handle, name);
+ Archive* archive = CreateArchive(handle, backend, name);
return handle;
}
diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h
index 98670f06c3..07fb37ae7c 100644
--- a/src/core/hle/kernel/archive.h
+++ b/src/core/hle/kernel/archive.h
@@ -7,6 +7,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/file_sys/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
@@ -14,10 +15,18 @@
namespace Kernel {
/**
- * Creates an archive
- * @param name Optional name of archive
- * @return Handle to newly created archive object
+ * Opens an archive
+ * @param id_code IdCode of the archive to open
+ * @return Handle to archive if it exists, otherwise a null handle (0)
*/
-Handle CreateArchive(const std::string& name="Unknown");
+Handle OpenArchive(FileSys::Archive::IdCode id_code);
+
+/**
+ * Creates an Archive
+ * @param backend File system backend interface to the archive
+ * @param name Optional name of Archive
+ * @return Handle to newly created Archive object
+ */
+Handle CreateArchive(FileSys::Archive* backend, const std::string& name);
} // namespace FileSys
diff --git a/src/core/hle/service/fs.cpp b/src/core/hle/service/fs.cpp
index 3a5afaa3ca..5eabf36ad5 100644
--- a/src/core/hle/service/fs.cpp
+++ b/src/core/hle/service/fs.cpp
@@ -2,11 +2,12 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
-
#include "common/common.h"
+#include "core/loader/loader.h"
#include "core/hle/hle.h"
#include "core/hle/service/fs.h"
+#include "core/hle/kernel/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace FS_User
@@ -15,8 +16,34 @@ namespace FS_User {
void Initialize(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
- DEBUG_LOG(KERNEL, "called");
cmd_buff[1] = 0; // No error
+ DEBUG_LOG(KERNEL, "called");
+}
+
+void OpenFileDirectly(Service::Interface* self) {
+ u32* cmd_buff = Service::GetCommandBuffer();
+
+ FileSys::Archive::IdCode arch_id = static_cast(cmd_buff[2]);
+
+ // TODO(bunnei): Properly implement use of these...
+ //u32 transaction = cmd_buff[1];
+ //u32 arch_lowpath_type = cmd_buff[3];
+ //u32 arch_lowpath_sz = cmd_buff[4];
+ //u32 file_lowpath_type = cmd_buff[5];
+ //u32 file_lowpath_sz = cmd_buff[6];
+ //u32 flags = cmd_buff[7];
+ //u32 attr = cmd_buff[8];
+ //u32 arch_lowpath_desc = cmd_buff[9];
+ //u32 arch_lowpath_ptr = cmd_buff[10];
+ //u32 file_lowpath_desc = cmd_buff[11];
+ //u32 file_lowpath_ptr = cmd_buff[12];
+
+ Handle handle = Kernel::OpenArchive(arch_id);
+ if (0 != handle) {
+ cmd_buff[1] = 0; // No error
+ cmd_buff[3] = handle;
+ }
+ DEBUG_LOG(KERNEL, "called");
}
const Interface::FunctionInfo FunctionTable[] = {
@@ -24,7 +51,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x040100C4, nullptr, "Control"},
{0x08010002, Initialize, "Initialize"},
{0x080201C2, nullptr, "OpenFile"},
- {0x08030204, nullptr, "OpenFileDirectly"},
+ {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
{0x08040142, nullptr, "DeleteFile"},
{0x08050244, nullptr, "RenameFile"},
{0x08060142, nullptr, "DeleteDirectory"},
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 96cb81de03..2b42e3c647 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -4,9 +4,11 @@
#include
+#include "core/file_sys/archive_romfs.h"
#include "core/loader/loader.h"
#include "core/loader/elf.h"
#include "core/loader/ncch.h"
+#include "core/hle/kernel/archive.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -51,14 +53,20 @@ ResultStatus LoadFile(const std::string& filename) {
switch (IdentifyFile(filename)) {
// Standard ELF file format...
- case FileType::ELF: {
+ case FileType::ELF:
return AppLoader_ELF(filename).Load();
- }
// NCCH/NCSD container formats...
case FileType::CXI:
case FileType::CCI: {
- return AppLoader_NCCH(filename).Load();
+ AppLoader_NCCH app_loader(filename);
+
+ // Load application and RomFS
+ if (ResultStatus::Success == app_loader.Load()) {
+ Kernel::CreateArchive(new FileSys::Archive_RomFS(app_loader), "RomFS");
+ return ResultStatus::Success;
+ }
+ break;
}
// Error occurred durring IdentifyFile...
@@ -70,7 +78,6 @@ ResultStatus LoadFile(const std::string& filename) {
default:
return ResultStatus::ErrorInvalidFormat;
}
-
return ResultStatus::Error;
}