Unpack now works.
This commit is contained in:
Родитель
56712c5d65
Коммит
ae88fe63e8
|
@ -107,6 +107,9 @@ Temporary Items
|
|||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Intermediate test files/data
|
||||
test/unpack
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@
|
|||
<ClInclude Include="..\..\lib\zlib\zlib.h" />
|
||||
<ClInclude Include="..\..\src\inc\Base64Stream.hpp" />
|
||||
<ClInclude Include="..\..\src\inc\CRC32Stream.hpp" />
|
||||
<ClInclude Include="..\..\src\inc\DirectoryObject.hpp" />
|
||||
<ClInclude Include="..\..\src\inc\Exceptions.hpp" />
|
||||
<ClInclude Include="..\..\src\inc\FileStream.hpp" />
|
||||
<ClInclude Include="..\..\src\inc\OffsetStream.hpp" />
|
||||
|
@ -159,7 +160,9 @@
|
|||
<None Include="xPlatAppx.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\xPlatAppx\PAL\FileSystem\Win32\DirectoryObject.cpp" />
|
||||
<ClCompile Include="..\..\src\xPlatAppx\xPlatAppx.cpp" />
|
||||
<ClCompile Include="..\..\src\xPlatAppx\ZipFileStream.cpp" />
|
||||
<ClCompile Include="..\..\src\xPlatAppx\ZipObject.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
|
|
@ -54,6 +54,9 @@
|
|||
<ClInclude Include="..\..\src\inc\ZipObject.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\inc\DirectoryObject.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="xPlatAppx.def">
|
||||
|
@ -67,5 +70,11 @@
|
|||
<ClCompile Include="..\..\src\xPlatAppx\ZipObject.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\xPlatAppx\PAL\FileSystem\Win32\DirectoryObject.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\xPlatAppx\ZipFileStream.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "StorageObject.hpp"
|
||||
|
||||
namespace xPlat {
|
||||
|
||||
class DirectoryObject : public StorageObject
|
||||
{
|
||||
public:
|
||||
DirectoryObject(std::string root) : m_root(root) {}
|
||||
|
||||
// StorageObject methods
|
||||
virtual std::vector<std::string> GetFileNames() override;
|
||||
virtual std::shared_ptr<StreamBase> GetFile(std::string& fileName) override;
|
||||
virtual void RemoveFile(std::string& fileName) override;
|
||||
virtual std::shared_ptr<StreamBase> OpenFile(std::string& fileName, FileStream::Mode mode) override;
|
||||
virtual void CommitChanges() override;
|
||||
|
||||
protected:
|
||||
std::map<std::string, std::shared_ptr<StreamBase>> m_streams;
|
||||
std::string m_root;
|
||||
|
||||
};//class ZipObject
|
||||
}
|
|
@ -9,7 +9,7 @@ namespace xPlat {
|
|||
class ExceptionBase : public std::exception
|
||||
{
|
||||
public:
|
||||
enum Facility : uint32_t {
|
||||
enum SubFacility : uint32_t {
|
||||
NONE = 0x0000,
|
||||
FILE = 0x1000,
|
||||
ZIP = 0x2000,
|
||||
|
@ -18,19 +18,20 @@ namespace xPlat {
|
|||
};
|
||||
|
||||
ExceptionBase() {}
|
||||
ExceptionBase(Facility facility) : facility(facility) {}
|
||||
ExceptionBase(SubFacility subFacility) : subFacility(subFacility) {}
|
||||
ExceptionBase(uint32_t headerOveride, SubFacility subFacility) : header(headerOveride), subFacility(subFacility) {}
|
||||
|
||||
void SetLastError(uint32_t error)
|
||||
{
|
||||
code = header + facility + (error & Facility::MAX);
|
||||
code = header + subFacility + (error & SubFacility::MAX);
|
||||
}
|
||||
|
||||
uint32_t Code() { return code; }
|
||||
|
||||
protected:
|
||||
Facility facility = Facility::NONE;
|
||||
uint32_t header = 0x8BAD0000; // facility 2989
|
||||
uint32_t code = 0xFFFFFFFF; // by default, something very bad happened...
|
||||
SubFacility subFacility = SubFacility::NONE;
|
||||
uint32_t header = 0x8BAD0000; // SubFacility 2989
|
||||
uint32_t code = 0xFFFFFFFF; // by default, something very bad happened...
|
||||
};
|
||||
|
||||
class NotImplementedException : public ExceptionBase { public: NotImplementedException() { SetLastError(1); } };
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace xPlat {
|
|||
{
|
||||
public:
|
||||
FileException(std::string message, uint32_t error = 0) :
|
||||
ExceptionBase(ExceptionBase::Facility::FILE),
|
||||
ExceptionBase(ExceptionBase::SubFacility::FILE),
|
||||
reason(message)
|
||||
{
|
||||
SetLastError(error);
|
||||
|
@ -58,9 +58,9 @@ namespace xPlat {
|
|||
offset = Ftell();
|
||||
}
|
||||
|
||||
virtual std::size_t Read(std::size_t size, const std::uint8_t* bytes) override
|
||||
virtual std::uint64_t Read(std::uint64_t size, const std::uint8_t* bytes) override
|
||||
{
|
||||
std::size_t bytesRead = std::fread(
|
||||
std::uint64_t bytesRead = std::fread(
|
||||
static_cast<void*>(const_cast<std::uint8_t*>(bytes)), 1, size, file
|
||||
);
|
||||
if (bytesRead < size && !Feof())
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "FileStream.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace xPlat {
|
||||
|
||||
// Interface over a namespace of collected file objects
|
||||
class StorageObject
|
||||
{
|
||||
public:
|
||||
virtual ~StorageObject() {}
|
||||
|
||||
// Obtains a vector of UTF-8 formatted string names contained in the storage object
|
||||
virtual std::vector<std::string> GetFileNames() = 0;
|
||||
|
||||
// Obtains a pointer to a stream representing the file that exists in the storage object
|
||||
virtual std::shared_ptr<StreamBase> GetFile(std::string& fileName) = 0;
|
||||
|
||||
// Remvoes a file by name from the storage object. If the file does not exist, the operation is a no-op
|
||||
virtual void RemoveFile(std::string& fileName) = 0;
|
||||
|
||||
// Opens a stream to a file by name in the storage object. If the file does not exist and mode is read,
|
||||
// or read + update, then nullptr is returned. If the file is opened with write and it does not exist,
|
||||
// then the file is created and an empty stream to the file is handed back to the caller.
|
||||
virtual std::shared_ptr<StreamBase> OpenFile(std::string& fileName, FileStream::Mode mode) = 0;
|
||||
|
||||
// Some storage objects may operate under cache semantics and therefore require an explicit commit.
|
||||
// Clients should explicitly call CommitChanges after all write operations into the object are complete.
|
||||
// An implementation of this interface MAY be a no-op.
|
||||
virtual void CommitChanges() = 0;
|
||||
};
|
||||
}
|
|
@ -14,19 +14,19 @@ namespace xPlat {
|
|||
|
||||
// This way, derived classes only have to implement what they actually need, and everything else is not implemented.
|
||||
virtual void Write(std::size_t size, const std::uint8_t* bytes) { throw NotImplementedException(); }
|
||||
virtual std::size_t Read(std::size_t size, const std::uint8_t* bytes) { throw NotImplementedException(); }
|
||||
virtual std::uint64_t Read(std::uint64_t size, const std::uint8_t* bytes) { throw NotImplementedException(); }
|
||||
virtual void Seek(std::uint64_t offset, Reference where) { throw NotImplementedException(); }
|
||||
virtual int Ferror() { throw NotImplementedException(); }
|
||||
virtual bool Feof() { throw NotImplementedException(); }
|
||||
virtual std::uint64_t Ftell() { throw NotImplementedException(); }
|
||||
|
||||
virtual void CopyTo(StreamBase& to)
|
||||
virtual void CopyTo(StreamBase* to)
|
||||
{
|
||||
std::uint8_t buffer[1024]; // 1k at a time ought to be sufficient
|
||||
std::size_t bytes = Read(sizeof(buffer), buffer);
|
||||
while (bytes != 0)
|
||||
{
|
||||
to.Write(bytes, buffer);
|
||||
to->Write(bytes, buffer);
|
||||
bytes = Read(sizeof(buffer), buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -13,19 +14,28 @@ namespace xPlat {
|
|||
// TODO: define what streams to pass in on the .ctor
|
||||
ZipFileStream(
|
||||
std::string&& fileName,
|
||||
std::uint32_t offset,
|
||||
std::uint32_t offset,
|
||||
std::uint32_t compressedSize,
|
||||
std::uint32_t uncompressedSize,
|
||||
bool isCompressed
|
||||
) :
|
||||
bool isCompressed,
|
||||
StreamBase* stream
|
||||
) :
|
||||
m_fileName(std::move(fileName)),
|
||||
m_offset(offset),
|
||||
m_compressedSize(compressedSize),
|
||||
m_uncompressedSize(uncompressedSize),
|
||||
m_isCompressed(isCompressed)
|
||||
m_isCompressed(isCompressed),
|
||||
m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Write(std::size_t size, const std::uint8_t* bytes) override;
|
||||
virtual std::uint64_t Read(std::uint64_t size, const std::uint8_t* bytes) override;
|
||||
virtual void Seek(std::uint64_t offset, Reference where) override;
|
||||
virtual int Ferror() override;
|
||||
virtual bool Feof() override;
|
||||
virtual std::uint64_t Ftell() override;
|
||||
|
||||
protected:
|
||||
std::string m_fileName;
|
||||
|
||||
|
@ -36,5 +46,6 @@ namespace xPlat {
|
|||
|
||||
bool m_isCompressed = false;
|
||||
std::uint64_t m_relativePosition = 0;
|
||||
StreamBase* m_stream = nullptr;
|
||||
};
|
||||
}
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "ObjectBase.hpp"
|
||||
#include "ZipFileStream.hpp"
|
||||
#include "StorageObject.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
@ -26,7 +25,7 @@ namespace xPlat {
|
|||
InvalidLocalFileHeader = 8,
|
||||
};
|
||||
|
||||
ZipException(std::string message, Error error) : reason(message), ExceptionBase(ExceptionBase::Facility::ZIP)
|
||||
ZipException(std::string message, Error error) : reason(message), ExceptionBase(ExceptionBase::SubFacility::ZIP)
|
||||
{
|
||||
SetLastError(static_cast<std::uint32_t>(error));
|
||||
}
|
||||
|
@ -38,15 +37,20 @@ namespace xPlat {
|
|||
class LocalFileHeader;
|
||||
|
||||
// This represents a raw stream over a.zip file.
|
||||
class ZipObject
|
||||
class ZipObject : public StorageObject
|
||||
{
|
||||
public:
|
||||
ZipObject(StreamBase* stream);
|
||||
|
||||
std::vector<std::string> GetFileNames();
|
||||
// StorageObject methods
|
||||
virtual std::vector<std::string> GetFileNames() override;
|
||||
virtual std::shared_ptr<StreamBase> GetFile(std::string& fileName) override;
|
||||
virtual void RemoveFile(std::string& fileName) override;
|
||||
virtual std::shared_ptr<StreamBase> OpenFile(std::string& fileName, FileStream::Mode mode) override;
|
||||
virtual void CommitChanges() override;
|
||||
|
||||
protected:
|
||||
std::map<std::string, std::shared_ptr<ZipFileStream>> m_streams;
|
||||
std::map<std::string, std::shared_ptr<StreamBase>> m_streams;
|
||||
std::map<std::string, std::shared_ptr<CentralDirectoryFileHeader>> m_centralDirectory;
|
||||
|
||||
// TODO: change to uint64_t when adding full zip64 support
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
// TODO: Implement FTS wrapper
|
|
@ -0,0 +1,236 @@
|
|||
#include "Exceptions.hpp"
|
||||
#include "DirectoryObject.hpp"
|
||||
#include "FileStream.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
// UNICODE MUST be defined before you include Windows.h if you want the non-ascii versions of APIs (and you do)
|
||||
#define UNICODE
|
||||
#include <windows.h>
|
||||
|
||||
namespace xPlat {
|
||||
|
||||
class Win32Exception : public ExceptionBase
|
||||
{
|
||||
public:
|
||||
Win32Exception(DWORD error) : ExceptionBase(0x8007, SubFacility::NONE)
|
||||
{
|
||||
SetLastError(static_cast<std::uint32_t>(error));
|
||||
}
|
||||
};
|
||||
|
||||
std::wstring utf8_to_utf16(std::string& utf8string)
|
||||
{
|
||||
/*
|
||||
from: https://connect.microsoft.com/VisualStudio/feedback/details/1403302/unresolved-external-when-using-codecvt-utf8
|
||||
Posted by Microsoft on 2/16/2016 at 11:49 AM
|
||||
<snip>
|
||||
|
||||
A workaround is to replace 'char32_t' with 'unsigned int'. In VS2013, char32_t was a typedef of 'unsigned int'. In VS2015, char32_t is a distinct type of it's own. Switching your use of 'char32_t' to 'unsigned int' will get you the old behavior from earlier versions and won't trigger a missing export error.
|
||||
|
||||
There is also a similar error to this one with 'char16_t' that can be worked around using 'unsigned short'.
|
||||
*/
|
||||
auto converted = std::wstring_convert<std::codecvt_utf8_utf16<unsigned short>, unsigned short>{}.from_bytes(utf8string.data());
|
||||
std::wstring result(converted.begin(), converted.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string utf16_to_utf8(std::wstring& utf16string)
|
||||
{
|
||||
auto converted = std::wstring_convert<std::codecvt_utf8<wchar_t>>{}.to_bytes(utf16string.data());
|
||||
std::string result(converted.begin(), converted.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
enum class WalkOptions : std::uint16_t
|
||||
{
|
||||
Files = 1, // Enumerate files within the specified directory
|
||||
Directories = 2, // Enumerate directories
|
||||
Recursive = 4 // Enumerate recursively
|
||||
};
|
||||
|
||||
inline constexpr WalkOptions operator &(WalkOptions a, WalkOptions b)
|
||||
{
|
||||
return static_cast<WalkOptions>(static_cast<uint16_t>(a) & static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
inline constexpr WalkOptions operator |(WalkOptions a, WalkOptions b)
|
||||
{
|
||||
return static_cast<WalkOptions>(static_cast<uint16_t>(a) | static_cast<uint16_t>(b));
|
||||
}
|
||||
|
||||
template <WalkOptions options, class Lambda>
|
||||
void WalkDirectory(std::string& root, Lambda& visitor)
|
||||
{
|
||||
static std::string dot(".");
|
||||
static std::string dotdot("..");
|
||||
|
||||
std::wstring utf16Name = utf8_to_utf16(root);
|
||||
|
||||
WIN32_FIND_DATA findFileData = {};
|
||||
std::unique_ptr<std::remove_pointer<HANDLE>::type, decltype(&::FindClose)> find(
|
||||
FindFirstFile(reinterpret_cast<LPCWSTR>(utf16Name.c_str()), &findFileData),
|
||||
&FindClose);
|
||||
|
||||
if (INVALID_HANDLE_VALUE == find.get())
|
||||
{
|
||||
DWORD lastError = GetLastError();
|
||||
if (lastError == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
return;
|
||||
}
|
||||
throw Win32Exception(lastError);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
utf16Name = std::wstring(findFileData.cFileName);
|
||||
auto utf8Name = utf16_to_utf8(utf16Name);
|
||||
|
||||
if (((options & WalkOptions::Directories) == WalkOptions::Directories) &&
|
||||
(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
)
|
||||
{
|
||||
std::string child = (root + "\\" + utf8Name);
|
||||
if (!visitor(root, WalkOptions::Directories, std::move(utf8Name)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ((options & WalkOptions::Recursive) == WalkOptions::Recursive)
|
||||
{
|
||||
WalkDirectory<options>(child, visitor);
|
||||
}
|
||||
}
|
||||
else if ((options & WalkOptions::Files) == WalkOptions::Files)
|
||||
{
|
||||
if (dot != utf8Name && dotdot != utf8Name)
|
||||
{
|
||||
if (!visitor(root, WalkOptions::Files, std::move(utf8Name)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (FindNextFile(find.get(), &findFileData));
|
||||
|
||||
DWORD lastError = GetLastError();
|
||||
if ((lastError != ERROR_NO_MORE_FILES) && (lastError != ERROR_SUCCESS))
|
||||
{
|
||||
throw Win32Exception(lastError);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> DirectoryObject::GetFileNames()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
WalkDirectory<WalkOptions::Files | WalkOptions::Directories | WalkOptions::Recursive>(m_root, [&](
|
||||
std::string,
|
||||
WalkOptions option,
|
||||
std::string&& name)
|
||||
{
|
||||
if (option == WalkOptions::Files)
|
||||
{
|
||||
result.push_back(std::move(name));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<StreamBase> DirectoryObject::GetFile(std::string& fileName)
|
||||
{
|
||||
// first check cache
|
||||
auto index = m_streams.find(fileName);
|
||||
if (index != m_streams.cend())
|
||||
{
|
||||
return index->second;
|
||||
}
|
||||
// create stream and populate cache
|
||||
std::string path = m_root + "\\" + fileName;
|
||||
auto result = m_streams[fileName] = std::make_unique<FileStream>(std::move(fileName), FileStream::Mode::READ);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DirectoryObject::RemoveFile(std::string& fileName)
|
||||
{
|
||||
throw NotImplementedException();
|
||||
}
|
||||
|
||||
std::shared_ptr<StreamBase> DirectoryObject::OpenFile(std::string& fileName, FileStream::Mode mode)
|
||||
{
|
||||
static const std::string slash("\\");
|
||||
|
||||
std::vector<std::string> directories;
|
||||
auto PopFirst = [&directories]()
|
||||
{
|
||||
auto result = directories.at(0);
|
||||
std::vector<std::string>(directories.begin() + 1, directories.end()).swap(directories);
|
||||
return result;
|
||||
};
|
||||
auto PopBack = [&directories]()
|
||||
{
|
||||
auto result = directories.at(directories.size() - 1);
|
||||
directories.pop_back();
|
||||
return result;
|
||||
};
|
||||
|
||||
// Build a list of directory names to ensure exist
|
||||
std::istringstream stream(fileName);
|
||||
std::string directory;
|
||||
while (getline(stream, directory, '/'))
|
||||
{
|
||||
directories.push_back(std::move(directory));
|
||||
}
|
||||
auto name = PopBack(); // remove the actual file name from the list of directories, but keep just the name
|
||||
|
||||
// Enforce that directory structure exists before creating file at specified location.
|
||||
bool found = false;
|
||||
std::string path = m_root;
|
||||
while (directories.size() != 0)
|
||||
{
|
||||
WalkDirectory<WalkOptions::Directories>(path + slash + directories.front(), [&](
|
||||
std::string,
|
||||
WalkOptions option,
|
||||
std::string&& name)
|
||||
{
|
||||
found = false;
|
||||
if (directories.front() == name)
|
||||
{
|
||||
found = true;
|
||||
path = path + slash + PopFirst();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!found)
|
||||
{
|
||||
std::wstring utf16Name = utf8_to_utf16(path + slash + directories.front());
|
||||
if (!CreateDirectory(utf16Name.c_str(), nullptr))
|
||||
{
|
||||
throw Win32Exception(GetLastError());
|
||||
}
|
||||
path = path + slash + PopFirst();
|
||||
}
|
||||
}
|
||||
name = path + slash + name;
|
||||
auto result = m_streams[fileName] = std::make_unique<FileStream>(std::move(name), mode);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DirectoryObject::CommitChanges()
|
||||
{
|
||||
m_streams.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't pollute other compilation units with any of our #defs...
|
||||
#undef UNICODE
|
|
@ -0,0 +1,58 @@
|
|||
#include "ZipFileStream.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
namespace xPlat {
|
||||
|
||||
void ZipFileStream::Write(std::size_t size, const std::uint8_t* bytes)
|
||||
{
|
||||
throw NotImplementedException();
|
||||
}
|
||||
|
||||
std::uint64_t ZipFileStream::Read(std::uint64_t size, const std::uint8_t* bytes)
|
||||
{
|
||||
m_stream->Seek(m_offset + m_relativePosition, StreamBase::Reference::START);
|
||||
|
||||
std::uint64_t amountToRead = std::min(size, (m_compressedSize - m_relativePosition));
|
||||
std::uint64_t bytesRead = m_stream->Read(amountToRead, bytes);
|
||||
m_relativePosition += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
void ZipFileStream::Seek(std::uint64_t offset, Reference where)
|
||||
{
|
||||
std::uint64_t newPos = 0;
|
||||
switch (where)
|
||||
{
|
||||
case Reference::CURRENT:
|
||||
newPos = m_offset + m_relativePosition + offset;
|
||||
break;
|
||||
case Reference::START:
|
||||
newPos = m_offset + offset;
|
||||
break;
|
||||
case Reference::END:
|
||||
newPos = m_offset + m_compressedSize + offset;
|
||||
break;
|
||||
}
|
||||
|
||||
m_stream->Seek(newPos, Reference::START);
|
||||
m_relativePosition = m_stream->Ftell() - m_offset;
|
||||
}
|
||||
|
||||
int ZipFileStream::Ferror()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ZipFileStream::Feof()
|
||||
{
|
||||
return m_relativePosition >= m_compressedSize;
|
||||
}
|
||||
|
||||
std::uint64_t ZipFileStream::Ftell()
|
||||
{
|
||||
return m_relativePosition;
|
||||
}
|
||||
|
||||
} /* xPlat */
|
|
@ -2,6 +2,7 @@
|
|||
#include "StreamBase.hpp"
|
||||
#include "ObjectBase.hpp"
|
||||
#include "ZipObject.hpp"
|
||||
#include "ZipFileStream.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -754,6 +755,36 @@ namespace xPlat {
|
|||
|
||||
};//class EndOfCentralDirectoryRecord
|
||||
|
||||
std::vector<std::string> ZipObject::GetFileNames()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::for_each(m_streams.begin(), m_streams.end(), [&](auto it)
|
||||
{
|
||||
result.push_back(it.first);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<StreamBase> ZipObject::GetFile(std::string& fileName)
|
||||
{
|
||||
return m_streams[fileName];
|
||||
}
|
||||
|
||||
void ZipObject::RemoveFile(std::string& fileName)
|
||||
{
|
||||
throw NotImplementedException();
|
||||
}
|
||||
|
||||
std::shared_ptr<StreamBase> ZipObject::OpenFile(std::string& fileName, FileStream::Mode mode)
|
||||
{
|
||||
throw NotImplementedException();
|
||||
}
|
||||
|
||||
void ZipObject::CommitChanges()
|
||||
{
|
||||
throw NotImplementedException();
|
||||
}
|
||||
|
||||
ZipObject::ZipObject(StreamBase* stream)
|
||||
{
|
||||
// Confirm that the file IS the correct format
|
||||
|
@ -801,23 +832,13 @@ namespace xPlat {
|
|||
centralFileHeader.second->GetRelativeOffsetOfLocalHeader() + localFileHeader->Size(),
|
||||
localFileHeader->GetCompressedSize(),
|
||||
localFileHeader->GetUncompressedSize(),
|
||||
localFileHeader->GetCompressionType() == CompressionType::Deflate
|
||||
localFileHeader->GetCompressionType() == CompressionType::Deflate,
|
||||
stream
|
||||
);
|
||||
|
||||
m_streams.insert(std::make_pair(
|
||||
centralFileHeader.second->GetFileName(),
|
||||
zipFileStream));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ZipObject::GetFileNames()
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::for_each(m_streams.begin(), m_streams.end(), [&](auto it)
|
||||
{
|
||||
result.push_back(it.first);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
} // ZipObject::ZipObject
|
||||
} // namespace xPlat
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "StreamBase.hpp"
|
||||
#include "FileStream.hpp"
|
||||
#include "ZipObject.hpp"
|
||||
#include "DirectoryObject.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
@ -49,21 +50,53 @@ unsigned int ResultOf(char* source, char* destination, Lambda lambda)
|
|||
return result;
|
||||
}
|
||||
|
||||
XPLATAPPX_API unsigned int UnpackAppx(char* source, char* destination)
|
||||
XPLATAPPX_API unsigned int UnpackAppx(char* from, char* to)
|
||||
{
|
||||
return ResultOf(source, destination, [&]() {
|
||||
std::string appxFileName(source);
|
||||
auto rawFile = std::make_unique<xPlat::FileStream>(
|
||||
std::move(appxFileName),
|
||||
xPlat::FileStream::Mode::READ);
|
||||
return ResultOf(from, to, [&]() {
|
||||
std::string source(from);
|
||||
std::string target(to);
|
||||
|
||||
xPlat::ZipObject zip(rawFile.get());
|
||||
xPlat::DirectoryObject directory(std::move(target));
|
||||
|
||||
auto rawFile = std::make_unique<xPlat::FileStream>(std::move(source), xPlat::FileStream::Mode::READ);
|
||||
|
||||
{
|
||||
xPlat::ZipObject zip(rawFile.get());
|
||||
|
||||
auto fileNames = zip.GetFileNames();
|
||||
for (auto fileName : fileNames)
|
||||
{
|
||||
auto sourceFile = zip.GetFile(fileName);
|
||||
auto targetFile = directory.OpenFile(fileName, xPlat::FileStream::Mode::WRITE);
|
||||
|
||||
sourceFile->CopyTo(targetFile.get());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
XPLATAPPX_API unsigned int PackAppx (char* source, char* destination)
|
||||
XPLATAPPX_API unsigned int PackAppx (char* from, char* to)
|
||||
{
|
||||
return ResultOf(source, destination, []() {
|
||||
// TODO: implement here
|
||||
return ResultOf(from, to, [&]() {
|
||||
std::string source(from);
|
||||
std::string target(to);
|
||||
|
||||
xPlat::DirectoryObject directory(std::move(source));
|
||||
|
||||
auto rawFile = std::make_unique<xPlat::FileStream>(std::move(target), xPlat::FileStream::Mode::WRITE);
|
||||
|
||||
{
|
||||
xPlat::ZipObject zip(rawFile.get());
|
||||
|
||||
auto fileNames = directory.GetFileNames();
|
||||
for (auto fileName : fileNames)
|
||||
{
|
||||
auto sourceFile = directory.GetFile(fileName);
|
||||
auto targetFile = zip.OpenFile(fileName, xPlat::FileStream::Mode::WRITE);
|
||||
|
||||
sourceFile->CopyTo(targetFile.get());
|
||||
}
|
||||
zip.CommitChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче