aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp')
-rw-r--r--Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp279
1 files changed, 279 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp b/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp
new file mode 100644
index 00000000..5aa0491f
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp
@@ -0,0 +1,279 @@
+/*
+ * unrar.cpp
+ * ---------
+ * Purpose: Implementation file for extracting modules from .rar archives
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+#include "stdafx.h"
+#include "unrar.h"
+
+#ifdef MPT_WITH_UNRAR
+
+#include "../common/mptFileIO.h"
+
+#if MPT_OS_WINDOWS
+ #include <windows.h>
+#else // !MPT_OS_WINDOWS
+ #ifdef _UNIX
+ #define MPT_UNRAR_UNIX_WAS_DEFINED
+ #else
+ #define _UNIX
+ #endif
+#endif // MPT_OS_WINDOWS
+
+#include "unrar/dll.hpp"
+
+#if !MPT_OS_WINDOWS
+ #ifndef MPT_UNRAR_UNIX_WAS_DEFINED
+ #undef _UNIX
+ #undef MPT_UNRAR_UNIX_WAS_DEFINED
+ #endif
+#endif // !MPT_OS_WINDOWS
+
+#endif // MPT_WITH_UNRAR
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+#ifdef MPT_WITH_UNRAR
+
+
+struct RARHandle // RAII
+{
+ HANDLE rar = nullptr;
+ explicit RARHandle(HANDLE rar_) : rar(rar_) { return; }
+ RARHandle(const RARHandle &) = delete;
+ ~RARHandle() { if(rar) RARCloseArchive(rar); }
+
+ operator HANDLE () const { return rar; }
+};
+
+
+static int CALLBACK RARCallback(unsigned int msg, LPARAM userData, LPARAM p1, LPARAM p2)
+{
+ int result = 0;
+ CRarArchive *that = reinterpret_cast<CRarArchive *>(userData);
+ switch(msg)
+ {
+ case UCM_PROCESSDATA:
+ // Receive extracted data
+ that->RARCallbackProcessData(reinterpret_cast<const char *>(p1), p2);
+ result = 1;
+ break;
+ default:
+ // No support for passwords or volumes
+ result = -1;
+ break;
+ }
+ return result;
+}
+
+
+void CRarArchive::RARCallbackProcessData(const char * buf, std::size_t size)
+{
+ if(!captureCurrentFile)
+ {
+ return;
+ }
+ mpt::append(data, buf, buf + size);
+}
+
+
+CRarArchive::CRarArchive(FileReader &file)
+ : ArchiveBase(file)
+{
+ // NOTE:
+ // We open the archive twice, once for listing the contents in the
+ // constructor and once for actual decompression in ExtractFile.
+ // For solid archives, listing the contents via RAR_OM_LIST is way faster.
+ // The overhead of opening twice for non-solid archives is negligable if the
+ // archive does not contain a lot of files (and archives with large amount of
+ // files are pretty useless for OpenMPT anyway).
+
+ // Early reject files with no Rar! magic
+ // so that we do not have to instantiate OnDiskFileWrapper.
+ inFile.Rewind();
+ if(!inFile.ReadMagic("Rar!\x1A"))
+ {
+ return;
+ }
+ inFile.Rewind();
+
+ diskFile = std::make_unique<OnDiskFileWrapper>(inFile);
+ if(!diskFile->IsValid())
+ {
+ return;
+ }
+
+ std::wstring ArcName = diskFile->GetFilename().ToWide();
+ std::vector<wchar_t> ArcNameBuf(ArcName.c_str(), ArcName.c_str() + ArcName.length() + 1);
+ std::vector<wchar_t> CmtBuf(65536);
+ RAROpenArchiveDataEx ArchiveData = {};
+ ArchiveData.OpenMode = RAR_OM_LIST;
+ ArchiveData.ArcNameW = ArcNameBuf.data();
+ ArchiveData.CmtBufW = CmtBuf.data();
+ ArchiveData.CmtBufSize = static_cast<unsigned int>(CmtBuf.size());
+ RARHandle rar(RAROpenArchiveEx(&ArchiveData));
+ if(!rar)
+ {
+ Reset();
+ return;
+ }
+
+ switch(ArchiveData.CmtState)
+ {
+ case 1:
+ if(ArchiveData.CmtSize)
+ {
+ comment = mpt::ToUnicode(std::wstring(ArchiveData.CmtBufW, ArchiveData.CmtBufW + ArchiveData.CmtSize - 1));
+ break;
+ }
+ [[fallthrough]];
+ case 0:
+ comment = mpt::ustring();
+ break;
+ default:
+ Reset();
+ return;
+ break;
+ }
+
+ bool eof = false;
+ int RARResult = 0;
+ while(!eof)
+ {
+ RARHeaderDataEx HeaderData = {};
+ RARResult = RARReadHeaderEx(rar, &HeaderData);
+ switch(RARResult)
+ {
+ case ERAR_SUCCESS:
+ break;
+ case ERAR_END_ARCHIVE:
+ eof = true;
+ continue;
+ break;
+ default:
+ Reset();
+ return;
+ break;
+ }
+ ArchiveFileInfo fileInfo;
+ fileInfo.name = mpt::PathString::FromWide(HeaderData.FileNameW);
+ fileInfo.type = ArchiveFileType::Normal;
+ fileInfo.size = HeaderData.UnpSize;
+ contents.push_back(fileInfo);
+ RARResult = RARProcessFileW(rar, RAR_SKIP, NULL, NULL);
+ switch(RARResult)
+ {
+ case ERAR_SUCCESS:
+ break;
+ default:
+ Reset();
+ return;
+ break;
+ }
+ }
+
+}
+
+
+CRarArchive::~CRarArchive()
+{
+}
+
+
+bool CRarArchive::ExtractFile(std::size_t index)
+{
+
+ if(!diskFile || !diskFile->IsValid())
+ {
+ return false;
+ }
+
+ if(index >= contents.size())
+ {
+ return false;
+ }
+
+ std::wstring ArcName = diskFile->GetFilename().ToWide();
+ std::vector<wchar_t> ArcNameBuf(ArcName.c_str(), ArcName.c_str() + ArcName.length() + 1);
+ RAROpenArchiveDataEx ArchiveData = {};
+ ArchiveData.OpenMode = RAR_OM_EXTRACT;
+ ArchiveData.ArcNameW = ArcNameBuf.data();
+ ArchiveData.Callback = RARCallback;
+ ArchiveData.UserData = reinterpret_cast<LPARAM>(this);
+ RARHandle rar(RAROpenArchiveEx(&ArchiveData));
+ if(!rar)
+ {
+ ResetFile();
+ return false;
+ }
+
+ std::size_t i = 0;
+ int RARResult = 0;
+ bool eof = false;
+ while(!eof)
+ {
+ RARHeaderDataEx HeaderData = {};
+ RARResult = RARReadHeaderEx(rar, &HeaderData);
+ switch(RARResult)
+ {
+ case ERAR_SUCCESS:
+ break;
+ case ERAR_END_ARCHIVE:
+ eof = true;
+ continue;
+ break;
+ default:
+ ResetFile();
+ return false;
+ break;
+ }
+ captureCurrentFile = (i == index);
+ RARResult = RARProcessFileW(rar, captureCurrentFile ? RAR_TEST : RAR_SKIP, NULL, NULL);
+ switch(RARResult)
+ {
+ case ERAR_SUCCESS:
+ break;
+ default:
+ ResetFile();
+ return false;
+ break;
+ }
+ if(captureCurrentFile)
+ { // done
+ return true;
+ }
+ captureCurrentFile = false;
+ ++i;
+ }
+
+ return false;
+
+}
+
+
+void CRarArchive::Reset()
+{
+ captureCurrentFile = false;
+ comment = mpt::ustring();
+ contents = std::vector<ArchiveFileInfo>();
+ data = std::vector<char>();
+}
+
+
+void CRarArchive::ResetFile()
+{
+ captureCurrentFile = false;
+ data = std::vector<char>();
+}
+
+
+#endif // MPT_WITH_UNRAR
+
+
+OPENMPT_NAMESPACE_END