Merge remote-tracking branch 'origin/feature' into helpphil

This commit is contained in:
Phil Smith 2017-12-06 15:19:50 -08:00
Родитель 821d5ff985 a4c27c4162
Коммит 45140194a5
9 изменённых файлов: 255 добавлений и 66 удалений

8
makewin32x64.cmd Normal file
Просмотреть файл

@ -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);

Двоичный файл не отображается.

Двоичный файл не отображается.