Merge remote-tracking branch 'origin/feature' into helpphil
This commit is contained in:
Коммит
45140194a5
|
@ -0,0 +1,8 @@
|
|||
REM HUZZA FOR: https://dmerej.info/blog/post/cmake-visual-studio-and-the-command-line/
|
||||
if not exist .vs md .vs
|
||||
cd .vs
|
||||
if exist CMakeFiles rd /s /q CMakeFiles
|
||||
if exist CMakeCache.txt del CMakeCache.txt
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
cmake -DWIN32=on -DCMAKE_BUILD_TYPE=Release -G"NMake Makefiles" ..
|
||||
nmake
|
|
@ -3,6 +3,10 @@
|
|||
#include <string>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#ifdef WIN32
|
||||
#define UNICODE
|
||||
|
@ -68,6 +72,98 @@ std::wstring utf8_to_utf16(const std::string& utf8string)
|
|||
return result;
|
||||
}
|
||||
|
||||
// describes an option to a command that the user may specify
|
||||
struct Option
|
||||
{
|
||||
using CBF = std::function<bool(const std::string& value)>;
|
||||
|
||||
Option(bool param, const std::string& help, CBF callback): Help(help), Callback(callback), TakesParameter(param)
|
||||
{}
|
||||
|
||||
bool TakesParameter;
|
||||
std::string Name;
|
||||
std::string Help;
|
||||
CBF Callback;
|
||||
};
|
||||
|
||||
// Tracks the state of the current parse operation as well as implements input validation
|
||||
struct State
|
||||
{
|
||||
bool CreatePackageSubfolder()
|
||||
{
|
||||
unpackOptions = static_cast<APPX_PACKUNPACK_OPTION>(unpackOptions | APPX_PACKUNPACK_OPTION::APPX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipManifestValidation()
|
||||
{
|
||||
validationOptions = static_cast<APPX_VALIDATION_OPTION>(validationOptions | APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_SKIPAPPXMANIFEST);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkipSignature()
|
||||
{
|
||||
validationOptions = static_cast<APPX_VALIDATION_OPTION>(validationOptions | APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_SKIPSIGNATURE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AllowSignatureOriginUnknown()
|
||||
{
|
||||
validationOptions = static_cast<APPX_VALIDATION_OPTION>(validationOptions | APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetPackageName(const std::string& name)
|
||||
{
|
||||
if (!packageName.empty() || name.empty()) { return false; }
|
||||
packageName = utf8_to_utf16(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetDirectoryName(const std::string& name)
|
||||
{
|
||||
if (!directoryName.empty() || name.empty()) { return false; }
|
||||
directoryName = utf8_to_utf16(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring packageName;
|
||||
std::wstring directoryName;
|
||||
APPX_VALIDATION_OPTION validationOptions = APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_FULL;
|
||||
APPX_PACKUNPACK_OPTION unpackOptions = APPX_PACKUNPACK_OPTION::APPX_PACKUNPACK_OPTION_NONE;
|
||||
};
|
||||
|
||||
// Displays contextual formatted help to the user.
|
||||
int Help(char* toolName, std::map<std::string, Option>& options)
|
||||
{
|
||||
std::cout << std::endl;
|
||||
std::cout << "Usage:" << std::endl;
|
||||
std::cout << "------" << std::endl;
|
||||
std::cout << "\t" << toolName << " -p <package> -d <directory> [options] " << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Description:" << std::endl;
|
||||
std::cout << "------------" << std::endl;
|
||||
std::cout << "\tExtracts all files within an app package at the input <package> name to the" << std::endl;
|
||||
std::cout << "\tspecified output <directory>. The output has the same directory structure " << std::endl;
|
||||
std::cout << "\tas the package." << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Options:" << std::endl;
|
||||
std::cout << "--------" << std::endl;
|
||||
|
||||
for (const auto& option : options)
|
||||
{
|
||||
std::cout << "\t" << std::left << std::setfill(' ') << std::setw(5) <<
|
||||
option.first << ": " << option.second.Help << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// error text if the user provided underspecified input
|
||||
void Error(char* toolName)
|
||||
{
|
||||
std::cout << toolName << ": error : Missing required options. Use '-?' for more details." << std::endl;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// TODO: paths coming in SHOULD have platform-appropriate path separators
|
||||
void replace(std::wstring& input, const wchar_t oldchar, const wchar_t newchar)
|
||||
|
@ -147,7 +243,7 @@ struct FootprintFilesType
|
|||
|
||||
// Types of footprint files in an app package
|
||||
const int FootprintFilesCount = 4;
|
||||
const FootprintFilesType footprintFilesType[FootprintFilesCount] = {
|
||||
FootprintFilesType footprintFilesType[FootprintFilesCount] = {
|
||||
{APPX_FOOTPRINT_FILE_TYPE_MANIFEST, "manifest", true },
|
||||
{APPX_FOOTPRINT_FILE_TYPE_BLOCKMAP, "block map", true },
|
||||
{APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, "digital signature", true },
|
||||
|
@ -198,13 +294,13 @@ void STDMETHODCALLTYPE MyFree(LPVOID pv) { std::free(pv); }
|
|||
// reader
|
||||
// On success, receives the created instance of IAppxPackageReader.
|
||||
//
|
||||
HRESULT GetPackageReader(LPCWSTR inputFileName, IAppxPackageReader** package)
|
||||
HRESULT GetPackageReader(State& state, IAppxPackageReader** package)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
ComPtr<IAppxFactory> appxFactory;
|
||||
ComPtr<IStream> inputStream;
|
||||
|
||||
hr = CreateStreamOnFileUTF16(inputFileName, true, &inputStream);
|
||||
hr = CreateStreamOnFileUTF16(state.packageName.c_str(), true, &inputStream);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// On Win32 platforms CoCreateAppxFactory defaults to CoTaskMemAlloc/CoTaskMemFree
|
||||
|
@ -213,7 +309,7 @@ HRESULT GetPackageReader(LPCWSTR inputFileName, IAppxPackageReader** package)
|
|||
hr = CoCreateAppxFactoryWithHeap(
|
||||
MyAllocate,
|
||||
MyFree,
|
||||
APPX_VALIDATION_OPTION::APPX_VALIDATION_OPTION_SKIPAPPXMANIFEST,
|
||||
state.validationOptions,
|
||||
&appxFactory);
|
||||
|
||||
// Create a new package reader using the factory. For
|
||||
|
@ -361,33 +457,88 @@ HRESULT ExtractPayloadFiles(IAppxPackageReader* package, LPCWSTR outputPath)
|
|||
return hr;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
// Parses argc/argv input via commands into state, and extract the package.
|
||||
int ParseAndRun(std::map<std::string, Option>& options, State& state, int argc, char* argv[])
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
auto ParseInput = [&]()->bool {
|
||||
int index = 1;
|
||||
while (index < argc)
|
||||
{
|
||||
auto option = options.find(argv[index]);
|
||||
if (option == options.end()) { return false; }
|
||||
char const *parameter = "";
|
||||
if (option->second.TakesParameter)
|
||||
{
|
||||
if (++index == argc) { break; }
|
||||
parameter = argv[index];
|
||||
}
|
||||
if (!option->second.Callback(parameter)) { return false; }
|
||||
++index;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
std::wstring fileName = utf8_to_utf16(argv[1]);
|
||||
std::wstring pathName = utf8_to_utf16(argv[2]);
|
||||
if (!ParseInput()) { return Help(argv[0], options); }
|
||||
if (state.packageName.empty() || state.directoryName.empty())
|
||||
{ Error(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
// Create a package using the file name in argv[1]
|
||||
ComPtr<IAppxPackageReader> package;
|
||||
hr = GetPackageReader(fileName.c_str(), &package);
|
||||
hr = GetPackageReader(state, &package);
|
||||
|
||||
// Print information about all footprint files, and extract them to disk
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ExtractFootprintFiles(package.Get(), pathName.c_str());
|
||||
hr = ExtractFootprintFiles(package.Get(), state.directoryName.c_str());
|
||||
}
|
||||
|
||||
// Print information about all payload files, and extract them to disk
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ExtractPayloadFiles(package.Get(), pathName.c_str());
|
||||
hr = ExtractPayloadFiles(package.Get(), state.directoryName.c_str());
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// TODO: Tell a more specific reason why the faiulre occurred.
|
||||
std::printf("\nError %X occurred while extracting the appx package\n", static_cast<int>(hr));
|
||||
}
|
||||
|
||||
return static_cast<int>(hr);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
State state;
|
||||
std::map<std::string, Option> options = {
|
||||
{ "-p", Option(true, "REQUIRED, specify input package name.",
|
||||
[&](const std::string& name) { return state.SetPackageName(name); })
|
||||
},
|
||||
{ "-d", Option(true, "REQUIRED, specify output directory name.",
|
||||
[&](const std::string& name) { return state.SetDirectoryName(name); })
|
||||
},
|
||||
{ "-pfn", Option(false, "Unpacks all files to a subdirectory under the specified output path, named after the package full name.",
|
||||
[&](const std::string&) { return state.CreatePackageSubfolder(); })
|
||||
},
|
||||
{ "-mv", Option(false, "Skips manifest validation. By default manifest validation is enabled.",
|
||||
[&](const std::string&) { return state.SkipManifestValidation(); })
|
||||
},
|
||||
{ "-sv", Option(false, "Skips signature validation. By default signature validation is enabled.",
|
||||
[&](const std::string&) { return state.AllowSignatureOriginUnknown(); })
|
||||
},
|
||||
{ "-ss", Option(false, "Skips enforcement of signed packages. By default packages must be signed.",
|
||||
[&](const std::string&)
|
||||
{ footprintFilesType[2].isRequired = false;
|
||||
return state.SkipSignature();
|
||||
})
|
||||
},
|
||||
{ "-?", Option(false, "Displays this help text.",
|
||||
[&](const std::string&) { return false; })
|
||||
}
|
||||
};
|
||||
|
||||
auto result = ParseAndRun(options, state, argc, argv);
|
||||
if (result != 0)
|
||||
{
|
||||
std::cout << "Error: " << std::hex << result << " while extracting the appx package" <<std::endl;
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
#include "HashStream.hpp"
|
||||
#include "ComHelper.hpp"
|
||||
#include "SHA256.hpp"
|
||||
#include "AppxFactory.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
@ -35,7 +36,8 @@ namespace xPlat {
|
|||
class BlockMapStream : public StreamBase
|
||||
{
|
||||
public:
|
||||
BlockMapStream(IStream* stream, std::vector<Block>& blocks)
|
||||
BlockMapStream(IxPlatFactory* factory, std::string decodedName, IStream* stream, std::vector<Block>& blocks)
|
||||
: m_factory(factory), m_decodedName(decodedName), m_stream(stream)
|
||||
{
|
||||
// Determine overall stream size
|
||||
ULARGE_INTEGER uli;
|
||||
|
@ -133,11 +135,34 @@ namespace xPlat {
|
|||
if (actualRead) { *actualRead = bytesRead; }
|
||||
return (countBytes == bytesRead) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetCompressionOption(APPX_COMPRESSION_OPTION* compressionOption) override
|
||||
{
|
||||
return ResultOf([&]{ return m_stream.As<IAppxFile>()->GetCompressionOption(compressionOption); });
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetName(LPWSTR* fileName) override
|
||||
{
|
||||
return m_factory->MarshalOutString(m_decodedName, fileName);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetContentType(LPWSTR* contentType) override
|
||||
{
|
||||
return ResultOf([&]{ return m_stream.As<IAppxFile>()->GetContentType(contentType); });
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetSize(UINT64* size) override
|
||||
{
|
||||
return ResultOf([&]{ if (size) { *size = m_streamSize; }});
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<BlockPlusStream>::iterator m_currentBlock;
|
||||
std::vector<BlockPlusStream> m_blockStreams;
|
||||
std::uint64_t m_relativePosition;
|
||||
std::uint64_t m_streamSize;
|
||||
std::string m_decodedName;
|
||||
ComPtr<IStream> m_stream;
|
||||
IxPlatFactory* m_factory;
|
||||
};
|
||||
}
|
|
@ -121,5 +121,25 @@ namespace xPlat {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetCompressionOption(APPX_COMPRESSION_OPTION* compressionOption) override
|
||||
{
|
||||
return ResultOf([&]{ return m_stream.As<IAppxFile>()->GetCompressionOption(compressionOption); });
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetName(LPWSTR* fileName) override
|
||||
{
|
||||
return ResultOf([&]{ return m_stream.As<IAppxFile>()->GetName(fileName); });
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetContentType(LPWSTR* contentType) override
|
||||
{
|
||||
return ResultOf([&]{ return m_stream.As<IAppxFile>()->GetContentType(contentType); });
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetSize(UINT64* size) override
|
||||
{
|
||||
return ResultOf([&]{ if (size) { *size = m_streamSize; }});
|
||||
}
|
||||
};
|
||||
}
|
|
@ -9,12 +9,12 @@
|
|||
|
||||
|
||||
namespace xPlat {
|
||||
|
||||
|
||||
// This represents a subset of a Stream
|
||||
class RangeStream : public StreamBase
|
||||
{
|
||||
public:
|
||||
RangeStream(std::uint64_t offset, std::uint64_t size, IStream* stream) :
|
||||
RangeStream(std::uint64_t offset, std::uint64_t size, IStream* stream) :
|
||||
m_offset(offset),
|
||||
m_size(size),
|
||||
m_stream(stream)
|
||||
|
@ -40,7 +40,7 @@ namespace xPlat {
|
|||
//TODO: We need to constrain newPos so that it can't exceed the end of the stream
|
||||
ULARGE_INTEGER pos = { 0 };
|
||||
m_stream->Seek(newPos, Reference::START, &pos);
|
||||
m_relativePosition = std::min((pos.QuadPart - m_offset), m_size);
|
||||
m_relativePosition = std::min(static_cast<std::uint64_t>(pos.QuadPart - m_offset), m_size);
|
||||
if (newPosition) { newPosition->QuadPart = m_relativePosition; }
|
||||
});
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace xPlat {
|
|||
bool hasValue = !attributeValue.empty();
|
||||
std::uint32_t value = 0;
|
||||
if (hasValue) { value = static_cast<std::uint32_t>(std::stoul(attributeValue)); }
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string GetName(XERCES_CPP_NAMESPACE::DOMElement* element)
|
||||
|
@ -67,7 +67,7 @@ namespace xPlat {
|
|||
XercesXMLChPtr nameAttr(XMLString::transcode("Hash"));
|
||||
XMLSize_t len = 0;
|
||||
XercesXMLBytePtr decodedData(XERCES_CPP_NAMESPACE::Base64::decodeToXMLByte(
|
||||
element->getAttribute(nameAttr.Get()),
|
||||
element->getAttribute(nameAttr.Get()),
|
||||
&len));
|
||||
std::vector<std::uint8_t> result(len);
|
||||
for(XMLSize_t index=0; index < len; index++)
|
||||
|
@ -83,7 +83,7 @@ namespace xPlat {
|
|||
result.hash = GetDigestData(element);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AppxBlockMapObject::AppxBlockMapObject(IxPlatFactory* factory, ComPtr<IStream>& stream) : m_factory(factory), m_stream(stream)
|
||||
{
|
||||
auto dom = ComPtr<IXmlObject>::Make<XmlObject>(stream, &blockMapSchema);
|
||||
|
@ -103,8 +103,13 @@ namespace xPlat {
|
|||
fileResult->snapshotItem(i);
|
||||
auto fileNode = static_cast<DOMElement*>(fileResult->getNodeValue());
|
||||
|
||||
auto name = GetName(fileNode);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (name == "[Content_Types].xml"), "[Content_Types].xml cannot be in the AppxBlockMap.xml file");
|
||||
auto existing = m_blockMap.find(name);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (existing != m_blockMap.end()), "duplicate file name specified.");
|
||||
|
||||
// Get blocks elements
|
||||
XercesXMLChPtr blockXPath(XMLString::transcode("./Block"));
|
||||
XercesXMLChPtr blockXPath(XMLString::transcode("./Block"));
|
||||
XercesPtr<DOMXPathResult> blockResult = dom->Document()->evaluate(
|
||||
blockXPath.Get(),
|
||||
fileNode,
|
||||
|
@ -113,19 +118,15 @@ namespace xPlat {
|
|||
nullptr);
|
||||
|
||||
// get all the blocks for the file.
|
||||
std::vector<Block> blocks(blockResult->getSnapshotLength());
|
||||
std::vector<Block> blocks(blockResult->getSnapshotLength());
|
||||
for (XMLSize_t j = 0; j < blockResult->getSnapshotLength(); j++)
|
||||
{
|
||||
blockResult->snapshotItem(j);
|
||||
auto blockNode = static_cast<DOMElement*>(blockResult->getNodeValue());
|
||||
blocks[j] = GetBlock(blockNode);
|
||||
}
|
||||
}
|
||||
|
||||
auto name = GetName(fileNode);
|
||||
auto existing = m_blockMap.find(name);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, (existing != m_blockMap.end()), "duplicate file name specified.");
|
||||
m_blockMap.insert(std::make_pair(name, std::move(blocks)));
|
||||
|
||||
m_blockMapfiles.insert(std::make_pair(name,
|
||||
ComPtr<IAppxBlockMapFile>::Make<AppxBlockMapFile>(
|
||||
factory,
|
||||
|
@ -133,7 +134,7 @@ namespace xPlat {
|
|||
GetLocalFileHeaderSize(fileNode),
|
||||
name,
|
||||
GetSize(fileNode))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xPlat::ComPtr<IStream> AppxBlockMapObject::GetValidationStream(const std::string& part, IStream* stream)
|
||||
|
@ -141,7 +142,7 @@ namespace xPlat {
|
|||
ThrowErrorIf(Error::InvalidParameter, (part.empty() || stream == nullptr), "bad input");
|
||||
auto item = m_blockMap.find(part);
|
||||
ThrowErrorIf(Error::BlockMapSemanticError, item == m_blockMap.end(), "file not tracked by blockmap");
|
||||
return ComPtr<IStream>::Make<BlockMapStream>(stream, item->second);
|
||||
return ComPtr<IStream>::Make<BlockMapStream>(m_factory, part, stream, item->second);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AppxBlockMapObject::GetFile(LPCWSTR filename, IAppxBlockMapFile **file)
|
||||
|
@ -162,7 +163,7 @@ namespace xPlat {
|
|||
ComPtr<IAppxBlockMapReader> self;
|
||||
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IAppxBlockMapReader>::iid, reinterpret_cast<void**>(&self)));
|
||||
*enumerator = ComPtr<IAppxBlockMapFilesEnumerator>::Make<AppxBlockMapFilesEnumerator>(
|
||||
self.Get(),
|
||||
self.Get(),
|
||||
std::move(GetFileNames(FileNameOptions::All))).Detach();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ namespace xPlat {
|
|||
};
|
||||
|
||||
static const std::uint8_t PercentangeEncodingTableSize = 0x5E;
|
||||
static const std::vector<std::string> PercentangeEncoding = {
|
||||
"", "", "", "", "", "", "", "",
|
||||
static const std::vector<std::string> PercentangeEncoding =
|
||||
{ "", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
|
@ -47,8 +47,7 @@ namespace xPlat {
|
|||
};
|
||||
|
||||
static const std::map<std::string, char> EncodingToChar =
|
||||
{
|
||||
{"20", ' '}, {"21", '!'}, {"23", '#'}, {"24", '$'},
|
||||
{ {"20", ' '}, {"21", '!'}, {"23", '#'}, {"24", '$'},
|
||||
{"25", '%'}, {"26", '&'}, {"27", '\''}, {"28", '('},
|
||||
{"29", ')'}, {"25", '+'}, {"2B", '%'}, {"2C", ','},
|
||||
{"3B", ';'}, {"3D", '='}, {"40", '@'}, {"5B", '['},
|
||||
|
@ -59,19 +58,15 @@ namespace xPlat {
|
|||
{
|
||||
std::string result;
|
||||
for (std::uint32_t position = 0; position < fileName.length(); ++position)
|
||||
{
|
||||
std::uint8_t index = static_cast<std::uint8_t>(fileName[position]);
|
||||
{ std::uint8_t index = static_cast<std::uint8_t>(fileName[position]);
|
||||
if(fileName[position] < PercentangeEncodingTableSize && index < PercentangeEncoding.size() && !PercentangeEncoding[index].empty())
|
||||
{
|
||||
result += PercentangeEncoding[index];
|
||||
{ result += PercentangeEncoding[index];
|
||||
}
|
||||
else if (fileName[position] == '\\') // Remove Windows file name
|
||||
{
|
||||
result += '/';
|
||||
else if (fileName[position] == '\\') // Remove Windows file separator.
|
||||
{ result += '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
result += fileName[position];
|
||||
{ result += fileName[position];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -81,27 +76,18 @@ namespace xPlat {
|
|||
{
|
||||
std::string result;
|
||||
for (std::uint32_t i = 0; i < fileName.length(); ++i)
|
||||
{
|
||||
if(fileName[i] == '%')
|
||||
{
|
||||
auto found = EncodingToChar.find(fileName.substr(i+1, 2));
|
||||
{ if(fileName[i] == '%')
|
||||
{ auto found = EncodingToChar.find(fileName.substr(i+1, 2));
|
||||
if (found != EncodingToChar.end())
|
||||
{
|
||||
result += found->second;
|
||||
{ result += found->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(Error::AppxUnknownFileNameEncoding, fileName);
|
||||
{ throw Exception(Error::AppxUnknownFileNameEncoding, fileName);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
else if (fileName[i] == '/') // Windows file name
|
||||
{
|
||||
result += '\\';
|
||||
}
|
||||
else
|
||||
{
|
||||
result += fileName[i];
|
||||
{ result += fileName[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -210,13 +196,11 @@ namespace xPlat {
|
|||
{
|
||||
std::string targetName;
|
||||
if (options & APPX_PACKUNPACK_OPTION_CREATEPACKAGESUBFOLDER)
|
||||
{
|
||||
throw Exception(Error::NotImplemented);
|
||||
{ throw Exception(Error::NotImplemented);
|
||||
//targetName = GetAppxManifest()->GetPackageFullName() + to->GetPathSeparator() + fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetName = fileName;
|
||||
{ targetName = DecodeFileName(fileName);
|
||||
}
|
||||
|
||||
auto targetFile = to->OpenFile(targetName, xPlat::FileStream::Mode::WRITE_UPDATE);
|
||||
|
|
Двоичные данные
test/appx/BlockMap/TODAVIANO/Signature_in_BlockMap.appx
Двоичные данные
test/appx/BlockMap/TODAVIANO/Signature_in_BlockMap.appx
Двоичный файл не отображается.
Двоичный файл не отображается.
Загрузка…
Ссылка в новой задаче