Implement flat bundle creation (#389)
* Attempt to create bundle method * ProcessCommonOptions method * Pass flags * Update * Create appxbundlefactory and bundlewriter * Added interfaces to implement * edit bundle options * Remove comments * Create vector of packages to write to bundle manifest * Create bundle manifest bundle element * Write Bundle Manifest Elements * Add AppxBundleManifest, BlockMap, contenttype.xml to zip * Validations to store fields into manifest * Write Resources and Dependencies to Bundle Manifest * Fetches correct value for resources and tdfs * Created AppxBundleWriter and BundleWriterHelper files * Writer verion to manifest, address PR comments * process common and input options * Use stdstrin instead of wchar * Address PR comments * Removed closeinternal from AppxBundleWriter.cpp * Use exceptions instead of hresult * Implement AppxManifestReader->GetQualifiedResources * Write resources to bundle manifest * Write correct namespaces to manifest file * Code review comments * Resolve build on other platforms * Add packbundle to if MSIX_PACK * Build should pass now * Remove ectra qualification for build to pass * add to msix_pack * STATFLAG_NONAME error * Resolve compiler error * UINT64 and std::uint64 compiler error * Use std::time instead of systemtime * Build break * resolve UINT64 ios compiler error * Comment out arm64 mac jobs- known config issue * Mac pipeline failure * Remove Text data structure as per PR feedback * gitignore, move zlib file, update filename to versionhelpers * Resolve build * Remove thumbs.db file
This commit is contained in:
Родитель
96f2ffc9ac
Коммит
eb2a53ca25
|
@ -406,3 +406,5 @@ src/msix/common/MSIXResource.cpp
|
|||
src/test/MacOS-Linux/testApiResults.txt
|
||||
src/test/mobile/iOSBVT/iOSBVT.xcodeproj/project.xcworkspace/
|
||||
src/test/mobile/TEST-MsixSDK-AOSP.xml
|
||||
|
||||
Thumbs.db
|
||||
|
|
|
@ -50,21 +50,21 @@ jobs:
|
|||
_arguments: -b Debug --pack
|
||||
_artifact: MACOSchk-pack
|
||||
# arm64
|
||||
debug_nopack_arm64:
|
||||
_arguments: -b Debug -arch arm64 --skip-tests
|
||||
_artifact: MACOSarm64chk
|
||||
release_nopack_arm64:
|
||||
_arguments: -b MinSizeRel -arch arm64 --skip-tests
|
||||
_artifact: MACOSarm64
|
||||
release_nobundle_arm64:
|
||||
_arguments: -b MinSizeRel -sb -arch arm64 --skip-tests
|
||||
_artifact: MACOSarm64-nobundle
|
||||
release_pack_arm64:
|
||||
_arguments: -b MinSizeRel --pack -arch arm64 --skip-tests
|
||||
_artifact: MACOSarm64-pack
|
||||
debug_pack_arm64:
|
||||
_arguments: -b Debug --pack -arch arm64 --skip-tests
|
||||
_artifact: MACOSarm64chk-pack
|
||||
#debug_nopack_arm64:
|
||||
# _arguments: -b Debug -arch arm64 --skip-tests
|
||||
# _artifact: MACOSarm64chk
|
||||
#release_nopack_arm64:
|
||||
# _arguments: -b MinSizeRel -arch arm64 --skip-tests
|
||||
# _artifact: MACOSarm64
|
||||
#release_nobundle_arm64:
|
||||
# _arguments: -b MinSizeRel -sb -arch arm64 --skip-tests
|
||||
# _artifact: MACOSarm64-nobundle
|
||||
#release_pack_arm64:
|
||||
# _arguments: -b MinSizeRel --pack -arch arm64 --skip-tests
|
||||
# _artifact: MACOSarm64-pack
|
||||
#debug_pack_arm64:
|
||||
# _arguments: -b Debug --pack -arch arm64 --skip-tests
|
||||
# _artifact: MACOSarm64chk-pack
|
||||
steps:
|
||||
|
||||
# Az Pipelines has Xcode 11.6 as default. For arm64, change to supported Xcode.
|
||||
|
@ -119,41 +119,41 @@ jobs:
|
|||
ArtifactName: $(_artifact)
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- job: macOS_universal_nopack
|
||||
dependsOn:
|
||||
- 'macOS'
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
vmImage: macOS-latest
|
||||
steps:
|
||||
- template: templates/macos-universal.yml
|
||||
parameters:
|
||||
artifact_output: MACOS-Universal
|
||||
artifact_x86: MACOS
|
||||
artifact_arm64: MACOSarm64
|
||||
#- job: macOS_universal_nopack
|
||||
# dependsOn:
|
||||
# - 'macOS'
|
||||
# pool:
|
||||
# name: Azure Pipelines
|
||||
# vmImage: macOS-latest
|
||||
# steps:
|
||||
# - template: templates/macos-universal.yml
|
||||
# parameters:
|
||||
# artifact_output: MACOS-Universal
|
||||
# artifact_x86: MACOS
|
||||
# artifact_arm64: MACOSarm64
|
||||
|
||||
- job: macOS_universal_nobundle
|
||||
dependsOn:
|
||||
- 'macOS'
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
vmImage: macOS-latest
|
||||
steps:
|
||||
- template: templates/macos-universal.yml
|
||||
parameters:
|
||||
artifact_output: MACOS-nobundle-Universal
|
||||
artifact_x86: MACOS-nobundle
|
||||
artifact_arm64: MACOSarm64-nobundle
|
||||
#- job: macOS_universal_nobundle
|
||||
# dependsOn:
|
||||
# - 'macOS'
|
||||
# pool:
|
||||
# name: Azure Pipelines
|
||||
# vmImage: macOS-latest
|
||||
# steps:
|
||||
# - template: templates/macos-universal.yml
|
||||
# parameters:
|
||||
# artifact_output: MACOS-nobundle-Universal
|
||||
# artifact_x86: MACOS-nobundle
|
||||
# artifact_arm64: MACOSarm64-nobundle
|
||||
|
||||
- job: macOS_universal_pack
|
||||
dependsOn:
|
||||
- 'macOS'
|
||||
pool:
|
||||
name: Azure Pipelines
|
||||
vmImage: macOS-latest
|
||||
steps:
|
||||
- template: templates/macos-universal.yml
|
||||
parameters:
|
||||
artifact_output: MACOS-pack-Universal
|
||||
artifact_x86: MACOS-pack
|
||||
artifact_arm64: MACOSarm64-pack
|
||||
#- job: macOS_universal_pack
|
||||
# dependsOn:
|
||||
# - 'macOS'
|
||||
# pool:
|
||||
# name: Azure Pipelines
|
||||
# vmImage: macOS-latest
|
||||
# steps:
|
||||
# - template: templates/macos-universal.yml
|
||||
# parameters:
|
||||
# artifact_output: MACOS-pack-Universal
|
||||
# artifact_x86: MACOS-pack
|
||||
# artifact_arm64: MACOSarm64-pack
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Copyright (C) 2019 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "AppxPackaging.hpp"
|
||||
#include "ComHelper.hpp"
|
||||
#include "DirectoryObject.hpp"
|
||||
#include "AppxBlockMapWriter.hpp"
|
||||
#include "ContentTypeWriter.hpp"
|
||||
#include "ZipObjectWriter.hpp"
|
||||
#include "BundleWriterHelper.hpp"
|
||||
#include "BundleManifestWriter.hpp"
|
||||
#include "AppxPackageInfo.hpp"
|
||||
|
||||
// internal interface
|
||||
// {ca90bcd9-78a2-4773-820c-0b687de49f99}
|
||||
#ifndef WIN32
|
||||
interface IBundleWriter : public IUnknown
|
||||
#else
|
||||
#include "Unknwn.h"
|
||||
#include "Objidl.h"
|
||||
class IBundleWriter : public IUnknown
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
virtual void ProcessBundlePayload(const MSIX::ComPtr<IDirectoryObject>& from, bool flatBundle) = 0;
|
||||
|
||||
};
|
||||
MSIX_INTERFACE(IBundleWriter, 0xca90bcd9,0x78a2,0x4773,0x82,0x0c,0x0b,0x68,0x7d,0xe4,0x9f,0x99);
|
||||
|
||||
namespace MSIX {
|
||||
class AppxBundleWriter final : public ComClass<AppxBundleWriter, IBundleWriter, IAppxBundleWriter, IAppxBundleWriter4>
|
||||
{
|
||||
public:
|
||||
AppxBundleWriter(IMsixFactory* factory, const ComPtr<IZipWriter>& zip, std::uint64_t bundleVersion);
|
||||
~AppxBundleWriter() {};
|
||||
|
||||
// IBundleWriter
|
||||
void ProcessBundlePayload(const ComPtr<IDirectoryObject>& from, bool flatBundle) override;
|
||||
|
||||
// IAppxBundleWriter
|
||||
HRESULT STDMETHODCALLTYPE AddPayloadPackage(LPCWSTR fileName, IStream* packageStream) noexcept override;
|
||||
HRESULT STDMETHODCALLTYPE Close() noexcept override;
|
||||
|
||||
// IAppxBundleWriter4
|
||||
HRESULT STDMETHODCALLTYPE AddPackageReference(LPCWSTR fileName, IStream* inputStream,
|
||||
BOOL isDefaultApplicablePackage) noexcept override;
|
||||
HRESULT STDMETHODCALLTYPE AddPayloadPackage(LPCWSTR fileName, IStream* packageStream,
|
||||
BOOL isDefaultApplicablePackage) noexcept override;
|
||||
HRESULT STDMETHODCALLTYPE AddExternalPackageReference(LPCWSTR fileName, IStream* inputStream,
|
||||
BOOL isDefaultApplicablePackage) noexcept override;
|
||||
|
||||
protected:
|
||||
typedef enum
|
||||
{
|
||||
Open = 1,
|
||||
Closed = 2,
|
||||
Failed = 3
|
||||
}
|
||||
WriterState;
|
||||
|
||||
void AddFileToPackage(const std::string& name, IStream* stream, bool toCompress,
|
||||
bool addToBlockMap, const char* contentType, bool forceContentTypeOverride = false);
|
||||
|
||||
void AddPackageReferenceInternal(std::string fileName, IStream* packageStream,
|
||||
bool isDefaultApplicablePackage);
|
||||
|
||||
WriterState m_state;
|
||||
ComPtr<IMsixFactory> m_factory;
|
||||
ComPtr<IZipWriter> m_zipWriter;
|
||||
BlockMapWriter m_blockMapWriter;
|
||||
ContentTypeWriter m_contentTypeWriter;
|
||||
BundleWriterHelper m_bundleWriterHelper;
|
||||
};
|
||||
}
|
||||
|
|
@ -32,9 +32,37 @@ public:
|
|||
};
|
||||
MSIX_INTERFACE(IAppxManifestObject, 0xeff6d561,0xa236,0x4058,0x9f,0x1d,0x8f,0x93,0x63,0x3f,0xba,0x4b);
|
||||
|
||||
// {daf72e2b-6252-4ed3-a476-5fb656aa0e2c}
|
||||
#ifndef WIN32
|
||||
interface IAppxManifestTargetDeviceFamilyInternal : public IUnknown
|
||||
#else
|
||||
#include "Unknwn.h"
|
||||
#include "Objidl.h"
|
||||
class IAppxManifestTargetDeviceFamilyInternal : public IUnknown
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
virtual const std::string& GetName() = 0;
|
||||
};
|
||||
MSIX_INTERFACE(IAppxManifestTargetDeviceFamilyInternal, 0xdaf72e2b,0x6252,0x4ed3,0xa4,0x76,0x5f,0xb6,0x56,0xaa,0x0e,0x2c);
|
||||
|
||||
// {9e2fb304-cec6-4ef0-8df3-10bb2ce714a3}
|
||||
#ifndef WIN32
|
||||
interface IAppxManifestQualifiedResourceInternal : public IUnknown
|
||||
#else
|
||||
#include "Unknwn.h"
|
||||
#include "Objidl.h"
|
||||
class IAppxManifestQualifiedResourceInternal : public IUnknown
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
virtual const std::string& GetLanguage() = 0;
|
||||
};
|
||||
MSIX_INTERFACE(IAppxManifestQualifiedResourceInternal, 0x9e2fb304,0xcec6,0x4ef0,0x8d,0xf3,0x10,0xbb,0x2c,0xe7,0x14,0xa3);
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
class AppxManifestTargetDeviceFamily final : public ComClass<AppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamilyUtf8>
|
||||
class AppxManifestTargetDeviceFamily final : public ComClass<AppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamily, IAppxManifestTargetDeviceFamilyUtf8, IAppxManifestTargetDeviceFamilyInternal>
|
||||
{
|
||||
public:
|
||||
AppxManifestTargetDeviceFamily(IMsixFactory* factory, std::string& name, std::string& minVersion, std::string& maxVersion) :
|
||||
|
@ -70,6 +98,12 @@ namespace MSIX {
|
|||
return m_factory->MarshalOutStringUtf8(m_name, name);
|
||||
} CATCH_RETURN();
|
||||
|
||||
//IAppxManifestTargetDeviceFamilyInternal
|
||||
const std::string& GetName() override
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
protected:
|
||||
ComPtr<IMsixFactory> m_factory;
|
||||
std::string m_name;
|
||||
|
@ -299,6 +333,51 @@ namespace MSIX {
|
|||
std::string m_packageFamilyName;
|
||||
};
|
||||
|
||||
class AppxManifestQualifiedResource final : public ComClass<AppxManifestQualifiedResource, IAppxManifestQualifiedResource, IAppxManifestQualifiedResourceUtf8, IAppxManifestQualifiedResourceInternal>
|
||||
{
|
||||
public:
|
||||
AppxManifestQualifiedResource(IMsixFactory* factory, std::string& language, std::string& scale, std::string& DXFeatureLevel) :
|
||||
m_factory(factory), m_language(language)
|
||||
{
|
||||
//TODO: Process and assign scale and DXFeatureLevel
|
||||
}
|
||||
|
||||
// IAppxManifestQualifiedResource
|
||||
HRESULT STDMETHODCALLTYPE GetLanguage(LPWSTR *language) noexcept override try
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidParameter, (language == nullptr || *language != nullptr), "bad pointer");
|
||||
return m_factory->MarshalOutString(m_language, language);
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetScale(UINT32 *scale) noexcept override try
|
||||
{
|
||||
return static_cast<HRESULT>(Error::NotImplemented);
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDXFeatureLevel(DX_FEATURE_LEVEL *dxFeatureLevel) noexcept override try
|
||||
{
|
||||
return static_cast<HRESULT>(Error::NotImplemented);
|
||||
} CATCH_RETURN();
|
||||
|
||||
// IAppxManifestQualifiedResourceUtf8
|
||||
HRESULT STDMETHODCALLTYPE GetLanguage(LPSTR *language) noexcept override try
|
||||
{
|
||||
return m_factory->MarshalOutStringUtf8(m_language, language);
|
||||
} CATCH_RETURN();
|
||||
|
||||
// IAppxManifestQualifiedResourceInternal
|
||||
const std::string& GetLanguage() override
|
||||
{
|
||||
return m_language;
|
||||
}
|
||||
|
||||
protected:
|
||||
ComPtr<IMsixFactory> m_factory;
|
||||
std::string m_language;
|
||||
std::uint32_t m_scale;
|
||||
DX_FEATURE_LEVEL m_DXFeatureLevel;
|
||||
};
|
||||
|
||||
// Object backed by AppxManifest.xml
|
||||
class AppxManifestObject final : public ComClass<AppxManifestObject, ChainInterfaces<IAppxManifestReader4, IAppxManifestReader3, IAppxManifestReader2, IAppxManifestReader>,
|
||||
IAppxManifestReader5, IVerifierObject, IAppxManifestObject, IMsixDocumentElement>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// Copyright (C) 2019 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "XmlWriter.hpp"
|
||||
#include "ComHelper.hpp"
|
||||
#include "Exceptions.hpp"
|
||||
#include "UnicodeConversion.hpp"
|
||||
#include "VersionHelpers.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
struct PackageInfo
|
||||
{
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE type;
|
||||
std::uint64_t version;
|
||||
std::string architecture;
|
||||
std::string resourceId;
|
||||
std::string fileName;
|
||||
std::uint64_t size;
|
||||
std::uint64_t offset;
|
||||
ComPtr<IAppxManifestQualifiedResourcesEnumerator> resources;
|
||||
bool isDefaultApplicablePackage;
|
||||
ComPtr<IAppxManifestTargetDeviceFamiliesEnumerator> tdfs;
|
||||
};
|
||||
|
||||
struct OptionalBundleInfo
|
||||
{
|
||||
std::string name;
|
||||
std::string publisher;
|
||||
std::uint64_t version;
|
||||
std::string fileName;
|
||||
std::vector<PackageInfo> optionalPackages;
|
||||
};
|
||||
|
||||
class BundleManifestWriter final
|
||||
{
|
||||
public:
|
||||
BundleManifestWriter();
|
||||
void StartBundleManifest(std::string targetXmlNamespace,
|
||||
std::string name, std::string publisher, std::uint64_t version);
|
||||
void StartBundleElement();
|
||||
void WriteIdentityElement(std::string name, std::string publisher, std::uint64_t version);
|
||||
void StartPackagesElement();
|
||||
void WritePackageElement(PackageInfo packageInfo);
|
||||
void WriteResourcesElement(IAppxManifestQualifiedResourcesEnumerator* resources);
|
||||
void WriteDependenciesElement(IAppxManifestTargetDeviceFamiliesEnumerator* tdfs);
|
||||
void EndPackagesElement();
|
||||
void Close();
|
||||
|
||||
ComPtr<IStream> GetStream() { return m_xmlWriter.GetStream(); }
|
||||
std::string GetQualifiedName(std::string namespaceAlias, std::string name);
|
||||
std::string GetElementName(std::string targetNamespace, std::string targetNamespaceAlias, std::string name);
|
||||
|
||||
protected:
|
||||
XmlWriter m_xmlWriter;
|
||||
std::string targetXmlNamespace;
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include "AppxPackaging.hpp"
|
||||
#include "ComHelper.hpp"
|
||||
#include "DirectoryObject.hpp"
|
||||
#include "AppxBlockMapWriter.hpp"
|
||||
#include "ContentTypeWriter.hpp"
|
||||
#include "ZipObjectWriter.hpp"
|
||||
#include "AppxPackageInfo.hpp"
|
||||
#include "BundleManifestWriter.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
class BundleWriterHelper
|
||||
{
|
||||
public:
|
||||
BundleWriterHelper();
|
||||
|
||||
std::uint64_t GetStreamSize(IStream* stream);
|
||||
|
||||
void AddPackage(std::string fileName, IAppxPackageReader* packageReader, std::uint64_t bundleOffset,
|
||||
std::uint64_t packageSize, bool isDefaultApplicableResource);
|
||||
|
||||
void GetValidatedPackageData(
|
||||
std::string fileName,
|
||||
IAppxPackageReader* packageReader,
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE* packageType,
|
||||
IAppxManifestPackageId** packageId,
|
||||
IAppxManifestQualifiedResourcesEnumerator** resources,
|
||||
IAppxManifestTargetDeviceFamiliesEnumerator** tdfs);
|
||||
|
||||
void AddValidatedPackageData(
|
||||
std::string fileName,
|
||||
std::uint64_t bundleOffset,
|
||||
std::uint64_t packageSize,
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType,
|
||||
ComPtr<IAppxManifestPackageId> packageId,
|
||||
bool isDefaultApplicablePackage,
|
||||
IAppxManifestQualifiedResourcesEnumerator* resources,
|
||||
IAppxManifestTargetDeviceFamiliesEnumerator* tdfs);
|
||||
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE GetPayloadPackageType(
|
||||
IAppxManifestReader* packageManifestReader,
|
||||
std::string fileName);
|
||||
|
||||
void ValidateNameAndPublisher(
|
||||
IAppxManifestPackageIdInternal* packageId,
|
||||
std::string filename);
|
||||
|
||||
void ValidateApplicationElement(
|
||||
IAppxManifestReader* packageManifestReader,
|
||||
std::string fileName);
|
||||
|
||||
void AddPackageInfoToVector(std::vector<PackageInfo>& packagesVector, PackageInfo packageInfo);
|
||||
|
||||
void EndBundleManifest();
|
||||
|
||||
ComPtr<IStream> GetBundleManifestStream() { return m_bundleManifestWriter.GetStream(); }
|
||||
|
||||
void SetBundleVersion(std::uint64_t bundleVersion) { this->bundleVersion = bundleVersion; }
|
||||
|
||||
std::uint64_t GetBundleVersion() { return this->bundleVersion; }
|
||||
|
||||
std::vector<PackageInfo> GetPayloadPackages() { return payloadPackages; }
|
||||
|
||||
private:
|
||||
std::vector<PackageInfo> payloadPackages;
|
||||
std::map<std::string, OptionalBundleInfo> optionalBundles;
|
||||
bool hasExternalPackages;
|
||||
bool hasDefaultOrNeutralResources;
|
||||
std::string mainPackageName;
|
||||
std::string mainPackagePublisher;
|
||||
std::uint64_t bundleVersion;
|
||||
|
||||
BundleManifestWriter m_bundleManifestWriter;
|
||||
};
|
||||
}
|
|
@ -23,6 +23,7 @@ namespace MSIX {
|
|||
|
||||
static const ContentType& GetContentTypeByExtension(std::string& ext);
|
||||
static const std::string GetPayloadFileContentType(APPX_FOOTPRINT_FILE_TYPE footprintFile);
|
||||
static const std::string GetBundlePayloadFileContentType(APPX_BUNDLE_FOOTPRINT_FILE_TYPE footprintFile);
|
||||
|
||||
private:
|
||||
APPX_COMPRESSION_OPTION m_compressionOpt;
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace MSIX {
|
|||
{
|
||||
static bool IsFileNameValid(const std::string& name);
|
||||
static bool IsIdentifierValid(const std::string& name);
|
||||
static bool IsFootPrintFile(const std::string& fileName);
|
||||
static bool IsFootPrintFile(const std::string& fileName, bool isBundle);
|
||||
static bool IsReservedFolder(const std::string& fileName);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ enum class XmlAttributeName : std::uint8_t
|
|||
Package_Applications_Application_Id,
|
||||
Category,
|
||||
MaxMajorVersionTested,
|
||||
DXFeatureLevel,
|
||||
};
|
||||
|
||||
// {ac94449e-442d-4bed-8fca-83770c0f7ee9}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "MSIXWindows.hpp"
|
||||
#include "UnicodeConversion.hpp"
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
std::uint64_t ConvertVersionStringToUint64(const std::string& versionString);
|
||||
|
||||
std::string ConvertVersionToString(std::uint64_t version);
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ namespace MSIX {
|
|||
|
||||
// common attribute names
|
||||
static const char* xmlnsAttribute = "xmlns";
|
||||
static const char* xmlNamespaceDelimiter = ":";
|
||||
|
||||
// This is a super light xml writer that doesn't use any xml libraries and
|
||||
// just writes to a stream the basics of an xml file.
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
// {350dd671-0c40-4cd7-9a5b-27456d604bd0}
|
||||
#ifndef WIN32
|
||||
interface IZipWriter : public IUnknown
|
||||
|
|
|
@ -78,6 +78,7 @@ SpecializeUuidOfImpl(IAppxManifestQualifiedResourcesEnumerator);
|
|||
SpecializeUuidOfImpl(IAppxManifestQualifiedResource);
|
||||
SpecializeUuidOfImpl(IAppxBundleFactory);
|
||||
SpecializeUuidOfImpl(IAppxBundleWriter);
|
||||
SpecializeUuidOfImpl(IAppxBundleWriter4);
|
||||
SpecializeUuidOfImpl(IAppxBundleReader);
|
||||
SpecializeUuidOfImpl(IAppxBundleManifestReader);
|
||||
SpecializeUuidOfImpl(IAppxBundleManifestPackageInfoEnumerator);
|
||||
|
@ -124,6 +125,7 @@ interface IAppxManifestQualifiedResourcesEnumerator;
|
|||
interface IAppxManifestQualifiedResource;
|
||||
interface IAppxBundleFactory;
|
||||
interface IAppxBundleWriter;
|
||||
interface IAppxBundleWriter4;
|
||||
interface IAppxBundleReader;
|
||||
interface IAppxBundleManifestReader;
|
||||
interface IAppxBundleManifestPackageInfoEnumerator;
|
||||
|
@ -970,6 +972,31 @@ enum tagLOCKTYPE
|
|||
};
|
||||
#endif /* __IAppxBundleWriter_INTERFACE_DEFINED__ */
|
||||
|
||||
#ifndef __IAppxBundleWriter4_INTERFACE_DEFINED__
|
||||
#define __IAppxBundleWriter4_INTERFACE_DEFINED__
|
||||
|
||||
// {9CD9D523-5009-4C01-9882-DC029FBD47A3}
|
||||
MSIX_INTERFACE(IAppxBundleWriter4,0x9cd9d523,0x5009,0x4c01,0x98,0x82,0xdc,0x02,0x9f,0xbd,0x47,0xa3);
|
||||
interface IAppxBundleWriter4 : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE AddPayloadPackage(
|
||||
/* [string][in] */ LPCWSTR fileName,
|
||||
/* [in] */ IStream* packageStream,
|
||||
/* [in] */ BOOL isDefaultApplicablePackage) noexcept = 0;
|
||||
|
||||
virtual HRESULT AddPackageReference(
|
||||
/* [string][in] */ LPCWSTR fileName,
|
||||
/* [in] */ IStream* inputStream,
|
||||
/* [in] */ BOOL isDefaultApplicablePackage) noexcept = 0;
|
||||
|
||||
virtual HRESULT AddExternalPackageReference(
|
||||
/* [string][in] */ LPCWSTR fileName,
|
||||
/* [in] */ IStream* inputStream,
|
||||
/* [in] */ BOOL isDefaultApplicablePackage) noexcept = 0;
|
||||
};
|
||||
#endif /* __IAppxBundleWriter4_INTERFACE_DEFINED__ */
|
||||
|
||||
#ifndef __IAppxBundleReader_INTERFACE_DEFINED__
|
||||
#define __IAppxBundleReader_INTERFACE_DEFINED__
|
||||
|
||||
|
@ -1665,6 +1692,17 @@ enum MSIX_APPLICABILITY_OPTIONS
|
|||
MSIX_APPLICABILITY_OPTION_SKIPLANGUAGE = 0x2,
|
||||
} MSIX_APPLICABILITY_OPTIONS;
|
||||
|
||||
typedef /* [v1_enum] */
|
||||
enum MSIX_BUNDLE_OPTIONS
|
||||
{
|
||||
MSIX_OPTION_NONE = 0x0,
|
||||
MSIX_OPTION_VERBOSE = 0x1,
|
||||
MSIX_OPTION_OVERWRITE = 0x2,
|
||||
MSIX_OPTION_NOOVERWRITE = 0x4,
|
||||
MSIX_OPTION_VERSION = 0x8,
|
||||
MSIX_BUNDLE_OPTION_FLATBUNDLE = 0x10,
|
||||
} MSIX_BUNDLE_OPTIONS;
|
||||
|
||||
#define MSIX_PLATFORM_ALL MSIX_PLATFORM_WINDOWS10 | \
|
||||
MSIX_PLATFORM_WINDOWS10 | \
|
||||
MSIX_PLATFORM_WINDOWS8 | \
|
||||
|
@ -1731,6 +1769,14 @@ MSIX_API HRESULT STDMETHODCALLTYPE PackPackage(
|
|||
char* outputPackage
|
||||
) noexcept;
|
||||
|
||||
MSIX_API HRESULT STDMETHODCALLTYPE PackBundle(
|
||||
MSIX_BUNDLE_OPTIONS bundleOptions,
|
||||
char* directoryPath,
|
||||
char* outputBundle,
|
||||
char* mappingFile,
|
||||
char* version
|
||||
) noexcept;
|
||||
|
||||
#endif // MSIX_PACK
|
||||
|
||||
// A call to called CoCreateAppxFactory is required before start using the factory on non-windows platforms specifying
|
||||
|
|
|
@ -418,6 +418,38 @@ MSIX_APPLICABILITY_OPTIONS GetApplicabilityOption(const Invocation& invocation)
|
|||
return applicability;
|
||||
}
|
||||
|
||||
MSIX_BUNDLE_OPTIONS GetBundleOptions(const Invocation& invocation)
|
||||
{
|
||||
MSIX_BUNDLE_OPTIONS bundleOptions = MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NONE;
|
||||
|
||||
if (invocation.IsOptionPresent("-v"))
|
||||
{
|
||||
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERBOSE;
|
||||
}
|
||||
|
||||
if (invocation.IsOptionPresent("-o"))
|
||||
{
|
||||
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_OVERWRITE;
|
||||
}
|
||||
|
||||
if (invocation.IsOptionPresent("-no"))
|
||||
{
|
||||
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NOOVERWRITE;
|
||||
}
|
||||
|
||||
if (invocation.IsOptionPresent("-bv"))
|
||||
{
|
||||
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERSION;
|
||||
}
|
||||
|
||||
if (invocation.IsOptionPresent("-fb"))
|
||||
{
|
||||
bundleOptions |= MSIX_BUNDLE_OPTIONS::MSIX_BUNDLE_OPTION_FLATBUNDLE;
|
||||
}
|
||||
|
||||
return bundleOptions;
|
||||
}
|
||||
|
||||
#pragma region Commands
|
||||
|
||||
Command CreateHelpCommand(const std::vector<Command>& commands)
|
||||
|
@ -551,6 +583,66 @@ Command CreatePackCommand()
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
Command CreateBundleCommand()
|
||||
{
|
||||
Command result{ "bundle", "Create a new app bundle from files on disk",
|
||||
{
|
||||
Option{ "-d", "Input directory path.", false, 1, "inputDirectory" },
|
||||
Option{ "-p", "Output bundle file path.", true, 1, "outputBundle" },
|
||||
Option{ "-f", "Mapping file path.", false, 1, "mappingFile" },
|
||||
Option{ "-bv", "Specifies the version number of the bundle being created. The version"
|
||||
"must be in dotted - quad notation of four integers"
|
||||
"<Major>.<Minor>.<Build>.<Revision> ranging from 0 to 65535 each.If the"
|
||||
"/ bv option is not specified or is set to 0.0.0.0, the bundle is created"
|
||||
"using the current date - time formatted as the version :"
|
||||
"<Year>.<Month - Day>.<Hour - Minute>.<Second - Millisecond>.", false, 1, "version" },
|
||||
Option{ "-fb", "Generates a fully sparse bundle where all packages are references to"
|
||||
"packages that exist outside of the bundle file." },
|
||||
Option{ "-o", "Forces the output to overwrite any existing files with the"
|
||||
"same name.By default, the user is asked whether to overwrite existing"
|
||||
"files with the same name.You can't use this option with /no." },
|
||||
Option{ "-no","Prevents the output from overwriting any existing files"
|
||||
"with the same name.By default, the user is asked whether to overwrite"
|
||||
"existing files with the same name.You can't use this option with /o." },
|
||||
Option{ "-v", "Enables verbose output of messages to the console."},
|
||||
Option{ TOOL_HELP_COMMAND_STRING, "Displays this help text." },
|
||||
}
|
||||
};
|
||||
|
||||
result.SetDescription({
|
||||
"Creates an app bundle at <output bundle name> by adding all files from",
|
||||
"either <content directory>(including subfolders) or a list of files within"
|
||||
"<mapping file>.If either source contains a bundle manifest, it will be"
|
||||
"ignored."
|
||||
|
||||
"Using / p will result in the bundle being unencrypted, while using / ep will"
|
||||
"result in the bundle being encrypted.If you use / ep you must specify"
|
||||
"either / kt or /kf.",
|
||||
});
|
||||
|
||||
result.SetInvocationFunc([](const Invocation& invocation)
|
||||
{
|
||||
char* directoryPath = (invocation.IsOptionPresent("-d")) ?
|
||||
const_cast<char*>(invocation.GetOptionValue("-d").c_str()) : nullptr;
|
||||
|
||||
char* mappingFile = (invocation.IsOptionPresent("-f")) ?
|
||||
const_cast<char*>(invocation.GetOptionValue("-f").c_str()) : nullptr;
|
||||
|
||||
char* version = (invocation.IsOptionPresent("-bv")) ?
|
||||
const_cast<char*>(invocation.GetOptionValue("-bv").c_str()) : nullptr;
|
||||
|
||||
return PackBundle(
|
||||
GetBundleOptions(invocation),
|
||||
directoryPath,
|
||||
const_cast<char*>(invocation.GetOptionValue("-p").c_str()),
|
||||
mappingFile,
|
||||
version);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
@ -566,6 +658,7 @@ int main(int argc, char* argv[])
|
|||
CreateUnbundleCommand(),
|
||||
#ifdef MSIX_PACK
|
||||
CreatePackCommand(),
|
||||
CreateBundleCommand(),
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ list(APPEND MSIX_UNPACK_EXPORTS
|
|||
if(MSIX_PACK)
|
||||
list(APPEND MSIX_PACK_EXPORTS
|
||||
"PackPackage"
|
||||
"PackBundle"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -130,6 +131,10 @@ if(MSIX_PACK)
|
|||
pack/ContentType.cpp
|
||||
pack/DeflateStream.cpp
|
||||
pack/ZipObjectWriter.cpp
|
||||
pack/BundleManifestWriter.cpp
|
||||
pack/BundleWriterHelper.cpp
|
||||
pack/AppxBundleWriter.cpp
|
||||
pack/VersionHelpers.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -224,7 +229,7 @@ endforeach()
|
|||
add_library(${PROJECT_NAME} SHARED
|
||||
msix.cpp
|
||||
${MsixSrc}
|
||||
)
|
||||
)
|
||||
|
||||
# Adding dependency to the third party libs directory
|
||||
add_dependencies(${PROJECT_NAME} LIBS)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "VectorStream.hpp"
|
||||
#include "MsixFeatureSelector.hpp"
|
||||
#include "AppxPackageWriter.hpp"
|
||||
#include "AppxBundleWriter.hpp"
|
||||
#include "ZipObjectWriter.hpp"
|
||||
|
||||
#ifdef BUNDLE_SUPPORT
|
||||
|
@ -88,7 +89,15 @@ namespace MSIX {
|
|||
HRESULT STDMETHODCALLTYPE AppxFactory::CreateBundleWriter(IStream *outputStream, UINT64 bundleVersion, IAppxBundleWriter **bundleWriter) noexcept try
|
||||
{
|
||||
THROW_IF_BUNDLE_NOT_ENABLED
|
||||
NOTIMPLEMENTED;
|
||||
ThrowErrorIf(Error::InvalidParameter, (outputStream == nullptr || bundleWriter == nullptr || *bundleWriter != nullptr), "Invalid parameter");
|
||||
#ifdef MSIX_PACK
|
||||
ComPtr<IMsixFactory> self;
|
||||
ThrowHrIfFailed(QueryInterface(UuidOfImpl<IMsixFactory>::iid, reinterpret_cast<void**>(&self)));
|
||||
auto zip = ComPtr<IZipWriter>::Make<ZipObjectWriter>(outputStream);
|
||||
auto result = ComPtr<IAppxBundleWriter>::Make<AppxBundleWriter>(self.Get(), zip, bundleVersion);
|
||||
*bundleWriter = result.Detach();
|
||||
#endif
|
||||
return static_cast<HRESULT>(Error::OK);
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AppxFactory::CreateBundleReader(IStream *inputStream, IAppxBundleReader **bundleReader) noexcept try
|
||||
|
|
|
@ -357,7 +357,31 @@ namespace MSIX {
|
|||
// IAppxManifestReader2
|
||||
HRESULT STDMETHODCALLTYPE AppxManifestObject::GetQualifiedResources(IAppxManifestQualifiedResourcesEnumerator **resources) noexcept
|
||||
{
|
||||
return static_cast<HRESULT>(Error::NotImplemented);
|
||||
ThrowErrorIf(Error::InvalidParameter, (resources == nullptr || *resources != nullptr), "bad pointer");
|
||||
|
||||
std::vector<ComPtr<IAppxManifestQualifiedResource>> qualifiedResources;
|
||||
struct _context
|
||||
{
|
||||
AppxManifestObject* self;
|
||||
std::vector<ComPtr<IAppxManifestQualifiedResource>>* qualifiedResources;
|
||||
};
|
||||
_context context = { this, &qualifiedResources};
|
||||
|
||||
// Parse Resource elements
|
||||
XmlVisitor visitorResource(static_cast<void*>(&context), [](void* c, const ComPtr<IXmlElement>& resourceNode)->bool
|
||||
{
|
||||
_context* context = reinterpret_cast<_context*>(c);
|
||||
auto language = resourceNode->GetAttributeValue(XmlAttributeName::Language);
|
||||
auto scale = resourceNode->GetAttributeValue(XmlAttributeName::Scale);
|
||||
auto dxFeatureLevel = resourceNode->GetAttributeValue(XmlAttributeName::DXFeatureLevel);
|
||||
auto resource = ComPtr<IAppxManifestQualifiedResource>::Make<AppxManifestQualifiedResource>(context->self->m_factory.Get(), language, scale, dxFeatureLevel);
|
||||
context->qualifiedResources->push_back(std::move(resource));
|
||||
return true;
|
||||
});
|
||||
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Resources_Resource, visitorResource);
|
||||
*resources = ComPtr<IAppxManifestQualifiedResourcesEnumerator>::
|
||||
Make<EnumeratorCom<IAppxManifestQualifiedResourcesEnumerator,IAppxManifestQualifiedResource>>(qualifiedResources).Detach();
|
||||
return static_cast<HRESULT>(Error::OK);
|
||||
}
|
||||
|
||||
// IAppxManifestReader3
|
||||
|
|
|
@ -288,10 +288,20 @@ namespace MSIX {
|
|||
return !IsProhibitedFileName(lowSegment) && !HasProhibitedPrefix(lowSegment) && !HasProhibitedSuffix(lowSegment);
|
||||
}
|
||||
|
||||
bool FileNameValidation::IsFootPrintFile(const std::string& fileName)
|
||||
bool FileNameValidation::IsFootPrintFile(const std::string& fileName, bool isBundle)
|
||||
{
|
||||
bool result = false;
|
||||
std::string lowIdent = Helper::tolower(fileName);
|
||||
return ((lowIdent == "appxmanifest.xml") ||
|
||||
if (isBundle)
|
||||
{
|
||||
result = lowIdent == "appxmetadata/appxbundlemanifest.xml";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = lowIdent == "appxmanifest.xml";
|
||||
}
|
||||
|
||||
return (result ||
|
||||
(lowIdent == "appxsignature.p7x") ||
|
||||
(lowIdent == "appxblockmap.xml") ||
|
||||
(lowIdent == "[content_types].xml"));
|
||||
|
|
|
@ -28,6 +28,7 @@ static const char* attributeNames[] = {
|
|||
/* Package_Applications_Application_Id */"Id",
|
||||
/* Category */"Category",
|
||||
/* MaxMajorVersionTested */"MaxMajorVersionTested",
|
||||
/* DXFeatureLevel */"DXFeatureLevel",
|
||||
};
|
||||
|
||||
#ifdef USING_MSXML
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <memory>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include "Exceptions.hpp"
|
||||
#include "FileStream.hpp"
|
||||
|
@ -17,7 +18,9 @@
|
|||
#include "AppxPackageObject.hpp"
|
||||
#include "MsixFeatureSelector.hpp"
|
||||
#include "AppxPackageWriter.hpp"
|
||||
#include "AppxBundleWriter.hpp"
|
||||
#include "ScopeExit.hpp"
|
||||
#include "VersionHelpers.hpp"
|
||||
|
||||
#ifndef WIN32
|
||||
// on non-win32 platforms, compile with -fvisibility=hidden
|
||||
|
@ -288,4 +291,101 @@ MSIX_API HRESULT STDMETHODCALLTYPE PackPackage(
|
|||
return static_cast<HRESULT>(MSIX::Error::OK);
|
||||
} CATCH_RETURN();
|
||||
|
||||
MSIX_API HRESULT STDMETHODCALLTYPE PackBundle(
|
||||
MSIX_BUNDLE_OPTIONS bundleOptions,
|
||||
char* directoryPath,
|
||||
char* outputBundle,
|
||||
char* mappingFile,
|
||||
char* version
|
||||
) noexcept try
|
||||
{
|
||||
std::unique_ptr<std::map<std::string, std::string>> externalPackagesList;
|
||||
std::uint64_t bundleVersion = 0;
|
||||
bool flatBundle = false;
|
||||
bool overWrite = false;
|
||||
|
||||
//Process Common Options
|
||||
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERSION)
|
||||
{
|
||||
bundleVersion = MSIX::ConvertVersionStringToUint64(version);
|
||||
}
|
||||
|
||||
if ((bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_OVERWRITE) && (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NOOVERWRITE))
|
||||
{
|
||||
ThrowErrorAndLog(MSIX::Error::InvalidParameter, "You can't specify options -o and -no at the same time.");
|
||||
}
|
||||
|
||||
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_OVERWRITE)
|
||||
{
|
||||
overWrite = true;
|
||||
}
|
||||
|
||||
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_NOOVERWRITE)
|
||||
{
|
||||
overWrite = false;
|
||||
}
|
||||
|
||||
if (0 == (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_BUNDLE_OPTION_FLATBUNDLE))
|
||||
{
|
||||
flatBundle = true;
|
||||
}
|
||||
|
||||
if (bundleOptions & MSIX_BUNDLE_OPTIONS::MSIX_OPTION_VERBOSE)
|
||||
{
|
||||
//TODO: Process option for verbose
|
||||
}
|
||||
|
||||
//TODO: Error if outputBundle is an existing directory
|
||||
|
||||
//Process Input options
|
||||
if(directoryPath == nullptr && mappingFile == nullptr)
|
||||
{
|
||||
ThrowErrorAndLog(MSIX::Error::InvalidParameter, "You must specify either a content directory (-d) or a mapping file (-f).");
|
||||
}
|
||||
else if(directoryPath != nullptr && mappingFile != nullptr)
|
||||
{
|
||||
ThrowErrorAndLog(MSIX::Error::InvalidParameter, "You can't specify both a content directory (-d) and a mapping file (-f).");
|
||||
}
|
||||
//TODO:: Error if directoryPath is a file
|
||||
|
||||
MSIX::ComPtr<IDirectoryObject> from;
|
||||
if(directoryPath != nullptr && outputBundle != nullptr)
|
||||
{
|
||||
from = MSIX::ComPtr<IDirectoryObject>::Make<MSIX::DirectoryObject>(directoryPath);
|
||||
}
|
||||
else if(mappingFile != nullptr && outputBundle != nullptr)
|
||||
{
|
||||
//Create from list from mapping file(Currently keeping it same as above, have to
|
||||
//parse from mapping file into externalPackagesList)
|
||||
from = MSIX::ComPtr<IDirectoryObject>::Make<MSIX::DirectoryObject>(directoryPath);
|
||||
}
|
||||
|
||||
auto deleteFile = MSIX::scope_exit([&outputBundle]
|
||||
{
|
||||
remove(outputBundle);
|
||||
});
|
||||
|
||||
MSIX::ComPtr<IStream> stream;
|
||||
ThrowHrIfFailed(CreateStreamOnFile(outputBundle, false, &stream));
|
||||
|
||||
MSIX::ComPtr<IAppxBundleFactory> factory;
|
||||
ThrowHrIfFailed(CoCreateAppxBundleFactoryWithHeap(InternalAllocate, InternalFree,
|
||||
MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL,
|
||||
MSIX_APPLICABILITY_OPTIONS::MSIX_APPLICABILITY_OPTION_FULL,
|
||||
&factory));
|
||||
|
||||
MSIX::ComPtr<IAppxBundleWriter> bundleWriter;
|
||||
MSIX::ComPtr<IAppxBundleWriter4> bundleWriter4;
|
||||
|
||||
ThrowHrIfFailed(factory->CreateBundleWriter(stream.Get(), bundleVersion, &bundleWriter));
|
||||
bundleWriter4 = bundleWriter.As<IAppxBundleWriter4>();
|
||||
|
||||
bundleWriter4.As<IBundleWriter>()->ProcessBundlePayload(from, flatBundle);
|
||||
ThrowHrIfFailed(bundleWriter->Close());
|
||||
deleteFile.release();
|
||||
return static_cast<HRESULT>(MSIX::Error::OK);
|
||||
|
||||
} CATCH_RETURN();
|
||||
|
||||
#endif // MSIX_PACK
|
||||
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
//
|
||||
// Copyright (C) 2019 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
#include "AppxPackaging.hpp"
|
||||
#include "AppxBundleWriter.hpp"
|
||||
#include "MsixErrors.hpp"
|
||||
#include "Exceptions.hpp"
|
||||
#include "ContentType.hpp"
|
||||
#include "Encoding.hpp"
|
||||
#include "ZipObjectWriter.hpp"
|
||||
#include "AppxManifestObject.hpp"
|
||||
#include "ScopeExit.hpp"
|
||||
#include "FileNameValidation.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
AppxBundleWriter::AppxBundleWriter(IMsixFactory* factory, const ComPtr<IZipWriter>& zip, std::uint64_t bundleVersion)
|
||||
: m_factory(factory), m_zipWriter(zip)
|
||||
{
|
||||
m_state = WriterState::Open;
|
||||
if(bundleVersion == 0)
|
||||
{
|
||||
// The generated version number has the format: YYYY.MMDD.hhmm.0
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::gmtime(&t);
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(&tm, "%Y.%m%d.%H%M.0");
|
||||
this->m_bundleWriterHelper.SetBundleVersion(ConvertVersionStringToUint64(ss.str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->m_bundleWriterHelper.SetBundleVersion(bundleVersion);
|
||||
}
|
||||
}
|
||||
|
||||
// IBundleWriter
|
||||
void AppxBundleWriter::ProcessBundlePayload(const ComPtr<IDirectoryObject>& from, bool flatBundle)
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidState, m_state != WriterState::Open, "Invalid package writer state");
|
||||
auto failState = MSIX::scope_exit([this]
|
||||
{
|
||||
this->m_state = WriterState::Failed;
|
||||
});
|
||||
|
||||
auto fileMap = from->GetFilesByLastModDate();
|
||||
for (const auto& file : fileMap)
|
||||
{
|
||||
if (!(FileNameValidation::IsFootPrintFile(file.second, true)))
|
||||
{
|
||||
std::string ext = Helper::tolower(file.second.substr(file.second.find_last_of(".") + 1));
|
||||
auto contentType = ContentType::GetContentTypeByExtension(ext);
|
||||
auto stream = from.As<IStorageObject>()->GetFile(file.second);
|
||||
|
||||
if (flatBundle)
|
||||
{
|
||||
ThrowHrIfFailed(AddPackageReference(utf8_to_wstring(file.second).c_str(), stream.Get(), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Process external packages passed as input created from mapping file
|
||||
/*if (externalPackagesList != nullptr)
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
failState.release();
|
||||
}
|
||||
|
||||
// IAppxBundleWriter
|
||||
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddPayloadPackage(LPCWSTR fileName, IStream* packageStream) noexcept try
|
||||
{
|
||||
// TODO: implement
|
||||
NOTIMPLEMENTED;
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AppxBundleWriter::Close() noexcept try
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidState, m_state != WriterState::Open, "Invalid package writer state");
|
||||
auto failState = MSIX::scope_exit([this]
|
||||
{
|
||||
this->m_state = WriterState::Failed;
|
||||
});
|
||||
|
||||
//Process AppxBundleManifest.xml and add it to the bundle
|
||||
m_bundleWriterHelper.EndBundleManifest();
|
||||
|
||||
auto bundleManifestStream = m_bundleWriterHelper.GetBundleManifestStream();
|
||||
auto bundleManifestContentType = ContentType::GetBundlePayloadFileContentType(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_MANIFEST);
|
||||
AddFileToPackage(APPXBUNDLEMANIFEST_XML, bundleManifestStream.Get(), true, true, bundleManifestContentType.c_str());
|
||||
|
||||
// Close blockmap and add it to the bundle
|
||||
m_blockMapWriter.Close();
|
||||
auto blockMapStream = m_blockMapWriter.GetStream();
|
||||
auto blockMapContentType = ContentType::GetPayloadFileContentType(APPX_FOOTPRINT_FILE_TYPE_BLOCKMAP);
|
||||
AddFileToPackage(APPXBLOCKMAP_XML, blockMapStream.Get(), true, false, blockMapContentType.c_str());
|
||||
|
||||
// Close content types and add it to the bundle
|
||||
m_contentTypeWriter.Close();
|
||||
auto contentTypeStream = m_contentTypeWriter.GetStream();
|
||||
AddFileToPackage(CONTENT_TYPES_XML, contentTypeStream.Get(), true, false, nullptr);
|
||||
|
||||
m_zipWriter->Close();
|
||||
failState.release();
|
||||
m_state = WriterState::Closed;
|
||||
return static_cast<HRESULT>(Error::OK);
|
||||
} CATCH_RETURN();
|
||||
|
||||
// IAppxBundleWriter4
|
||||
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddPackageReference(LPCWSTR fileName,
|
||||
IStream* inputStream, BOOL isDefaultApplicablePackage) noexcept try
|
||||
{
|
||||
this->AddPackageReferenceInternal(wstring_to_utf8(fileName), inputStream, !!isDefaultApplicablePackage);
|
||||
|
||||
return static_cast<HRESULT>(Error::OK);
|
||||
} CATCH_RETURN();
|
||||
|
||||
void AppxBundleWriter::AddPackageReferenceInternal(std::string fileName, IStream* packageStream,
|
||||
bool isDefaultApplicablePackage)
|
||||
{
|
||||
auto appxFactory = m_factory.As<IAppxFactory>();
|
||||
|
||||
ComPtr<IAppxPackageReader> reader;
|
||||
ThrowHrIfFailed(appxFactory->CreatePackageReader(packageStream, &reader));
|
||||
|
||||
std::uint64_t packageStreamSize = this->m_bundleWriterHelper.GetStreamSize(packageStream);
|
||||
|
||||
this->m_bundleWriterHelper.AddPackage(fileName, reader.Get(), 0, packageStreamSize, isDefaultApplicablePackage);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddPayloadPackage(LPCWSTR fileName, IStream* packageStream,
|
||||
BOOL isDefaultApplicablePackage) noexcept try
|
||||
{
|
||||
// TODO: implement
|
||||
NOTIMPLEMENTED;
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE AppxBundleWriter::AddExternalPackageReference(LPCWSTR fileName,
|
||||
IStream* inputStream, BOOL isDefaultApplicablePackage) noexcept try
|
||||
{
|
||||
// TODO: implement
|
||||
NOTIMPLEMENTED;
|
||||
} CATCH_RETURN();
|
||||
|
||||
void AppxBundleWriter::AddFileToPackage(const std::string& name, IStream* stream, bool toCompress,
|
||||
bool addToBlockMap, const char* contentType, bool forceContentTypeOverride)
|
||||
{
|
||||
std::string opcFileName;
|
||||
// Don't encode [Content Type].xml
|
||||
if (contentType != nullptr)
|
||||
{
|
||||
opcFileName = Encoding::EncodeFileName(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
opcFileName = name;
|
||||
}
|
||||
auto fileInfo = m_zipWriter->PrepareToAddFile(opcFileName, toCompress);
|
||||
|
||||
// Add content type to [Content Types].xml
|
||||
if (contentType != nullptr)
|
||||
{
|
||||
m_contentTypeWriter.AddContentType(name, contentType, forceContentTypeOverride);
|
||||
}
|
||||
|
||||
// This might be called with external IStream implementations. Don't rely on internal implementation of FileStream
|
||||
LARGE_INTEGER start = { 0 };
|
||||
ULARGE_INTEGER end = { 0 };
|
||||
ThrowHrIfFailed(stream->Seek(start, StreamBase::Reference::END, &end));
|
||||
ThrowHrIfFailed(stream->Seek(start, StreamBase::Reference::START, nullptr));
|
||||
std::uint64_t uncompressedSize = static_cast<std::uint64_t>(end.QuadPart);
|
||||
|
||||
// Add file to block map.
|
||||
if (addToBlockMap)
|
||||
{
|
||||
m_blockMapWriter.AddFile(name, uncompressedSize, fileInfo.first);
|
||||
}
|
||||
|
||||
auto& zipFileStream = fileInfo.second;
|
||||
|
||||
std::uint64_t bytesToRead = uncompressedSize;
|
||||
std::uint32_t crc = 0;
|
||||
while (bytesToRead > 0)
|
||||
{
|
||||
// Calculate the size of the next block to add
|
||||
std::uint32_t blockSize = (bytesToRead > DefaultBlockSize) ? DefaultBlockSize : static_cast<std::uint32_t>(bytesToRead);
|
||||
bytesToRead -= blockSize;
|
||||
|
||||
// read block from stream
|
||||
std::vector<std::uint8_t> block;
|
||||
block.resize(blockSize);
|
||||
ULONG bytesRead;
|
||||
ThrowHrIfFailed(stream->Read(static_cast<void*>(block.data()), static_cast<ULONG>(blockSize), &bytesRead));
|
||||
ThrowErrorIfNot(Error::FileRead, (static_cast<ULONG>(blockSize) == bytesRead), "Read stream file failed");
|
||||
crc = crc32(crc, block.data(), static_cast<uInt>(block.size()));
|
||||
|
||||
// Write block and compress if needed
|
||||
ULONG bytesWritten = 0;
|
||||
ThrowHrIfFailed(zipFileStream->Write(block.data(), static_cast<ULONG>(block.size()), &bytesWritten));
|
||||
|
||||
// Add block to blockmap
|
||||
if (addToBlockMap)
|
||||
{
|
||||
m_blockMapWriter.AddBlock(block, bytesWritten, toCompress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (toCompress)
|
||||
{
|
||||
// Put the stream termination on
|
||||
std::vector<std::uint8_t> buffer;
|
||||
ULONG bytesWritten = 0;
|
||||
ThrowHrIfFailed(zipFileStream->Write(buffer.data(), static_cast<ULONG>(buffer.size()), &bytesWritten));
|
||||
}
|
||||
|
||||
// Close File element
|
||||
if (addToBlockMap)
|
||||
{
|
||||
m_blockMapWriter.CloseFile();
|
||||
}
|
||||
|
||||
// This could be the compressed or uncompressed size
|
||||
auto streamSize = zipFileStream.As<IStreamInternal>()->GetSize();
|
||||
m_zipWriter->EndFile(crc, streamSize, uncompressedSize, true);
|
||||
}
|
||||
|
||||
}
|
|
@ -21,8 +21,6 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
AppxPackageWriter::AppxPackageWriter(IMsixFactory* factory, const ComPtr<IZipWriter>& zip) : m_factory(factory), m_zipWriter(zip)
|
||||
|
@ -44,7 +42,7 @@ namespace MSIX {
|
|||
{
|
||||
// If any footprint file is present, ignore it. We only require the AppxManifest.xml
|
||||
// and any other will be ignored and a new one will be created for the package.
|
||||
if(!(FileNameValidation::IsFootPrintFile(file.second) || FileNameValidation::IsReservedFolder(file.second)))
|
||||
if(!(FileNameValidation::IsFootPrintFile(file.second, false) || FileNameValidation::IsReservedFolder(file.second)))
|
||||
{
|
||||
std::string ext = Helper::tolower(file.second.substr(file.second.find_last_of(".") + 1));
|
||||
auto contentType = ContentType::GetContentTypeByExtension(ext);
|
||||
|
@ -155,7 +153,7 @@ namespace MSIX {
|
|||
APPX_COMPRESSION_OPTION compressionOpt, const char* contentType)
|
||||
{
|
||||
ThrowErrorIfNot(Error::InvalidParameter, FileNameValidation::IsFileNameValid(name), "Invalid file name");
|
||||
ThrowErrorIf(Error::InvalidParameter, FileNameValidation::IsFootPrintFile(name), "Trying to add footprint file to package");
|
||||
ThrowErrorIf(Error::InvalidParameter, FileNameValidation::IsFootPrintFile(name, false), "Trying to add footprint file to package");
|
||||
ThrowErrorIf(Error::InvalidParameter, FileNameValidation::IsReservedFolder(name), "Trying to add file in reserved folder");
|
||||
ValidateCompressionOption(compressionOpt);
|
||||
AddFileToPackage(name, stream, compressionOpt != APPX_COMPRESSION_OPTION_NONE, true, contentType);
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
//
|
||||
// Copyright (C) 2019 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
#include "XmlWriter.hpp"
|
||||
#include "BundleManifestWriter.hpp"
|
||||
#include "Crypto.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
|
||||
#include "AppxManifestObject.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
static const char* bundleManifestElement = "Bundle";
|
||||
static const char* schemaVersionAttribute = "SchemaVersion";
|
||||
static const char* Win2019SchemaVersion = "5.0";
|
||||
static const char* identityManifestElement = "Identity";
|
||||
static const char* identityNameAttribute = "Name";
|
||||
static const char* identityPublisherAttribute = "Publisher";
|
||||
static const char* identityVersionAttribute = "Version";
|
||||
static const char* packagesManifestElement = "Packages";
|
||||
static const char* packageManifestElement = "Package";
|
||||
static const char* packageTypeAttribute = "Type";
|
||||
static const char* packageVersionAttribute = "Version";
|
||||
static const char* packageArchitectureAttribute = "Architecture";
|
||||
static const char* packageResourceIdAttribute = "ResourceId";
|
||||
static const char* packageFileNameAttribute = "FileName";
|
||||
static const char* resourcesManifestElement = "Resources";
|
||||
static const char* resourceManifestElement = "Resource";
|
||||
static const char* resourceLanguageAttribute = "Language";
|
||||
static const char* dependenciesManifestElementWithoutPrefix = "Dependencies";
|
||||
static const char* targetDeviceFamilyManifestElementWithoutPrefix = "TargetDeviceFamily";
|
||||
static const char* tdfNameAttribute = "Name";
|
||||
static const char* tdfMinVersionAttribute = "MinVersion";
|
||||
static const char* tdfMaxVersionTestedAttribute = "MaxVersionTested";
|
||||
|
||||
static const char* ApplicationPackageType = "application";
|
||||
static const char* ResourcePackageType = "resource";
|
||||
|
||||
static const char* NamespaceAlias = "b";
|
||||
static const char* Namespace = "http://schemas.microsoft.com/appx/2013/bundle";
|
||||
static const char* Namespace2016Alias = "b2";
|
||||
static const char* Namespace2016 = "http://schemas.microsoft.com/appx/2016/bundle";
|
||||
static const char* Namespace2017Alias = "b3";
|
||||
static const char* Namespace2017 = "http://schemas.microsoft.com/appx/2017/bundle";
|
||||
static const char* Namespace2018Alias = "b4";
|
||||
static const char* Namespace2018 = "http://schemas.microsoft.com/appx/2018/bundle";
|
||||
static const char* Namespace2019Alias = "b5";
|
||||
static const char* Namespace2019 = "http://schemas.microsoft.com/appx/2019/bundle";
|
||||
|
||||
BundleManifestWriter::BundleManifestWriter() : m_xmlWriter(XmlWriter(bundleManifestElement)) {}
|
||||
|
||||
void BundleManifestWriter::StartBundleManifest(std::string targetXmlNamespace, std::string name,
|
||||
std::string publisher, std::uint64_t version)
|
||||
{
|
||||
this->targetXmlNamespace = targetXmlNamespace;
|
||||
StartBundleElement();
|
||||
WriteIdentityElement(name, publisher, version);
|
||||
StartPackagesElement();
|
||||
}
|
||||
|
||||
void BundleManifestWriter::StartBundleElement()
|
||||
{
|
||||
m_xmlWriter.AddAttribute(xmlnsAttribute, this->targetXmlNamespace);
|
||||
m_xmlWriter.AddAttribute(schemaVersionAttribute, Win2019SchemaVersion);
|
||||
|
||||
std::string bundle2018QName = GetQualifiedName(xmlnsAttribute, Namespace2018Alias);
|
||||
m_xmlWriter.AddAttribute(bundle2018QName, Namespace2018);
|
||||
|
||||
std::string bundle2019QName = GetQualifiedName(xmlnsAttribute, Namespace2019Alias);
|
||||
m_xmlWriter.AddAttribute(bundle2019QName, Namespace2019);
|
||||
|
||||
std::string ignorableNamespaces;
|
||||
ignorableNamespaces.append(Namespace2018Alias);
|
||||
ignorableNamespaces.append(" ");
|
||||
ignorableNamespaces.append(Namespace2019Alias);
|
||||
m_xmlWriter.AddAttribute("IgnorableNamespaces", ignorableNamespaces);
|
||||
}
|
||||
|
||||
void BundleManifestWriter::WriteIdentityElement(std::string name, std::string publisher, std::uint64_t version)
|
||||
{
|
||||
m_xmlWriter.StartElement(identityManifestElement);
|
||||
|
||||
m_xmlWriter.AddAttribute(identityNameAttribute, name);
|
||||
m_xmlWriter.AddAttribute(identityPublisherAttribute, publisher);
|
||||
|
||||
std::string versionString = MSIX::ConvertVersionToString(version);
|
||||
m_xmlWriter.AddAttribute(identityVersionAttribute, versionString);
|
||||
|
||||
m_xmlWriter.CloseElement();
|
||||
}
|
||||
|
||||
void BundleManifestWriter::StartPackagesElement()
|
||||
{
|
||||
m_xmlWriter.StartElement(packagesManifestElement);
|
||||
}
|
||||
|
||||
void BundleManifestWriter::WritePackageElement(PackageInfo packageInfo)
|
||||
{
|
||||
m_xmlWriter.StartElement(packageManifestElement);
|
||||
|
||||
std::string packageTypeString;
|
||||
if(packageInfo.type == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION)
|
||||
{
|
||||
packageTypeString = ApplicationPackageType;
|
||||
}
|
||||
else if (packageInfo.type == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_RESOURCE)
|
||||
{
|
||||
packageTypeString = ResourcePackageType;
|
||||
}
|
||||
m_xmlWriter.AddAttribute(packageTypeAttribute, packageTypeString);
|
||||
|
||||
std::string versionString = MSIX::ConvertVersionToString(packageInfo.version);
|
||||
m_xmlWriter.AddAttribute(packageVersionAttribute, versionString);
|
||||
|
||||
if(packageInfo.type == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION)
|
||||
{
|
||||
m_xmlWriter.AddAttribute(packageArchitectureAttribute, packageInfo.architecture);
|
||||
}
|
||||
|
||||
if (!packageInfo.resourceId.empty() && (packageInfo.resourceId.size() > 0))
|
||||
{
|
||||
m_xmlWriter.AddAttribute(packageResourceIdAttribute, packageInfo.resourceId);
|
||||
}
|
||||
|
||||
if(!packageInfo.fileName.empty())
|
||||
{
|
||||
m_xmlWriter.AddAttribute(packageFileNameAttribute, packageInfo.fileName);
|
||||
}
|
||||
|
||||
if(packageInfo.offset > 0)
|
||||
{
|
||||
//TODO: not applicable for flat bundle
|
||||
}
|
||||
|
||||
if (packageInfo.size > 0 && packageInfo.offset > 0)
|
||||
{
|
||||
//TODO: not applicable for flat bundles
|
||||
}
|
||||
|
||||
//WriteResourcesElement
|
||||
WriteResourcesElement(packageInfo.resources.Get());
|
||||
|
||||
//WriteDependenciesElement
|
||||
WriteDependenciesElement(packageInfo.tdfs.Get());
|
||||
|
||||
//End Package Tag
|
||||
m_xmlWriter.CloseElement();
|
||||
}
|
||||
|
||||
void BundleManifestWriter::WriteResourcesElement(IAppxManifestQualifiedResourcesEnumerator* resources)
|
||||
{
|
||||
BOOL hasResources = FALSE;
|
||||
ThrowHrIfFailed(resources->GetHasCurrent(&hasResources));
|
||||
|
||||
if (hasResources)
|
||||
{
|
||||
//Start Resources element
|
||||
m_xmlWriter.StartElement(resourcesManifestElement);
|
||||
|
||||
BOOL hasNext = FALSE;
|
||||
ThrowHrIfFailed(resources->GetHasCurrent(&hasNext));
|
||||
while (hasNext)
|
||||
{
|
||||
ComPtr<IAppxManifestQualifiedResource> resource;
|
||||
ThrowHrIfFailed(resources->GetCurrent(&resource));
|
||||
|
||||
//Start Resource element
|
||||
m_xmlWriter.StartElement(resourceManifestElement);
|
||||
|
||||
auto qualifiedResourceInternal = resource.As<IAppxManifestQualifiedResourceInternal>();
|
||||
std::string languageString = qualifiedResourceInternal->GetLanguage();
|
||||
if (!languageString.empty())
|
||||
{
|
||||
m_xmlWriter.AddAttribute(resourceLanguageAttribute, languageString);
|
||||
}
|
||||
|
||||
//TODO:: Write scale and dxfeaturelevel attributes
|
||||
|
||||
//End Resource element
|
||||
m_xmlWriter.CloseElement();
|
||||
|
||||
ThrowHrIfFailed(resources->MoveNext(&hasNext));
|
||||
}
|
||||
|
||||
//End Resources element
|
||||
m_xmlWriter.CloseElement();
|
||||
}
|
||||
}
|
||||
|
||||
void BundleManifestWriter::WriteDependenciesElement(IAppxManifestTargetDeviceFamiliesEnumerator* tdfs)
|
||||
{
|
||||
BOOL hasNext = FALSE;
|
||||
ThrowHrIfFailed(tdfs->GetHasCurrent(&hasNext));
|
||||
|
||||
if (hasNext)
|
||||
{
|
||||
std::string dependencyQName = GetElementName(Namespace2018, Namespace2018Alias, dependenciesManifestElementWithoutPrefix);
|
||||
m_xmlWriter.StartElement(dependencyQName);
|
||||
|
||||
while (hasNext)
|
||||
{
|
||||
ComPtr<IAppxManifestTargetDeviceFamily> tdf;
|
||||
ThrowHrIfFailed(tdfs->GetCurrent(&tdf));
|
||||
|
||||
//Start TargetDeviceFamily manifest element
|
||||
std::string tdfQName = GetElementName(Namespace2018, Namespace2018Alias, targetDeviceFamilyManifestElementWithoutPrefix);
|
||||
m_xmlWriter.StartElement(tdfQName);
|
||||
|
||||
auto targetDeviceFamilyInternal = tdf.As<IAppxManifestTargetDeviceFamilyInternal>();
|
||||
std::string name = targetDeviceFamilyInternal->GetName();
|
||||
m_xmlWriter.AddAttribute(tdfNameAttribute, name);
|
||||
|
||||
//Get minversion
|
||||
UINT64 minVersion;
|
||||
ThrowHrIfFailed(tdf->GetMinVersion(&minVersion));
|
||||
std::string minVerionString = MSIX::ConvertVersionToString(minVersion);
|
||||
m_xmlWriter.AddAttribute(tdfMinVersionAttribute, minVerionString);
|
||||
|
||||
//Get maxversiontested
|
||||
UINT64 maxVersionTested;
|
||||
ThrowHrIfFailed(tdf->GetMaxVersionTested(&maxVersionTested));
|
||||
std::string maxVersionTestedString = MSIX::ConvertVersionToString(maxVersionTested);
|
||||
m_xmlWriter.AddAttribute(tdfMaxVersionTestedAttribute, maxVersionTestedString);
|
||||
|
||||
//End TargetDeviceFamily manifest element
|
||||
m_xmlWriter.CloseElement();
|
||||
|
||||
ThrowHrIfFailed(tdfs->MoveNext(&hasNext));
|
||||
}
|
||||
|
||||
//End Dependencies Tag
|
||||
m_xmlWriter.CloseElement();
|
||||
}
|
||||
}
|
||||
|
||||
void BundleManifestWriter::EndPackagesElement()
|
||||
{
|
||||
//TODO:: Main state to check if package is added and close only if
|
||||
m_xmlWriter.CloseElement();
|
||||
}
|
||||
|
||||
void BundleManifestWriter::Close()
|
||||
{
|
||||
//Ends Bundle Element
|
||||
m_xmlWriter.CloseElement();
|
||||
}
|
||||
|
||||
std::string BundleManifestWriter::GetElementName(std::string targetNamespace, std::string targetNamespaceAlias, std::string name)
|
||||
{
|
||||
std::string qualifiedName;
|
||||
if ((this->targetXmlNamespace.compare(targetNamespace) != 0) && (!targetNamespaceAlias.empty()))
|
||||
{
|
||||
qualifiedName = GetQualifiedName(targetNamespaceAlias, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
qualifiedName = name;
|
||||
}
|
||||
return qualifiedName;
|
||||
}
|
||||
|
||||
std::string BundleManifestWriter::GetQualifiedName(std::string namespaceAlias, std::string name)
|
||||
{
|
||||
std::string output;
|
||||
output.append(namespaceAlias);
|
||||
output.append(xmlNamespaceDelimiter);
|
||||
output.append(name);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
#include "BundleWriterHelper.hpp"
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
BundleWriterHelper::BundleWriterHelper()
|
||||
{
|
||||
this->bundleVersion = 0;
|
||||
this->hasExternalPackages = false;
|
||||
this->hasDefaultOrNeutralResources = false;
|
||||
}
|
||||
|
||||
std::uint64_t BundleWriterHelper::GetStreamSize(IStream* stream)
|
||||
{
|
||||
STATSTG stat;
|
||||
ThrowHrIfFailed(stream->Stat(&stat, 1));
|
||||
|
||||
return stat.cbSize.QuadPart;
|
||||
}
|
||||
|
||||
void BundleWriterHelper::AddPackage(std::string fileName, IAppxPackageReader* packageReader,
|
||||
std::uint64_t bundleOffset, std::uint64_t packageSize, bool isDefaultApplicableResource)
|
||||
{
|
||||
ComPtr<IAppxManifestPackageId> packageId;
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType = APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION;
|
||||
ComPtr<IAppxManifestQualifiedResourcesEnumerator> resources;
|
||||
ComPtr<IAppxManifestTargetDeviceFamiliesEnumerator> tdfs;
|
||||
|
||||
GetValidatedPackageData(fileName, packageReader, &packageType, &packageId, &resources, &tdfs);
|
||||
|
||||
AddValidatedPackageData(fileName, bundleOffset, packageSize, packageType, packageId,
|
||||
isDefaultApplicableResource, resources.Get(), tdfs.Get());
|
||||
}
|
||||
|
||||
void BundleWriterHelper::GetValidatedPackageData(
|
||||
std::string fileName,
|
||||
IAppxPackageReader* packageReader,
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE* packageType,
|
||||
IAppxManifestPackageId** packageId,
|
||||
IAppxManifestQualifiedResourcesEnumerator** resources,
|
||||
IAppxManifestTargetDeviceFamiliesEnumerator** tdfs)
|
||||
{
|
||||
*packageId = nullptr;
|
||||
*resources = nullptr;
|
||||
*tdfs = nullptr;
|
||||
|
||||
ComPtr<IAppxManifestPackageId> loadedPackageId;
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE loadedPackageType = APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION;
|
||||
ComPtr<IAppxManifestQualifiedResourcesEnumerator> loadedResources;
|
||||
ComPtr<IAppxManifestTargetDeviceFamiliesEnumerator> loadedTdfs;
|
||||
|
||||
ComPtr<IAppxManifestReader> manifestReader;
|
||||
ThrowHrIfFailed(packageReader->GetManifest(&manifestReader));
|
||||
ThrowHrIfFailed(manifestReader->GetPackageId(&loadedPackageId));
|
||||
|
||||
ComPtr<IAppxManifestReader3> manifestReader3;
|
||||
ThrowHrIfFailed(manifestReader->QueryInterface(UuidOfImpl<IAppxManifestReader3>::iid, reinterpret_cast<void**>(&manifestReader3)));
|
||||
|
||||
ThrowHrIfFailed(manifestReader3->GetQualifiedResources(&loadedResources));
|
||||
|
||||
ThrowHrIfFailed(manifestReader3->GetTargetDeviceFamilies(&loadedTdfs));
|
||||
|
||||
loadedPackageType = GetPayloadPackageType(manifestReader.Get(), fileName);
|
||||
//TODO:: Validate Package matches SHA256 hash method
|
||||
|
||||
auto packageIdInternal = loadedPackageId.As<IAppxManifestPackageIdInternal>();
|
||||
ValidateNameAndPublisher(packageIdInternal.Get(), fileName);
|
||||
|
||||
//TODO: TDF checks
|
||||
|
||||
if (loadedPackageType == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION)
|
||||
{
|
||||
ValidateApplicationElement(manifestReader.Get(), fileName);
|
||||
}
|
||||
|
||||
*packageType = loadedPackageType;
|
||||
*packageId = loadedPackageId.Detach();
|
||||
*resources = loadedResources.Detach();
|
||||
|
||||
if (loadedTdfs.Get() != nullptr)
|
||||
{
|
||||
*tdfs = loadedTdfs.Detach();
|
||||
}
|
||||
}
|
||||
|
||||
void BundleWriterHelper::ValidateApplicationElement(
|
||||
IAppxManifestReader* packageManifestReader,
|
||||
std::string fileName)
|
||||
{
|
||||
ComPtr<IAppxManifestReader4> manifestReader4;
|
||||
ThrowHrIfFailed(packageManifestReader->QueryInterface(UuidOfImpl<IAppxManifestReader4>::iid, reinterpret_cast<void**>(&manifestReader4)));
|
||||
|
||||
ComPtr<IAppxManifestOptionalPackageInfo> optionalPackageInfo;
|
||||
ThrowHrIfFailed(manifestReader4->GetOptionalPackageInfo(&optionalPackageInfo));
|
||||
|
||||
BOOL packageIsOptional = FALSE;
|
||||
ThrowHrIfFailed(optionalPackageInfo->GetIsOptionalPackage(&packageIsOptional));
|
||||
|
||||
if (!packageIsOptional) // optional payload packages are not required to declare any <Application> elements
|
||||
{
|
||||
ComPtr<IAppxManifestApplicationsEnumerator> applications;
|
||||
ThrowHrIfFailed(packageManifestReader->GetApplications(&applications));
|
||||
BOOL hasApplication = FALSE;
|
||||
ThrowHrIfFailed(applications->GetHasCurrent(&hasApplication));
|
||||
|
||||
if (!hasApplication)
|
||||
{
|
||||
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because its manifest does not declare any Application elements.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BundleWriterHelper::ValidateNameAndPublisher(IAppxManifestPackageIdInternal* packageId,
|
||||
std::string filename)
|
||||
{
|
||||
if(this->mainPackageName.empty())
|
||||
{
|
||||
this->mainPackageName = packageId->GetName();
|
||||
this->mainPackagePublisher = packageId->GetPublisher();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string packageName = packageId->GetName();
|
||||
|
||||
if ((this->mainPackageName.compare(packageName)) != 0)
|
||||
{
|
||||
std::string packageFullName = packageId->GetPackageFullName();
|
||||
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because it has a different package family name than other packages in the bundle.");
|
||||
}
|
||||
|
||||
std::string publisherName = packageId->GetPublisher();
|
||||
if ((this->mainPackagePublisher.compare(publisherName)) != 0)
|
||||
{
|
||||
std::string packageFullName = packageId->GetPackageFullName();
|
||||
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because it has a different package family name than other packages in the bundle.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE BundleWriterHelper::GetPayloadPackageType(IAppxManifestReader* packageManifestReader,
|
||||
std::string fileName)
|
||||
{
|
||||
ComPtr<IAppxManifestProperties> packageProperties;
|
||||
ThrowHrIfFailed(packageManifestReader->GetProperties(&packageProperties));
|
||||
|
||||
BOOL isFrameworkPackage = FALSE;
|
||||
ThrowHrIfFailed(packageProperties->GetBoolValue(L"Framework", &isFrameworkPackage));
|
||||
|
||||
if (isFrameworkPackage)
|
||||
{
|
||||
ThrowErrorAndLog(Error::AppxManifestSemanticError, "The package is not valid in the bundle because it is a framework package.");
|
||||
}
|
||||
|
||||
BOOL isResourcePackage = FALSE;
|
||||
ThrowHrIfFailed(packageProperties->GetBoolValue(L"ResourcePackage", &isResourcePackage));
|
||||
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType = (isResourcePackage ? APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_RESOURCE : APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION);
|
||||
return packageType;
|
||||
}
|
||||
|
||||
void BundleWriterHelper::AddValidatedPackageData(
|
||||
std::string fileName,
|
||||
std::uint64_t bundleOffset,
|
||||
std::uint64_t packageSize,
|
||||
APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType,
|
||||
ComPtr<IAppxManifestPackageId> packageId,
|
||||
bool isDefaultApplicablePackage,
|
||||
IAppxManifestQualifiedResourcesEnumerator* resources,
|
||||
IAppxManifestTargetDeviceFamiliesEnumerator* tdfs)
|
||||
{
|
||||
//TODO: validate package payload extension
|
||||
|
||||
auto innerPackageIdInternal = packageId.As<IAppxManifestPackageIdInternal>();
|
||||
|
||||
PackageInfo packageInfo;
|
||||
packageInfo.type = packageType;
|
||||
packageInfo.architecture = innerPackageIdInternal->GetArchitecture();
|
||||
UINT64 version;
|
||||
ThrowHrIfFailed(packageId->GetVersion(&version));
|
||||
packageInfo.version = version;
|
||||
packageInfo.resourceId = innerPackageIdInternal->GetResourceId();
|
||||
packageInfo.isDefaultApplicablePackage = isDefaultApplicablePackage;
|
||||
packageInfo.resources = resources;
|
||||
packageInfo.fileName = fileName;
|
||||
packageInfo.size = packageSize;
|
||||
packageInfo.offset = bundleOffset;
|
||||
packageInfo.tdfs = tdfs;
|
||||
|
||||
AddPackageInfoToVector(this->payloadPackages, packageInfo);
|
||||
}
|
||||
|
||||
void BundleWriterHelper::AddPackageInfoToVector(std::vector<PackageInfo>& packagesVector,
|
||||
PackageInfo packageInfo)
|
||||
{
|
||||
packagesVector.push_back(packageInfo);
|
||||
|
||||
if (packageInfo.offset == 0)
|
||||
{
|
||||
this->hasExternalPackages = true;
|
||||
}
|
||||
|
||||
if (packageInfo.isDefaultApplicablePackage)
|
||||
{
|
||||
this->hasDefaultOrNeutralResources = true;
|
||||
}
|
||||
|
||||
BOOL hasResources = FALSE;
|
||||
ThrowHrIfFailed(packageInfo.resources->GetHasCurrent(&hasResources));
|
||||
if (!hasResources)
|
||||
{
|
||||
this->hasDefaultOrNeutralResources = true;
|
||||
}
|
||||
}
|
||||
|
||||
void BundleWriterHelper::EndBundleManifest()
|
||||
{
|
||||
std::string targetXmlNamespace = "http://schemas.microsoft.com/appx/2013/bundle";
|
||||
bool isPre2018BundleManifest = true;
|
||||
|
||||
//TODO: Only use new 2018 bundle schema if the bundle contains more than 1 neutral app packages
|
||||
if (this->hasDefaultOrNeutralResources)
|
||||
{
|
||||
targetXmlNamespace = "http://schemas.microsoft.com/appx/2017/bundle";
|
||||
}
|
||||
else if ((this->optionalBundles.size() > 0) || this->hasExternalPackages)
|
||||
{
|
||||
targetXmlNamespace = "http://schemas.microsoft.com/appx/2016/bundle";
|
||||
}
|
||||
|
||||
m_bundleManifestWriter.StartBundleManifest(targetXmlNamespace, this->mainPackageName,
|
||||
this->mainPackagePublisher, this->bundleVersion);
|
||||
|
||||
for(std::size_t i = 0; i < this->payloadPackages.size(); i++)
|
||||
{
|
||||
m_bundleManifestWriter.WritePackageElement(payloadPackages[i]);
|
||||
}
|
||||
|
||||
//TODO: this->OptionalBundles
|
||||
|
||||
//Ends Packages and bundle Element
|
||||
m_bundleManifestWriter.EndPackagesElement();
|
||||
m_bundleManifestWriter.Close();
|
||||
}
|
||||
|
||||
}
|
|
@ -135,4 +135,22 @@ namespace MSIX {
|
|||
// TODO: add other ones if needed, otherwise throw
|
||||
ThrowErrorAndLog(Error::NotSupported, "Payload file content type not found");
|
||||
}
|
||||
|
||||
const std::string ContentType::GetBundlePayloadFileContentType(APPX_BUNDLE_FOOTPRINT_FILE_TYPE footprintFile)
|
||||
{
|
||||
if (footprintFile == APPX_BUNDLE_FOOTPRINT_FILE_TYPE_MANIFEST)
|
||||
{
|
||||
return "application/vnd.ms-appx.bundlemanifest+xml";
|
||||
}
|
||||
if (footprintFile == APPX_BUNDLE_FOOTPRINT_FILE_TYPE_BLOCKMAP)
|
||||
{
|
||||
return "application/vnd.ms-appx.blockmap+xml";
|
||||
}
|
||||
if (footprintFile == APPX_BUNDLE_FOOTPRINT_FILE_TYPE_SIGNATURE)
|
||||
{
|
||||
return "application/vnd.ms-appx.signature";
|
||||
}
|
||||
// TODO: add other ones if needed, otherwise throw
|
||||
ThrowErrorAndLog(Error::NotSupported, "Bundle Payload file content type not found");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#include "VersionHelpers.hpp"
|
||||
|
||||
namespace MSIX{
|
||||
|
||||
std::uint64_t ConvertVersionStringToUint64(const std::string& versionString)
|
||||
{
|
||||
std::uint64_t version = 0;
|
||||
size_t position = 0;
|
||||
auto nextPeriod = versionString.find('.', position);
|
||||
version = (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod)) << 0x30;
|
||||
|
||||
position = nextPeriod + 1;
|
||||
nextPeriod = versionString.find('.', position);
|
||||
version += (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod)) << 0x20;
|
||||
|
||||
position = nextPeriod + 1;
|
||||
nextPeriod = versionString.find('.', position);
|
||||
version += (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod)) << 0x10;
|
||||
|
||||
position = nextPeriod + 1;
|
||||
nextPeriod = versionString.find('.', position);
|
||||
version += (std::uint64_t)std::stoi(versionString.substr(position, nextPeriod));
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
std::string ConvertVersionToString(std::uint64_t version)
|
||||
{
|
||||
return std::to_string((version >> 0x30) & 0xFFFF) + "."
|
||||
+ std::to_string((version >> 0x20) & 0xFFFF) + "."
|
||||
+ std::to_string((version >> 0x10) & 0xFFFF) + "."
|
||||
+ std::to_string((version) & 0xFFFF);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче