Update about Manager.

This commit is contained in:
Bruce
2026-01-27 22:47:49 +08:00
parent 5a404a55e3
commit 0c87a2cdcd
52 changed files with 9170 additions and 201 deletions

View File

@@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Reflection;
namespace AppxPackage
{
[Serializable]
@@ -229,4 +231,31 @@ namespace AppxPackage.Info
return a._value < value;
}
}
public static class JSHelper
{
public static void CallJS (object jsFunc, params object [] args)
{
if (jsFunc == null) return;
try
{
// 这里固定第一个参数为 thisArg比如 1
object [] realArgs = new object [args.Length + 1];
realArgs [0] = jsFunc; // thisArg
Array.Copy (args, 0, realArgs, 1, args.Length);
jsFunc.GetType ().InvokeMember (
"call",
BindingFlags.InvokeMethod,
null,
jsFunc,
realArgs
);
}
catch
{
// ignore errors in callback invocation
}
}
}
}

View File

@@ -7,6 +7,8 @@ using System.Text;
using AppxPackage.Info;
using NativeWrappers;
using System.IO;
using System.Threading;
//using PriFormat;
namespace AppxPackage
{
public static class DataUrlHelper
@@ -543,13 +545,14 @@ namespace AppxPackage
private MRApplication ReadSingleApplication (IntPtr hKeyValues)
{
var app = new MRApplication (ref m_hReader, ref m_pri, ref m_usePri, ref m_enablePri, m_reader.Value.FileRoot);
uint pairCount = (uint)Marshal.ReadInt32 (hKeyValues);
int baseOffset = Marshal.SizeOf (typeof (uint));
int pairSize = Marshal.SizeOf (typeof (PackageReadHelper.PAIR_PVOID));
for (int j = 0; j < pairCount; j++)
int pairCount = Marshal.ReadInt32 (hKeyValues);
IntPtr arrayBase = IntPtr.Add (hKeyValues, sizeof (uint));
for (int i = 0; i < pairCount; i++)
{
IntPtr pPair = IntPtr.Add (hKeyValues, baseOffset + j * pairSize);
var pair = (PackageReadHelper.PAIR_PVOID)Marshal.PtrToStructure (pPair, typeof (PackageReadHelper.PAIR_PVOID));
IntPtr pPairPtr = Marshal.ReadIntPtr (arrayBase, i * IntPtr.Size);
if (pPairPtr == IntPtr.Zero) continue;
PackageReadHelper.PAIR_PVOID pair =
(PackageReadHelper.PAIR_PVOID)Marshal.PtrToStructure (pPairPtr, typeof (PackageReadHelper.PAIR_PVOID));
if (pair.lpKey == IntPtr.Zero) continue;
string key = Marshal.PtrToStringUni (pair.lpKey);
if (string.IsNullOrEmpty (key)) continue;
@@ -849,12 +852,12 @@ namespace AppxPackage
private string m_filePath = string.Empty;
private bool m_usePRI = false;
private bool m_enablePRI = false;
private PriReader m_pri = new PriReader ();
private PriReader m_pri = null;
public IntPtr Instance => m_hReader;
public string FileRoot{ get { return Path.GetPathRoot (m_filePath); } }
private void InitPri ()
{
m_pri.Dispose ();
m_pri?.Dispose ();
if (!m_usePRI) return;
#region Get PRI IStream
switch (Type)
@@ -862,45 +865,12 @@ namespace AppxPackage
case PackageType.Appx:
{
var pripath = Path.Combine (FileRoot, "resources.pri");
m_pri.Create (pripath);
m_pri = new PriReader (pripath);
}
break;
}
#endregion
try
{
var resnames = new HashSet<string> ();
using (var prop = Properties)
{
var temp = prop.Description;
if (PriFileHelper.IsMsResourcePrefix (temp)) resnames.Add (temp);
temp = prop.DisplayName;
if (PriFileHelper.IsMsResourcePrefix (temp)) resnames.Add (temp);
temp = prop.Publisher;
if (PriFileHelper.IsMsResourcePrefix (temp)) resnames.Add (temp);
resnames.Add (prop.Logo);
}
using (var apps = Applications)
{
foreach (var app in apps)
{
foreach (var pair in app)
{
foreach (var pathres in ConstData.FilePathItems)
{
if ((pathres?.Trim ()?.ToLower () ?? "") == (pair.Key?.Trim ()?.ToLower ()))
{
resnames.Add (pair.Value);
}
else if (PriFileHelper.IsMsResourcePrefix (pair.Value))
resnames.Add (pair.Value);
}
}
}
}
m_pri.AddSearch (resnames);
}
catch (Exception) { }
return;
}
public PackageType Type
{
@@ -979,6 +949,25 @@ namespace AppxPackage
Newtonsoft.Json.Formatting.Indented
);
}
public void BuildJsonTextAsync (object callback)
{
if (callback == null) return;
Thread thread = new Thread (() => {
string json = string.Empty;
try
{
json = BuildJsonText ();
}
catch
{
json = string.Empty;
}
JSHelper.CallJS (callback, json);
});
thread.SetApartmentState (ApartmentState.MTA);
thread.IsBackground = true;
thread.Start ();
}
private object BuildJsonObject ()
{
return new

View File

@@ -4,8 +4,10 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using AppxPackage.Info;
using NativeWrappers;
//using PriFormat;
namespace AppxPackage
{
internal static partial class ConstData
@@ -220,6 +222,9 @@ namespace AppxPackage
if (hr.Succeeded) return ret != 0;
else return defaultValue;
}
#if DEBUG
public string Debug_StringValue (string attr) => StringValue (attr);
#endif
protected string StringResValue (string attr)
{
var res = StringValue (attr);
@@ -237,6 +242,7 @@ namespace AppxPackage
protected string PathResValue (string attr)
{
var res = StringValue (attr);
//var id = new PriFormat.PriResourceIdentifier (res);
try
{
if (m_usePri && m_enablePri)
@@ -267,7 +273,7 @@ namespace AppxPackage
{
IntPtr base64Head = IntPtr.Zero;
var base64s = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
//if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
return PackageReadHelper.GetStringAndFreeFromPkgRead (base64s);
}
catch (Exception) { return ""; }
@@ -288,7 +294,7 @@ namespace AppxPackage
pic = PackageReadHelper.GetFileFromPayloadPackage (pkg, logopath);
IntPtr base64Head = IntPtr.Zero;
var lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
//if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
if (!(lpstr != IntPtr.Zero && !string.IsNullOrEmpty (PackageReadHelper.GetStringFromPkgRead (lpstr))))
{
if (lpstr != IntPtr.Zero) PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr);
@@ -300,7 +306,7 @@ namespace AppxPackage
{
pic1 = PackageReadHelper.GetFileFromPayloadPackage (pkg1, logopath);
lpstr = PackageReadHelper.StreamToBase64W (pic1, null, 0, out base64Head);
if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
//if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
}
}
catch (Exception) { }
@@ -478,11 +484,7 @@ namespace AppxPackage
pic = PackageReadHelper.GetAppxFileFromAppxPackage (m_hReader, value);
IntPtr base64Head = IntPtr.Zero;
IntPtr lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
if (base64Head != IntPtr.Zero)
{
PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head);
base64Head = IntPtr.Zero;
}
//if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
return lpstr != IntPtr.Zero
? PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr)
: "";
@@ -508,16 +510,12 @@ namespace AppxPackage
{
IntPtr header = IntPtr.Zero;
PackageReadHelper.GetSuitablePackageFromBundle (m_hReader, out header, out pkg);
if (header != IntPtr.Zero) PackageReadHelper.GetStringAndFreeFromPkgRead (header);
//if (header != IntPtr.Zero) PackageReadHelper.GetStringAndFreeFromPkgRead (header);
header = IntPtr.Zero;
pic = PackageReadHelper.GetFileFromPayloadPackage (pkg, value);
IntPtr base64Head = IntPtr.Zero;
IntPtr lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
if (base64Head != IntPtr.Zero)
{
PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head);
base64Head = IntPtr.Zero;
}
//if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
return lpstr != IntPtr.Zero
? PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr)
: "";
@@ -667,13 +665,14 @@ namespace AppxPackage
private PRApplication ReadSingleApplication (IntPtr hKeyValues)
{
var app = new PRApplication (ref m_hReader, ref m_priBundle, ref m_usePri, ref m_enablePri);
uint pairCount = (uint)Marshal.ReadInt32 (hKeyValues);
int baseOffset = Marshal.SizeOf (typeof (uint));
int pairSize = Marshal.SizeOf (typeof (PackageReadHelper.PAIR_PVOID));
for (int j = 0; j < pairCount; j++)
int pairCount = Marshal.ReadInt32 (hKeyValues);
IntPtr arrayBase = IntPtr.Add (hKeyValues, sizeof (uint));
for (int i = 0; i < pairCount; i++)
{
IntPtr pPair = IntPtr.Add (hKeyValues, baseOffset + j * pairSize);
var pair = (PackageReadHelper.PAIR_PVOID) Marshal.PtrToStructure (pPair, typeof (PackageReadHelper.PAIR_PVOID));
IntPtr pPairPtr = Marshal.ReadIntPtr (arrayBase, i * IntPtr.Size);
if (pPairPtr == IntPtr.Zero) continue;
PackageReadHelper.PAIR_PVOID pair =
(PackageReadHelper.PAIR_PVOID)Marshal.PtrToStructure (pPairPtr, typeof (PackageReadHelper.PAIR_PVOID));
if (pair.lpKey == IntPtr.Zero) continue;
string key = Marshal.PtrToStringUni (pair.lpKey);
if (string.IsNullOrEmpty (key)) continue;
@@ -1064,41 +1063,11 @@ namespace AppxPackage
} break;
}
#endregion
try
{
var resnames = new HashSet<string> ();
using (var prop = Properties)
{
var temp = prop.Description;
if (PriFileHelper.IsMsResourcePrefix (temp)) resnames.Add (temp);
temp = prop.DisplayName;
if (PriFileHelper.IsMsResourcePrefix (temp)) resnames.Add (temp);
temp = prop.Publisher;
if (PriFileHelper.IsMsResourcePrefix (temp)) resnames.Add (temp);
resnames.Add (prop.Logo);
}
using (var apps = Applications)
{
foreach (var app in apps)
{
foreach (var pair in app)
{
foreach (var pathres in ConstData.FilePathItems)
{
if ((pathres?.Trim ()?.ToLower () ?? "") == (pair.Key?.Trim ()?.ToLower ()))
{
resnames.Add (pair.Value);
}
else if (PriFileHelper.IsMsResourcePrefix (pair.Value))
resnames.Add (pair.Value);
}
}
}
}
m_priBundle.AddSearch (resnames);
}
catch (Exception) { }
return;
}
#if DEBUG
public PriReaderBundle PriInstance => m_priBundle;
#endif
public PackageType Type
{
get
@@ -1176,6 +1145,26 @@ namespace AppxPackage
Newtonsoft.Json.Formatting.Indented
);
}
public void BuildJsonTextAsync (object callback)
{
if (callback == null) return;
Thread thread = new Thread (() =>
{
string json = string.Empty;
try
{
json = BuildJsonText ();
}
catch
{
json = string.Empty;
}
JSHelper.CallJS (callback, json);
});
thread.SetApartmentState (ApartmentState.MTA);
thread.IsBackground = true;
thread.Start ();
}
private object BuildJsonObject ()
{
return new

View File

@@ -401,6 +401,8 @@ namespace NativeWrappers
);
[DllImport (DllName, CallingConvention = CallConv)]
public static extern void PackageReaderFreeString (IntPtr p);
[DllImport (DllName, CallingConvention = CallConv, CharSet = CharSet.Unicode)]
public static extern IntPtr GetManifestPrerequistieSystemVersionName (IntPtr hReader, string lpName);
}
}

View File

@@ -58,7 +58,7 @@ namespace AppxPackage
[return: MarshalAs (UnmanagedType.Bool)]
public static extern bool IsMsResourceUri ([MarshalAs (UnmanagedType.LPWStr)] string pResUri);
[DllImport (DLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void PriFormatFreeString (IntPtr ptr);
private static extern void PriFormatFreeString (IntPtr ptr);
public static string PtrToString (IntPtr ptr)
{
if (ptr == IntPtr.Zero) return null;
@@ -66,8 +66,6 @@ namespace AppxPackage
PriFormatFreeString (ptr); // 如果 DLL 返回的内存要求 free
return s;
}
[DllImport (DLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void FreePriString (IntPtr p);
}
public static class LpcwstrListHelper
{

View File

@@ -1,9 +1,11 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace AppxPackage
{
@@ -75,6 +77,7 @@ namespace AppxPackage
public void AddSearch (string uri) { AddSearch (new string [] { uri }); }
public string Resource (string resName)
{
var task = Task.Factory.StartNew (() => {
IntPtr ret = IntPtr.Zero;
try
{
@@ -84,8 +87,10 @@ namespace AppxPackage
}
finally
{
if (ret != IntPtr.Zero) PriFileHelper.FreePriString (ret);
//if (ret != IntPtr.Zero) PriFileHelper.FreePriString(ret);
}
});
return task.Result;
}
public Dictionary<string, string> Resources (IEnumerable<string> resnames)
{

View File

@@ -12,6 +12,7 @@ using System.Drawing;
using Newtonsoft.Json;
using AppxPackage;
using ModernNotice;
using System.Threading;
namespace Bridge
{
@@ -494,10 +495,183 @@ namespace Bridge
[ClassInterface (ClassInterfaceType.AutoDual)]
public class _I_Package
{
private static void CallJS (object jsFunc, params object [] args)
{
if (jsFunc == null) return;
try
{
// 这里固定第一个参数为 thisArg比如 1
object [] realArgs = new object [args.Length + 1];
realArgs [0] = jsFunc; // thisArg
Array.Copy (args, 0, realArgs, 1, args.Length);
jsFunc.GetType ().InvokeMember (
"call",
BindingFlags.InvokeMethod,
null,
jsFunc,
realArgs
);
}
catch
{
// ignore errors in callback invocation
}
}
public AppxPackage.PackageReader Reader (string packagePath) { return new AppxPackage.PackageReader (packagePath); }
public _I_PackageManager Manager => new _I_PackageManager ();
public AppxPackage.ManifestReader Manifest (string manifestPath) { return new AppxPackage.ManifestReader (manifestPath); }
public AppxPackage.ManifestReader FromInstallLocation (string installLocation) { return Manifest (Path.Combine (installLocation, "AppxManifest.xml")); }
public void ReadFromPackageAsync (string packagePath, bool enablePri, object successCallback, object failedCallback)
{
Thread thread = new Thread (() => {
try
{
using (var reader = Reader (packagePath))
{
if (enablePri)
{
reader.EnablePri = true;
reader.UsePri = true;
}
if (!reader.IsValid)
{
var failObj = new
{
status = false,
message = "Reader invalid",
jsontext = ""
};
string failJson = Newtonsoft.Json.JsonConvert.SerializeObject (failObj);
if (failedCallback != null) CallJS (failedCallback, failJson);
return;
}
var obj = new
{
status = true,
message = "ok",
jsontext = reader.BuildJsonText () // 你之前写好的函数
};
string json = Newtonsoft.Json.JsonConvert.SerializeObject (obj);
if (successCallback != null) CallJS (successCallback, json);
}
}
catch (Exception ex)
{
var errObj = new
{
status = false,
message = ex.Message,
jsontext = ""
};
string errJson = Newtonsoft.Json.JsonConvert.SerializeObject (errObj);
if (failedCallback != null) CallJS (failedCallback, errJson);
}
});
thread.IsBackground = true;
thread.SetApartmentState (ApartmentState.MTA);
thread.Start ();
}
public void ReadFromManifestAsync (string manifestPath, bool enablePri, object successCallback, object failedCallback)
{
Thread thread = new Thread (() => {
try
{
using (var reader = Manifest (manifestPath))
{
if (enablePri)
{
reader.EnablePri = true;
reader.UsePri = true;
}
if (!reader.IsValid)
{
var failObj = new
{
status = false,
message = "Reader invalid",
jsontext = ""
};
string failJson = Newtonsoft.Json.JsonConvert.SerializeObject (failObj);
if (failedCallback != null) CallJS (failedCallback, failJson);
return;
}
var obj = new
{
status = true,
message = "ok",
jsontext = reader.BuildJsonText () // 你之前写好的函数
};
string json = Newtonsoft.Json.JsonConvert.SerializeObject (obj);
if (successCallback != null) CallJS (successCallback, json);
}
}
catch (Exception ex)
{
var errObj = new
{
status = false,
message = ex.Message,
jsontext = ""
};
string errJson = Newtonsoft.Json.JsonConvert.SerializeObject (errObj);
if (failedCallback != null) CallJS (failedCallback, errJson);
}
});
thread.IsBackground = true;
thread.SetApartmentState (ApartmentState.MTA);
thread.Start ();
}
public void ReadFromInstallLocationAsync (string installLocation, bool enablePri, object successCallback, object failedCallback)
{
Thread thread = new Thread (() => {
try
{
using (var reader = FromInstallLocation (installLocation))
{
if (enablePri)
{
reader.EnablePri = true;
reader.UsePri = true;
}
if (!reader.IsValid)
{
var failObj = new
{
status = false,
message = "Reader invalid",
jsontext = ""
};
string failJson = Newtonsoft.Json.JsonConvert.SerializeObject (failObj);
if (failedCallback != null) CallJS (failedCallback, failJson);
return;
}
var obj = new
{
status = true,
message = "ok",
jsontext = reader.BuildJsonText () // 你之前写好的函数
};
string json = Newtonsoft.Json.JsonConvert.SerializeObject (obj);
if (successCallback != null) CallJS (successCallback, json);
}
}
catch (Exception ex)
{
var errObj = new
{
status = false,
message = ex.Message,
jsontext = ""
};
string errJson = Newtonsoft.Json.JsonConvert.SerializeObject (errObj);
if (failedCallback != null) CallJS (failedCallback, errJson);
}
});
thread.IsBackground = true;
thread.SetApartmentState (ApartmentState.MTA);
thread.Start ();
}
}
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDual)]

View File

@@ -93,10 +93,6 @@
<Project>{ffd3fd52-37a8-4f43-883c-de8d996cb0e0}</Project>
<Name>DataUtils</Name>
</ProjectReference>
<ProjectReference Include="..\PriFileFormat\PriFile.csproj">
<Project>{ef4012d4-ef08-499c-b803-177739350b2d}</Project>
<Name>PriFile</Name>
</ProjectReference>
<ProjectReference Include="..\PrivateInit\PrivateInit.csproj">
<Project>{8e708d9a-6325-4aa9-b5a5-d1b5eca8eef7}</Project>
<Name>PrivateInit</Name>

View File

@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
namespace Manager
{
public partial class ManagerShell: WAShell.WebAppForm
@@ -65,10 +66,6 @@ namespace Manager
{
var root = Path.GetDirectoryName (DataUtils.Utilities.GetCurrentProgramPath ());
WebUI.Navigate (Path.Combine (root, "html\\manager.html"));
var pkg = new AppxPackage.PackageReader (@"F:\新建文件夹 (2)\9E2F88E3.Twitter_1.1.13.8_x86__wgeqdkkx372wm.appx");
pkg.EnablePri = true;
pkg.UsePri = true;
var displayName = pkg.Properties.LogoBase64;
}
private void ManagerShell_Resize (object sender, EventArgs e)
{

View File

@@ -8,6 +8,7 @@ namespace PriFileFormat
public class ComStreamWrapper: Stream
{
private IStream comStream;
private object _sync = new object ();
public ComStreamWrapper (IStream stream)
{
if (stream == null)
@@ -29,15 +30,20 @@ namespace PriFileFormat
public override long Length
{
get
{
lock (_sync)
{
System.Runtime.InteropServices.ComTypes.STATSTG stat;
comStream.Stat (out stat, 1); // STATFLAG_NONAME = 1
return stat.cbSize;
}
}
}
public override long Position
{
get
{
lock (_sync)
{
IntPtr posPtr = Marshal.AllocHGlobal (sizeof (long));
try
@@ -51,17 +57,26 @@ namespace PriFileFormat
Marshal.FreeHGlobal (posPtr);
}
}
}
set
{
lock (_sync)
{
// SEEK_SET = 0
comStream.Seek (value, 0, IntPtr.Zero);
}
}
}
public override void Flush ()
{
lock (_sync)
{
comStream.Commit (0); // STGC_DEFAULT = 0
}
}
public override int Read (byte [] buffer, int offset, int count)
{
lock (_sync)
{
if (offset != 0)
throw new NotSupportedException ("Offset != 0 not supported in this wrapper.");
@@ -77,7 +92,10 @@ namespace PriFileFormat
Marshal.FreeHGlobal (bytesRead);
}
}
}
public override void Write (byte [] buffer, int offset, int count)
{
lock (_sync)
{
if (offset != 0)
throw new NotSupportedException ("Offset != 0 not supported in this wrapper.");
@@ -92,7 +110,10 @@ namespace PriFileFormat
Marshal.FreeHGlobal (bytesWritten);
}
}
}
public override long Seek (long offset, SeekOrigin origin)
{
lock (_sync)
{
int originInt = 0;
switch (origin)
@@ -113,10 +134,14 @@ namespace PriFileFormat
Marshal.FreeHGlobal (posPtr);
}
}
}
public override void SetLength (long value)
{
lock (_sync)
{
comStream.SetSize (value);
}
}
~ComStreamWrapper () { comStream = null;}
}
}

View File

@@ -25,10 +25,11 @@ namespace PriFileFormat
return priFile;
}
public static PriFile Parse (System.Runtime.InteropServices.ComTypes.IStream stream)
public static PriFile Parse (System.Runtime.InteropServices.ComTypes.IStream stream, out Stream output)
{
ComStreamWrapper csw = new ComStreamWrapper (stream);
output = csw;
PriFile priFile = new PriFile ();
priFile.ParseInternal (csw, true);
return priFile;

BIN
PriFormat.zip Normal file

Binary file not shown.

22
PriFormat/ByteSpan.cs Normal file
View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PriFormat
{
public struct ByteSpan
{
public long Offset { get; private set; }
public uint Length { get; private set; }
public ByteSpan (long offset, uint length)
{
Offset = offset;
Length = length;
}
public override string ToString ()
{
return "ByteSpan | Offset = " + Offset + "\t, Length = " + Length;
}
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
namespace PriFormat
{
public class DataItemSection: Section
{
public IList<ByteSpan> DataItems { get; private set; }
internal const string Identifier = "[mrm_dataitem] \0";
internal DataItemSection (PriFile priFile)
: base (Identifier, priFile)
{
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
long sectionPosition = 0;
SubStream sub = binaryReader.BaseStream as SubStream;
if (sub != null)
sectionPosition = sub.Position;
binaryReader.ExpectUInt32 (0);
ushort numStrings = binaryReader.ReadUInt16 ();
ushort numBlobs = binaryReader.ReadUInt16 ();
uint totalDataLength = binaryReader.ReadUInt32 ();
List<ByteSpan> dataItems = new List<ByteSpan> (numStrings + numBlobs);
long dataStartOffset =
binaryReader.BaseStream.Position +
numStrings * 2 * sizeof (ushort) +
numBlobs * 2 * sizeof (uint);
for (int i = 0; i < numStrings; i++)
{
ushort stringOffset = binaryReader.ReadUInt16 ();
ushort stringLength = binaryReader.ReadUInt16 ();
dataItems.Add (new ByteSpan (sectionPosition + dataStartOffset + stringOffset, stringLength));
}
for (int i = 0; i < numBlobs; i++)
{
uint blobOffset = binaryReader.ReadUInt32 ();
uint blobLength = binaryReader.ReadUInt32 ();
dataItems.Add (new ByteSpan (sectionPosition + dataStartOffset + blobOffset, blobLength));
}
DataItems = new ReadOnlyCollection<ByteSpan> (dataItems);
return true;
}
public override void Dispose ()
{
DataItems?.Clear ();
DataItems = null;
base.Dispose ();
}
}
public class DataItemRef
{
internal SectionRef<DataItemSection> dataItemSection;
internal int itemIndex;
public SectionRef<DataItemSection> DataItemSection => dataItemSection;
public int ItemIndex => itemIndex;
internal DataItemRef (SectionRef<DataItemSection> dataItemSection, int itemIndex)
{
this.dataItemSection = dataItemSection;
this.itemIndex = itemIndex;
}
public override string ToString ()
{
return string.Format ("Data item {0} in section {1}", itemIndex, dataItemSection.SectionIndex);
}
}
}

819
PriFormat/Datas.cs Normal file
View File

@@ -0,0 +1,819 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace PriFormat
{
public struct SectionRef<T> where T : Section
{
internal int SectionIndex;
internal SectionRef (int sectionIndex)
{
SectionIndex = sectionIndex;
}
public override string ToString ()
{
return "Section " + typeof (T).Name + " at index " + SectionIndex;
}
}
public static class LocaleExt
{
// GetLocaleInfoW for LCID-based queries
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetLocaleInfoW (int Locale, int LCType, [Out] StringBuilder lpLCData, int cchData);
// GetLocaleInfoEx for locale name based queries
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetLocaleInfoEx (string lpLocaleName, int LCType, [Out] StringBuilder lpLCData, int cchData);
// LocaleNameToLCID - available on Vista+; fallback is to use CultureInfo
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int LocaleNameToLCID (string lpName, uint dwFlags);
// LCIDToLocaleName (Vista+)
[DllImport ("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int LCIDToLocaleName (int Locale, [Out] StringBuilder lpName, int cchName, uint dwFlags);
// Current locale name like "en-US"
public static string CurrentLocale
{
get
{
try
{
// prefer thread culture name which reflects user culture
string name = CultureInfo.CurrentCulture.Name;
if (string.IsNullOrEmpty (name))
name = CultureInfo.InstalledUICulture.Name;
return name ?? string.Empty;
}
catch
{
return string.Empty;
}
}
}
// Current LCID (int)
public static int CurrentLCID
{
get
{
try
{
return CultureInfo.CurrentCulture.LCID;
}
catch
{
return CultureInfo.InvariantCulture.LCID;
}
}
}
// Convert LCID -> locale name (e.g. 1033 -> "en-US")
public static string ToLocaleName (int lcid)
{
try
{
// Try managed first
var ci = new CultureInfo (lcid);
if (!string.IsNullOrEmpty (ci.Name))
return ci.Name;
}
catch
{
// try Win32 LCIDToLocaleName (Vista+)
try
{
StringBuilder sb = new StringBuilder (LOCALE_NAME_MAX_LENGTH);
int res = LCIDToLocaleName (lcid, sb, sb.Capacity, 0);
if (res > 0) return sb.ToString ();
}
catch { }
}
return string.Empty;
}
// Convert locale name -> LCID
public static int ToLCID (string localeName)
{
if (string.IsNullOrEmpty (localeName)) return CultureInfo.InvariantCulture.LCID;
try
{
// prefer managed creation
var ci = new CultureInfo (localeName);
return ci.LCID;
}
catch
{
// try Win32 LocaleNameToLCID (Vista+)
try
{
int lcid = LocaleNameToLCID (localeName, 0);
if (lcid != 0) return lcid;
}
catch { }
}
// fallback: invariant culture
return CultureInfo.InvariantCulture.LCID;
}
// Return a locale info string for given LCID and LCTYPE. LCTYPE is the Win32 LOCALE_* constant.
// Returns a string (or empty string on failure).
public static object LocaleInfo (int lcid, int lctype)
{
try
{
// First try mapping common LCTYPE values to managed properties for better correctness
// Some common LCTYPE values:
// LOCALE_SISO639LANGNAME = 0x59 (89) -> Two-letter ISO language name
// LOCALE_SISO3166CTRYNAME = 0x5A (90) -> Two-letter country/region name
// LOCALE_SNAME = 0x5c (92) -> locale name like "en-US" (Vista+)
// But we cannot rely on all values, so we fallback to native GetLocaleInfoW.
if (lctype == 0x59) // LOCALE_SISO639LANGNAME
{
try
{
var ci = new CultureInfo (lcid);
return ci.TwoLetterISOLanguageName ?? string.Empty;
}
catch { }
}
else if (lctype == 0x5A) // LOCALE_SISO3166CTRYNAME
{
try
{
var ci = new CultureInfo (lcid);
try
{
var ri = new RegionInfo (ci.Name);
return ri.TwoLetterISORegionName ?? string.Empty;
}
catch
{
// some cultures have no region; fallback to parsing name
var name = ci.Name;
if (!string.IsNullOrEmpty (name) && name.IndexOf ('-') >= 0)
{
return name.Split ('-') [1];
}
}
}
catch { }
}
else if (lctype == 0x5c) // LOCALE_SNAME
{
try
{
var ci = new CultureInfo (lcid);
return ci.Name ?? string.Empty;
}
catch { }
}
// Fallback to native GetLocaleInfoW
StringBuilder sb = new StringBuilder (256);
int ret = GetLocaleInfoW (lcid, lctype, sb, sb.Capacity);
if (ret > 0)
return sb.ToString ();
return string.Empty;
}
catch
{
return string.Empty;
}
}
// LocaleInfoEx: query by locale name string and LCTYPE
// Returns string if available; otherwise returns the integer result code (as int) if string empty (mimic C++ behavior).
public static object LocaleInfoEx (string localeName, int lctype)
{
if (string.IsNullOrEmpty (localeName))
{
// fall back to current culture name
localeName = CurrentLocale;
if (string.IsNullOrEmpty (localeName)) return 0;
}
try
{
// Try managed shortcuts for common types
if (lctype == 0x59) // LOCALE_SISO639LANGNAME
{
try
{
var ci = new CultureInfo (localeName);
return ci.TwoLetterISOLanguageName ?? string.Empty;
}
catch { }
}
else if (lctype == 0x5A) // LOCALE_SISO3166CTRYNAME
{
try
{
var ci = new CultureInfo (localeName);
var ri = new RegionInfo (ci.Name);
return ri.TwoLetterISORegionName ?? string.Empty;
}
catch
{
// try to split
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2) return parts [1];
}
}
else if (lctype == 0x5c) // LOCALE_SNAME
{
// localeName is probably already the name
return localeName;
}
// Fallback to GetLocaleInfoEx
StringBuilder sb = new StringBuilder (LOCALE_NAME_MAX_LENGTH);
int res = GetLocaleInfoEx (localeName, lctype, sb, sb.Capacity);
if (res > 0)
{
string outStr = sb.ToString ();
if (!string.IsNullOrEmpty (outStr))
return outStr;
}
// if nothing returned, return the result code
return res;
}
catch
{
return 0;
}
}
// Helpers similar to the C++: restricted (language) and elaborated (region) codes
public static string GetLocaleRestrictedCode (string localeName)
{
if (string.IsNullOrEmpty (localeName)) localeName = CurrentLocale;
try
{
var ci = new CultureInfo (localeName);
return ci.TwoLetterISOLanguageName ?? string.Empty;
}
catch
{
// fallback: parse name
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1) return parts [0];
return string.Empty;
}
}
public static string GetLocaleElaboratedCode (string localeName)
{
if (string.IsNullOrEmpty (localeName)) localeName = CurrentLocale;
try
{
var ci = new CultureInfo (localeName);
// Region part from RegionInfo
try
{
var ri = new RegionInfo (ci.Name);
return ri.TwoLetterISORegionName ?? string.Empty;
}
catch
{
// fallback: parse
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2) return parts [1];
}
}
catch
{
var parts = localeName.Split (new char [] { '-', '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2) return parts [1];
}
return string.Empty;
}
// LCID -> combined code like "en-US" (with configurable separator)
public static string LcidToLocaleCode (int lcid)
{
try
{
var ci = new CultureInfo (lcid);
if (!string.IsNullOrEmpty (ci.Name)) return ci.Name;
}
catch
{
try
{
var name = ToLocaleName (lcid);
if (!string.IsNullOrEmpty (name)) return name;
}
catch { }
}
return string.Empty;
}
// Get the user default locale name
public static string GetUserDefaultLocaleName ()
{
try
{
// In .NET, CurrentCulture corresponds to user default
string name = CultureInfo.CurrentCulture.Name;
if (!string.IsNullOrEmpty (name)) return name;
}
catch { }
return LcidToLocaleCode (CultureInfo.CurrentCulture.LCID);
}
// Get system default locale name (machine)
public static string GetSystemDefaultLocaleName ()
{
try
{
// InstalledUICulture / Invariant fallback
string name = CultureInfo.InstalledUICulture.Name;
if (!string.IsNullOrEmpty (name)) return name;
}
catch { }
return LcidToLocaleCode (CultureInfo.InstalledUICulture.LCID);
}
// Get computer locale code similar to C++ approach
public static string GetComputerLocaleCode ()
{
try
{
// Thread culture -> user -> system
string threadName = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
if (!string.IsNullOrEmpty (threadName)) return threadName;
string user = GetUserDefaultLocaleName ();
if (!string.IsNullOrEmpty (user)) return user;
string system = GetSystemDefaultLocaleName ();
if (!string.IsNullOrEmpty (system)) return system;
}
catch { }
// fallback to invariant
return CultureInfo.InvariantCulture.Name ?? string.Empty;
}
// Compare two locale names; returns true if equal by name or LCID
public static bool LocaleNameCompare (string left, string right)
{
if (string.Equals (left, right, StringComparison.OrdinalIgnoreCase)) return true;
try
{
int l = ToLCID (left);
int r = ToLCID (right);
return l == r && l != CultureInfo.InvariantCulture.LCID;
}
catch
{
return false;
}
}
// Constants
private const int LOCALE_NAME_MAX_LENGTH = 85; // defined by Windows
}
public static class UIExt
{
// GetDeviceCaps index for DPI X
private const int LOGPIXELSX = 88;
[DllImport ("user32.dll")]
private static extern IntPtr GetDC (IntPtr hWnd);
[DllImport ("user32.dll")]
private static extern int ReleaseDC (IntPtr hWnd, IntPtr hDC);
[DllImport ("gdi32.dll")]
private static extern int GetDeviceCaps (IntPtr hdc, int nIndex);
/// <summary>
/// Gets system DPI as percentage (100 = 96 DPI, 125 = 120 DPI, etc.)
/// </summary>
public static int DPI
{
get { return GetDPI (); }
}
/// <summary>
/// Gets system DPI as scale factor (1.0 = 100%, 1.25 = 125%)
/// </summary>
public static double DPIScale
{
get { return DPI * 0.01; }
}
/// <summary>
/// Gets system DPI percentage based on 96 DPI baseline.
/// </summary>
public static int GetDPI ()
{
IntPtr hdc = IntPtr.Zero;
try
{
hdc = GetDC (IntPtr.Zero);
if (hdc == IntPtr.Zero)
return 100; // safe default
int dpiX = GetDeviceCaps (hdc, LOGPIXELSX);
if (dpiX <= 0)
return 100;
// 96 DPI == 100%
return (int)Math.Round (dpiX * 100.0 / 96.0);
}
catch
{
return 100;
}
finally
{
if (hdc != IntPtr.Zero)
ReleaseDC (IntPtr.Zero, hdc);
}
}
}
public static class MSRUriHelper
{
public const string MsResScheme = "ms-resource:";
public static readonly int MsResSchemeLength = MsResScheme.Length;
/// <summary>
/// Converts ms-resource URI or file path to path segments.
/// </summary>
public static int KeyToPath (string key, IList<string> output)
{
output.Clear ();
if (string.IsNullOrEmpty (key))
return 0;
key = key.Trim ();
try
{
// URI
if (IsMsResourceUri (key))
{
Uri uri = new Uri (key, UriKind.RelativeOrAbsolute);
return UriToPath (uri, output);
}
// File path
SplitPath (key, '\\', output);
}
catch
{
// fallback: treat as file path
SplitPath (key, '\\', output);
}
return output.Count;
}
public static List<string> KeyToPath (string key)
{
List<string> ret = new List<string> ();
KeyToPath (key, ret);
return ret;
}
/// <summary>
/// Converts System.Uri to path segments.
/// </summary>
public static int UriToPath (Uri uri, IList<string> output)
{
output.Clear ();
if (uri == null)
return 0;
try
{
string path = uri.AbsolutePath;
string [] parts = path.Split (new [] { '/' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string p in parts)
output.Add (p);
}
catch
{
// ignored
}
return output.Count;
}
public static int UriToPath (string uristr, IList<string> output)
{
var uri = new Uri (uristr);
return UriToPath (uri, output);
}
public static List<string> UriToPath (Uri uri)
{
List<string> ret = new List<string> ();
UriToPath (uri, ret);
return ret;
}
public static List<string> UriToPath (string uristr)
{
var uri = new Uri (uristr);
return UriToPath (uri);
}
/// <summary>
/// Checks whether key starts with ms-resource:
/// </summary>
public static bool IsMsResourceUri (string key)
{
if (string.IsNullOrEmpty (key))
return false;
return key.TrimStart ().StartsWith (MsResScheme, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// ms-resource://... (full uri)
/// </summary>
public static bool IsFullMsResourceUri (string key)
{
if (!IsMsResourceUri (key))
return false;
return key.TrimStart ().StartsWith (
MsResScheme + "//",
StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// ms-resource:foo/bar (relative uri)
/// </summary>
public static bool IsRelativeMsResourceUri (string key)
{
return IsMsResourceUri (key) && !IsFullMsResourceUri (key);
}
private static void SplitPath (string value, char sep, IList<string> output)
{
string [] parts = value.Split (new [] { sep }, StringSplitOptions.RemoveEmptyEntries);
foreach (string p in parts)
output.Add (p);
}
}
public enum PriPathSeparator
{
Backslash, // "\"
Slash // "/"
}
public sealed class PriPath: IList<string>, IEquatable<PriPath>
{
private readonly List<string> _segments;
public bool IgnoreCase { get; }
public PriPath (bool ignoreCase = true)
{
_segments = new List<string> ();
IgnoreCase = ignoreCase;
}
public PriPath (IEnumerable<string> segments, bool ignoreCase = true)
{
_segments = new List<string> (segments ?? Enumerable.Empty<string> ());
IgnoreCase = ignoreCase;
}
public PriPath (Uri resuri, bool ignoreCase = true) :
this (MSRUriHelper.UriToPath (resuri), ignoreCase)
{ }
public PriPath (string resname, bool ignoreCase = true) :
this (MSRUriHelper.KeyToPath (resname), ignoreCase)
{ }
public int Count => _segments.Count;
public bool IsReadOnly => false;
public string this [int index]
{
get { return _segments [index]; }
set { _segments [index] = value; }
}
public void Add (string item) => _segments.Add (item);
public void Clear () => _segments.Clear ();
public bool Contains (string item) =>
_segments.Contains (item, IgnoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);
public void CopyTo (string [] array, int arrayIndex) => _segments.CopyTo (array, arrayIndex);
public IEnumerator<string> GetEnumerator () => _segments.GetEnumerator ();
IEnumerator IEnumerable.GetEnumerator () => _segments.GetEnumerator ();
public int IndexOf (string item) =>
_segments.FindIndex (x =>
string.Equals (x, item,
IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal));
public void Insert (int index, string item) => _segments.Insert (index, item);
public bool Remove (string item) => _segments.Remove (item);
public void RemoveAt (int index) => _segments.RemoveAt (index);
public override string ToString ()
{
return ToString (PriPathSeparator.Backslash);
}
public string ToString (PriPathSeparator sep)
{
string s = sep == PriPathSeparator.Backslash ? "\\" : "/";
return string.Join (s, _segments);
}
public string ToUriString ()
{
// ms-resource: URI style (relative)
return "ms-resource:" + ToString (PriPathSeparator.Slash);
}
public static PriPath FromString (string path, bool ignoreCase = true)
{
if (path == null) return new PriPath (ignoreCase: ignoreCase);
// detect URI
if (path.StartsWith ("ms-resource:", StringComparison.OrdinalIgnoreCase))
{
string rest = path.Substring ("ms-resource:".Length);
rest = rest.TrimStart ('/');
var segs = rest.Split (new [] { '/' }, StringSplitOptions.RemoveEmptyEntries);
return new PriPath (segs, ignoreCase);
}
// file path
var parts = path.Split (new [] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
return new PriPath (parts, ignoreCase);
}
public override bool Equals (object obj)
{
return Equals (obj as PriPath);
}
public bool Equals (PriPath other)
{
if (ReferenceEquals (other, null)) return false;
if (ReferenceEquals (this, other)) return true;
if (Count != other.Count) return false;
var comparer = IgnoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
for (int i = 0; i < Count; i++)
{
if (!comparer.Equals (_segments [i], other._segments [i]))
return false;
}
return true;
}
public override int GetHashCode ()
{
var comparer = IgnoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
int hash = 17;
foreach (var seg in _segments)
{
hash = hash * 31 + comparer.GetHashCode (seg?.Trim () ?? "");
}
return hash;
}
// Operators
public static bool operator == (PriPath a, PriPath b)
{
if (ReferenceEquals (a, b)) return true;
if (ReferenceEquals (a, null) || ReferenceEquals (b, null)) return false;
return a.Equals (b);
}
public static bool operator != (PriPath a, PriPath b) => !(a == b);
// Concat with another path
public static PriPath operator + (PriPath a, PriPath b)
{
if (a == null) return b == null ? null : new PriPath (b, ignoreCase: true);
if (b == null) return new PriPath (a, ignoreCase: a.IgnoreCase);
var result = new PriPath (a, a.IgnoreCase);
result._segments.AddRange (b._segments);
return result;
}
// Append segment
public static PriPath operator / (PriPath a, string segment)
{
if (a == null) return new PriPath (new [] { segment });
var result = new PriPath (a, a.IgnoreCase);
result._segments.Add (segment);
return result;
}
}
public sealed class PriResourceIdentifier: IEquatable<PriResourceIdentifier>
{
public string Key { get; private set; }
public int TaskType { get; private set; } // 0: string (ms-resource), 1: file path
public PriPath Path { get; private set; }
public PriResourceIdentifier ()
{
Key = string.Empty;
TaskType = 1;
Path = new PriPath ();
}
public PriResourceIdentifier (string key, int type = -1)
{
SetKey (key, type);
}
public PriResourceIdentifier (Uri uri, int type = -1)
{
if (uri == null)
{
SetKey (string.Empty, type);
return;
}
SetKey (uri.ToString (), type);
}
public void SetKey (string value, int type = -1)
{
Key = value ?? string.Empty;
if (type < 0 || type > 1)
{
TaskType = MSRUriHelper.IsMsResourceUri (Key) ? 0 : 1;
}
else
{
TaskType = type;
}
var arr = MSRUriHelper.KeyToPath (Key);
if (TaskType == 1) arr.Insert (0, "Files");
else if (TaskType == 0)
{
if (MSRUriHelper.IsRelativeMsResourceUri (Key)) arr.Insert (0, "resources");
}
// build path segments
Path = new PriPath (arr, ignoreCase: true);
}
public bool IsUri ()
{
return TaskType == 0;
}
public bool IsFilePath ()
{
return TaskType == 1;
}
public bool IsRelativeUri ()
{
return MSRUriHelper.IsRelativeMsResourceUri (Key);
}
public bool IsFullUri ()
{
return MSRUriHelper.IsFullMsResourceUri (Key);
}
public int GetPath (IList<string> output)
{
if (output == null)
throw new ArgumentNullException ("output");
output.Clear ();
if (Path != null)
{
foreach (var seg in Path)
output.Add (seg);
}
return output.Count;
}
public override string ToString ()
{
return Key;
}
// Equals / HashCode
public override bool Equals (object obj)
{
return Equals (obj as PriResourceIdentifier);
}
public bool Equals (PriResourceIdentifier other)
{
if (ReferenceEquals (other, null))
return false;
if (ReferenceEquals (this, other))
return true;
// Key and Path should be equivalent
return string.Equals (Key, other.Key, StringComparison.OrdinalIgnoreCase)
&& ((Path == null && other.Path == null) ||
(Path != null && Path.Equals (other.Path)));
}
public override int GetHashCode ()
{
int hash = 17;
hash = hash * 31 + (Key ?? "").ToLowerInvariant ().GetHashCode ();
hash = hash * 31 + (Path != null ? Path.GetHashCode () : 0);
return hash;
}
public static bool operator == (PriResourceIdentifier a, PriResourceIdentifier b)
{
if (ReferenceEquals (a, b))
return true;
if (ReferenceEquals (a, null) || ReferenceEquals (b, null))
return false;
return a.Equals (b);
}
public static bool operator != (PriResourceIdentifier a, PriResourceIdentifier b)
{
return !(a == b);
}
}
}

View File

@@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace PriFormat
{
public class DecisionInfoSection: Section
{
public IList<Decision> Decisions { get; private set; }
public IList<QualifierSet> QualifierSets { get; private set; }
public IList<Qualifier> Qualifiers { get; private set; }
internal const string Identifier = "[mrm_decn_info]\0";
internal DecisionInfoSection (PriFile priFile)
: base (Identifier, priFile)
{
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
ushort numDistinctQualifiers = binaryReader.ReadUInt16 ();
ushort numQualifiers = binaryReader.ReadUInt16 ();
ushort numQualifierSets = binaryReader.ReadUInt16 ();
ushort numDecisions = binaryReader.ReadUInt16 ();
ushort numIndexTableEntries = binaryReader.ReadUInt16 ();
ushort totalDataLength = binaryReader.ReadUInt16 ();
List<DecisionInfo> decisionInfos = new List<DecisionInfo> (numDecisions);
for (int i = 0; i < numDecisions; i++)
{
ushort firstQualifierSetIndexIndex = binaryReader.ReadUInt16 ();
ushort numQualifierSetsInDecision = binaryReader.ReadUInt16 ();
decisionInfos.Add (new DecisionInfo (firstQualifierSetIndexIndex, numQualifierSetsInDecision));
}
List<QualifierSetInfo> qualifierSetInfos = new List<QualifierSetInfo> (numQualifierSets);
for (int i = 0; i < numQualifierSets; i++)
{
ushort firstQualifierIndexIndex = binaryReader.ReadUInt16 ();
ushort numQualifiersInSet = binaryReader.ReadUInt16 ();
qualifierSetInfos.Add (new QualifierSetInfo (firstQualifierIndexIndex, numQualifiersInSet));
}
List<QualifierInfo> qualifierInfos = new List<QualifierInfo> (numQualifiers);
for (int i = 0; i < numQualifiers; i++)
{
ushort index = binaryReader.ReadUInt16 ();
ushort priority = binaryReader.ReadUInt16 ();
ushort fallbackScore = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
qualifierInfos.Add (new QualifierInfo (index, priority, fallbackScore));
}
List<DistinctQualifierInfo> distinctQualifierInfos = new List<DistinctQualifierInfo> (numDistinctQualifiers);
for (int i = 0; i < numDistinctQualifiers; i++)
{
binaryReader.ReadUInt16 ();
QualifierType qualifierType = (QualifierType)binaryReader.ReadUInt16 ();
binaryReader.ReadUInt16 ();
binaryReader.ReadUInt16 ();
uint operandValueOffset = binaryReader.ReadUInt32 ();
distinctQualifierInfos.Add (new DistinctQualifierInfo (qualifierType, operandValueOffset));
}
ushort [] indexTable = new ushort [numIndexTableEntries];
for (int i = 0; i < numIndexTableEntries; i++)
indexTable [i] = binaryReader.ReadUInt16 ();
long dataStartOffset = binaryReader.BaseStream.Position;
List<Qualifier> qualifiers = new List<Qualifier> (numQualifiers);
for (int i = 0; i < numQualifiers; i++)
{
DistinctQualifierInfo distinctQualifierInfo = distinctQualifierInfos [qualifierInfos [i].Index];
binaryReader.BaseStream.Seek (dataStartOffset + distinctQualifierInfo.OperandValueOffset * 2, SeekOrigin.Begin);
string value = binaryReader.ReadNullTerminatedString (Encoding.Unicode);
qualifiers.Add (new Qualifier (
(ushort)i,
distinctQualifierInfo.QualifierType,
qualifierInfos [i].Priority,
qualifierInfos [i].FallbackScore / 1000f,
value));
}
Qualifiers = qualifiers;
List<QualifierSet> qualifierSets = new List<QualifierSet> (numQualifierSets);
for (int i = 0; i < numQualifierSets; i++)
{
List<Qualifier> qualifiersInSet = new List<Qualifier> (qualifierSetInfos [i].NumQualifiersInSet);
for (int j = 0; j < qualifierSetInfos [i].NumQualifiersInSet; j++)
qualifiersInSet.Add (qualifiers [indexTable [qualifierSetInfos [i].FirstQualifierIndexIndex + j]]);
qualifierSets.Add (new QualifierSet ((ushort)i, qualifiersInSet));
}
QualifierSets = qualifierSets;
List<Decision> decisions = new List<Decision> (numDecisions);
for (int i = 0; i < numDecisions; i++)
{
List<QualifierSet> qualifierSetsInDecision = new List<QualifierSet> (decisionInfos [i].NumQualifierSetsInDecision);
for (int j = 0; j < decisionInfos [i].NumQualifierSetsInDecision; j++)
qualifierSetsInDecision.Add (qualifierSets [indexTable [decisionInfos [i].FirstQualifierSetIndexIndex + j]]);
decisions.Add (new Decision ((ushort)i, qualifierSetsInDecision));
}
Decisions = decisions;
return true;
}
private struct DecisionInfo
{
public ushort FirstQualifierSetIndexIndex;
public ushort NumQualifierSetsInDecision;
public DecisionInfo (ushort first, ushort num)
{
FirstQualifierSetIndexIndex = first;
NumQualifierSetsInDecision = num;
}
}
private struct QualifierSetInfo
{
public ushort FirstQualifierIndexIndex;
public ushort NumQualifiersInSet;
public QualifierSetInfo (ushort first, ushort num)
{
FirstQualifierIndexIndex = first;
NumQualifiersInSet = num;
}
}
private struct QualifierInfo
{
public ushort Index;
public ushort Priority;
public ushort FallbackScore;
public QualifierInfo (ushort index, ushort priority, ushort fallbackScore)
{
Index = index;
Priority = priority;
FallbackScore = fallbackScore;
}
}
private struct DistinctQualifierInfo
{
public QualifierType QualifierType;
public uint OperandValueOffset;
public DistinctQualifierInfo (QualifierType type, uint offset)
{
QualifierType = type;
OperandValueOffset = offset;
}
}
public override void Dispose ()
{
Decisions?.Clear ();
Decisions = null;
QualifierSets?.Clear ();
QualifierSets = null;
Qualifiers?.Clear ();
Qualifiers = null;
base.Dispose ();
}
~DecisionInfoSection () { Dispose (); }
}
public enum QualifierType
{
Language,
Contrast,
Scale,
HomeRegion,
TargetSize,
LayoutDirection,
Theme,
AlternateForm,
DXFeatureLevel,
Configuration,
DeviceFamily,
Custom
}
public struct Qualifier
{
public ushort Index;
public QualifierType Type;
public ushort Priority;
public float FallbackScore;
public string Value;
public Qualifier (ushort index, QualifierType type, ushort priority, float fallbackScore, string value)
{
Index = index;
Type = type;
Priority = priority;
FallbackScore = fallbackScore;
Value = value;
}
}
public struct QualifierSet
{
public ushort Index;
public IList<Qualifier> Qualifiers;
public QualifierSet (ushort index, IList<Qualifier> qualifiers)
{
Index = index;
Qualifiers = qualifiers;
}
}
public struct Decision
{
public ushort Index;
public IList<QualifierSet> QualifierSets;
public Decision (ushort index, IList<QualifierSet> qualifierSets)
{
Index = index;
QualifierSets = qualifierSets;
}
}
}

View File

@@ -0,0 +1,347 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace PriFormat
{
public class HierarchicalSchemaSection: Section
{
public HierarchicalSchemaVersionInfo Version { get; private set; }
public string UniqueName { get; private set; }
public string Name { get; private set; }
public IList<ResourceMapScope> Scopes { get; private set; }
public IList<ResourceMapItem> Items { get; private set; }
readonly bool extendedVersion;
internal const string Identifier1 = "[mrm_hschema] \0";
internal const string Identifier2 = "[mrm_hschemaex] ";
internal HierarchicalSchemaSection (PriFile priFile, bool extendedVersion)
: base (extendedVersion ? Identifier2 : Identifier1, priFile)
{
this.extendedVersion = extendedVersion;
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
if (binaryReader.BaseStream.Length == 0)
{
Version = null;
UniqueName = null;
Name = null;
Scopes = new List<ResourceMapScope> ();
Items = new List<ResourceMapItem> ();
return true;
}
binaryReader.ExpectUInt16 (1);
ushort uniqueNameLength = binaryReader.ReadUInt16 ();
ushort nameLength = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
bool extendedHNames;
if (extendedVersion)
{
string def = new string (binaryReader.ReadChars (16));
switch (def)
{
case "[def_hnamesx] \0":
extendedHNames = true;
break;
case "[def_hnames] \0":
extendedHNames = false;
break;
default:
throw new InvalidDataException ();
}
}
else
{
extendedHNames = false;
}
// hierarchical schema version info
ushort majorVersion = binaryReader.ReadUInt16 ();
ushort minorVersion = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt32 (0);
uint checksum = binaryReader.ReadUInt32 ();
uint numScopes = binaryReader.ReadUInt32 ();
uint numItems = binaryReader.ReadUInt32 ();
Version = new HierarchicalSchemaVersionInfo (majorVersion, minorVersion, checksum, numScopes, numItems);
UniqueName = binaryReader.ReadNullTerminatedString (Encoding.Unicode);
Name = binaryReader.ReadNullTerminatedString (Encoding.Unicode);
if (UniqueName.Length != uniqueNameLength - 1 || Name.Length != nameLength - 1)
throw new InvalidDataException ();
binaryReader.ExpectUInt16 (0);
ushort maxFullPathLength = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
binaryReader.ExpectUInt32 (numScopes + numItems);
binaryReader.ExpectUInt32 (numScopes);
binaryReader.ExpectUInt32 (numItems);
uint unicodeDataLength = binaryReader.ReadUInt32 ();
binaryReader.ReadUInt32 (); // meaning unknown
if (extendedHNames)
binaryReader.ReadUInt32 (); // meaning unknown
List<ScopeAndItemInfo> scopeAndItemInfos = new List<ScopeAndItemInfo> ((int)(numScopes + numItems));
for (int i = 0; i < numScopes + numItems; i++)
{
ushort parent = binaryReader.ReadUInt16 ();
ushort fullPathLength = binaryReader.ReadUInt16 ();
char uppercaseFirstChar = (char)binaryReader.ReadUInt16 ();
byte nameLength2 = binaryReader.ReadByte ();
byte flags = binaryReader.ReadByte ();
uint nameOffset = binaryReader.ReadUInt16 () | (uint)((flags & 0xF) << 16);
ushort index = binaryReader.ReadUInt16 ();
scopeAndItemInfos.Add (new ScopeAndItemInfo (parent, fullPathLength, flags, nameOffset, index));
}
List<ScopeExInfo> scopeExInfos = new List<ScopeExInfo> ((int)numScopes);
for (int i = 0; i < numScopes; i++)
{
ushort scopeIndex = binaryReader.ReadUInt16 ();
ushort childCount = binaryReader.ReadUInt16 ();
ushort firstChildIndex = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
scopeExInfos.Add (new ScopeExInfo (scopeIndex, childCount, firstChildIndex));
}
ushort [] itemIndexPropertyToIndex = new ushort [numItems];
for (int i = 0; i < numItems; i++)
itemIndexPropertyToIndex [i] = binaryReader.ReadUInt16 ();
long unicodeDataOffset = binaryReader.BaseStream.Position;
long asciiDataOffset = binaryReader.BaseStream.Position + unicodeDataLength * 2;
ResourceMapScope [] scopes = new ResourceMapScope [numScopes];
ResourceMapItem [] items = new ResourceMapItem [numItems];
for (int i = 0; i < numScopes + numItems; i++)
{
long pos;
if (scopeAndItemInfos [i].NameInAscii)
pos = asciiDataOffset + scopeAndItemInfos [i].NameOffset;
else
pos = unicodeDataOffset + scopeAndItemInfos [i].NameOffset * 2;
binaryReader.BaseStream.Seek (pos, SeekOrigin.Begin);
string name;
if (scopeAndItemInfos [i].FullPathLength != 0)
name = binaryReader.ReadNullTerminatedString (scopeAndItemInfos [i].NameInAscii ? Encoding.ASCII : Encoding.Unicode);
else
name = string.Empty;
ushort index = scopeAndItemInfos [i].Index;
if (scopeAndItemInfos [i].IsScope)
{
if (scopes [index] != null)
throw new InvalidDataException ();
scopes [index] = new ResourceMapScope (index, null, name);
}
else
{
if (items [index] != null)
throw new InvalidDataException ();
items [index] = new ResourceMapItem (index, null, name);
}
}
for (int i = 0; i < numScopes + numItems; i++)
{
ushort index = scopeAndItemInfos [i].Index;
ushort parent = scopeAndItemInfos [scopeAndItemInfos [i].Parent].Index;
if (parent != 0xFFFF)
{
if (scopeAndItemInfos [i].IsScope)
{
if (parent != index)
scopes [index].Parent = scopes [parent];
}
else
items [index].Parent = scopes [parent];
}
}
for (int i = 0; i < numScopes; i++)
{
List<ResourceMapEntry> children = new List<ResourceMapEntry> (scopeExInfos [i].ChildCount);
for (int j = 0; j < scopeExInfos [i].ChildCount; j++)
{
ScopeAndItemInfo saiInfo = scopeAndItemInfos [scopeExInfos [i].FirstChildIndex + j];
if (saiInfo.IsScope)
children.Add (scopes [saiInfo.Index]);
else
children.Add (items [saiInfo.Index]);
}
scopes [i].Children = children;
}
Scopes = scopes;
Items = items;
return true;
}
private struct ScopeAndItemInfo
{
public ushort Parent;
public ushort FullPathLength;
public byte Flags;
public uint NameOffset;
public ushort Index;
public ScopeAndItemInfo (ushort parent, ushort fullPathLength, byte flags, uint nameOffset, ushort index)
{
Parent = parent;
FullPathLength = fullPathLength;
Flags = flags;
NameOffset = nameOffset;
Index = index;
}
public bool IsScope
{
get { return (Flags & 0x10) != 0; }
}
public bool NameInAscii
{
get { return (Flags & 0x20) != 0; }
}
}
private struct ScopeExInfo
{
public ushort ScopeIndex;
public ushort ChildCount;
public ushort FirstChildIndex;
public ScopeExInfo (ushort scopeIndex, ushort childCount, ushort firstChildIndex)
{
ScopeIndex = scopeIndex;
ChildCount = childCount;
FirstChildIndex = firstChildIndex;
}
}
public override void Dispose ()
{
this.Version = null;
Scopes?.Clear ();
Scopes = null;
Items?.Clear ();
Items = null;
base.Dispose ();
}
}
public class HierarchicalSchemaVersionInfo
{
public ushort MajorVersion { get; private set; }
public ushort MinorVersion { get; private set; }
public uint Checksum { get; private set; }
public uint NumScopes { get; private set; }
public uint NumItems { get; private set; }
public HierarchicalSchemaVersionInfo (ushort major, ushort minor, uint checksum, uint numScopes, uint numItems)
{
MajorVersion = major;
MinorVersion = minor;
Checksum = checksum;
NumScopes = numScopes;
NumItems = numItems;
}
}
public abstract class ResourceMapEntry: IDisposable
{
public ushort Index { get; private set; }
public ResourceMapScope Parent { get; internal set; }
public string Name { get; private set; }
internal ResourceMapEntry (ushort index, ResourceMapScope parent, string name)
{
Index = index;
Parent = parent;
Name = name;
}
string fullName;
public string FullName
{
get
{
if (fullName == null)
{
if (Parent == null)
fullName = Name;
else
fullName = Parent.FullName + "\\" + Name;
}
return fullName;
}
}
~ResourceMapEntry ()
{
Dispose ();
}
public virtual void Dispose ()
{
Parent = null;
}
}
public sealed class ResourceMapScope: ResourceMapEntry
{
internal ResourceMapScope (ushort index, ResourceMapScope parent, string name)
: base (index, parent, name)
{
}
public IList<ResourceMapEntry> Children { get; internal set; }
public override string ToString ()
{
return string.Format ("Scope {0} {1} ({2} children)", Index, FullName, Children.Count);
}
public override void Dispose ()
{
Children?.Clear ();
Children = null;
base.Dispose ();
}
~ResourceMapScope () { Dispose (); }
}
public sealed class ResourceMapItem: ResourceMapEntry
{
internal ResourceMapItem (ushort index, ResourceMapScope parent, string name)
: base (index, parent, name)
{
}
public override string ToString ()
{
return string.Format ("Item {0} {1}", Index, FullName);
}
}
}

63
PriFormat/Polyfill.cs Normal file
View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace PriFormat
{
public static class Polyfill
{
public static string ReadString (this BinaryReader reader, Encoding encoding, int length)
{
//byte [] data = reader.ReadBytes (length * encoding.GetByteCount ("a"));
//return encoding.GetString (data, 0, data.Length);
// ==========
if (length <= 0) return string.Empty;
int maxBytes = encoding.GetMaxByteCount (length);
byte [] buffer = reader.ReadBytes (maxBytes);
if (buffer.Length == 0) return string.Empty;
string decoded = encoding.GetString (buffer, 0, buffer.Length);
if (decoded.Length > length) decoded = decoded.Substring (0, length);
return decoded;
}
public static string ReadNullTerminatedString (this BinaryReader reader, Encoding encoding)
{
MemoryStream ms = new MemoryStream ();
while (true)
{
byte b1 = reader.ReadByte ();
byte b2 = reader.ReadByte ();
if (b1 == 0 && b2 == 0)
break;
ms.WriteByte (b1);
ms.WriteByte (b2);
}
return encoding.GetString (ms.ToArray ());
// ==========
List<byte> bytes = new List<byte> ();
byte b;
while ((b = reader.ReadByte ()) != 0) bytes.Add (b);
return encoding.GetString (bytes.ToArray ());
}
public static void ExpectByte (this BinaryReader reader, byte expectedValue)
{
if (reader.ReadByte () != expectedValue) throw new InvalidDataException ("Unexpected value read.");
}
public static void ExpectUInt16 (this BinaryReader reader, ushort expectedValue)
{
if (reader.ReadUInt16 () != expectedValue) throw new InvalidDataException ("Unexpected value read.");
}
public static void ExpectUInt32 (this BinaryReader reader, uint expectedValue)
{
if (reader.ReadUInt32 () != expectedValue) throw new InvalidDataException ("Unexpected value read.");
}
public static void ExpectString (this BinaryReader reader, string s)
{
if (new string (reader.ReadChars (s.Length)) != s) throw new InvalidDataException ("Unexpected value read.");
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace PriFormat
{
public class PriDescriptorSection: Section
{
public PriDescriptorFlags PriFlags { get; private set; }
public IList<SectionRef<HierarchicalSchemaSection>> HierarchicalSchemaSections { get; private set; }
public IList<SectionRef<DecisionInfoSection>> DecisionInfoSections { get; private set; }
public IList<SectionRef<ResourceMapSection>> ResourceMapSections { get; private set; }
public IList<SectionRef<ReferencedFileSection>> ReferencedFileSections { get; private set; }
public IList<SectionRef<DataItemSection>> DataItemSections { get; private set; }
public SectionRef<ResourceMapSection> PrimaryResourceMapSection { get; private set; }
public bool HasPrimaryResourceMapSection { get; private set; }
internal const string Identifier = "[mrm_pridescex]\0";
internal PriDescriptorSection (PriFile priFile)
: base (Identifier, priFile)
{
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
PriFlags = (PriDescriptorFlags)binaryReader.ReadUInt16 ();
ushort includedFileListSection = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
ushort numHierarchicalSchemaSections = binaryReader.ReadUInt16 ();
ushort numDecisionInfoSections = binaryReader.ReadUInt16 ();
ushort numResourceMapSections = binaryReader.ReadUInt16 ();
ushort primaryResourceMapSection = binaryReader.ReadUInt16 ();
if (primaryResourceMapSection != 0xFFFF)
{
PrimaryResourceMapSection =
new SectionRef<ResourceMapSection> (primaryResourceMapSection);
HasPrimaryResourceMapSection = true;
}
else
{
HasPrimaryResourceMapSection = false;
}
ushort numReferencedFileSections = binaryReader.ReadUInt16 ();
ushort numDataItemSections = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
// Hierarchical schema sections
List<SectionRef<HierarchicalSchemaSection>> hierarchicalSchemaSections =
new List<SectionRef<HierarchicalSchemaSection>> (numHierarchicalSchemaSections);
for (int i = 0; i < numHierarchicalSchemaSections; i++)
{
hierarchicalSchemaSections.Add (
new SectionRef<HierarchicalSchemaSection> (binaryReader.ReadUInt16 ()));
}
HierarchicalSchemaSections = hierarchicalSchemaSections;
// Decision info sections
List<SectionRef<DecisionInfoSection>> decisionInfoSections =
new List<SectionRef<DecisionInfoSection>> (numDecisionInfoSections);
for (int i = 0; i < numDecisionInfoSections; i++)
{
decisionInfoSections.Add (
new SectionRef<DecisionInfoSection> (binaryReader.ReadUInt16 ()));
}
DecisionInfoSections = decisionInfoSections;
// Resource map sections
List<SectionRef<ResourceMapSection>> resourceMapSections =
new List<SectionRef<ResourceMapSection>> (numResourceMapSections);
for (int i = 0; i < numResourceMapSections; i++)
{
resourceMapSections.Add (
new SectionRef<ResourceMapSection> (binaryReader.ReadUInt16 ()));
}
ResourceMapSections = resourceMapSections;
// Referenced file sections
List<SectionRef<ReferencedFileSection>> referencedFileSections =
new List<SectionRef<ReferencedFileSection>> (numReferencedFileSections);
for (int i = 0; i < numReferencedFileSections; i++)
{
referencedFileSections.Add (
new SectionRef<ReferencedFileSection> (binaryReader.ReadUInt16 ()));
}
ReferencedFileSections = referencedFileSections;
// Data item sections
List<SectionRef<DataItemSection>> dataItemSections =
new List<SectionRef<DataItemSection>> (numDataItemSections);
for (int i = 0; i < numDataItemSections; i++)
{
dataItemSections.Add (
new SectionRef<DataItemSection> (binaryReader.ReadUInt16 ()));
}
DataItemSections = dataItemSections;
return true;
}
public override void Dispose ()
{
this.HierarchicalSchemaSections?.Clear ();
this.DecisionInfoSections?.Clear ();
this.ResourceMapSections?.Clear ();
this.ReferencedFileSections?.Clear ();
this.DataItemSections?.Clear ();
HierarchicalSchemaSections = null;
DecisionInfoSections = null;
ResourceMapSections = null;
ReferencedFileSections = null;
DataItemSections = null;
base.Dispose ();
}
}
[Flags]
public enum PriDescriptorFlags: ushort
{
AutoMerge = 1,
IsDeploymentMergeable = 2,
IsDeploymentMergeResult = 4,
IsAutomergeMergeResult = 8
}
}

144
PriFormat/PriFile.cs Normal file
View File

@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace PriFormat
{
public class PriFile: IDisposable
{
public string Version { get; private set; }
public uint TotalFileSize { get; private set; }
public IList<TocEntry> TableOfContents { get; private set; }
public IList<Section> Sections { get; private set; }
private PriFile ()
{
}
public static PriFile Parse (Stream stream)
{
PriFile priFile = new PriFile ();
priFile.ParseInternal (stream);
return priFile;
}
private void ParseInternal (Stream stream)
{
BinaryReader binaryReader = new BinaryReader (stream, Encoding.ASCII);
long fileStartOffset = binaryReader.BaseStream.Position;
string magic = new string (binaryReader.ReadChars (8));
switch (magic)
{
case "mrm_pri0":
case "mrm_pri1":
case "mrm_pri2":
case "mrm_pri3":
case "mrm_prif":
Version = magic;
break;
default:
throw new InvalidDataException ("Data does not start with a PRI file header.");
}
binaryReader.ExpectUInt16 (0);
binaryReader.ExpectUInt16 (1);
TotalFileSize = binaryReader.ReadUInt32 ();
uint tocOffset = binaryReader.ReadUInt32 ();
uint sectionStartOffset = binaryReader.ReadUInt32 ();
ushort numSections = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0xFFFF);
binaryReader.ExpectUInt32 (0);
binaryReader.BaseStream.Seek (fileStartOffset + TotalFileSize - 16, SeekOrigin.Begin);
binaryReader.ExpectUInt32 (0xDEFFFADE);
binaryReader.ExpectUInt32 (TotalFileSize);
binaryReader.ExpectString (magic);
binaryReader.BaseStream.Seek (tocOffset, SeekOrigin.Begin);
List<TocEntry> toc = new List<TocEntry> (numSections);
for (int i = 0; i < numSections; i++)
{
toc.Add (TocEntry.Parse (binaryReader));
}
TableOfContents = toc;
Section [] sections = new Section [numSections];
Sections = sections;
bool parseSuccess;
bool parseFailure;
do
{
parseSuccess = false;
parseFailure = false;
for (int i = 0; i < sections.Length; i++)
{
if (sections [i] != null) continue;
binaryReader.BaseStream.Seek (
sectionStartOffset + toc [i].SectionOffset,
SeekOrigin.Begin);
Section section = Section.CreateForIdentifier (
toc [i].SectionIdentifier,
this);
if (section.Parse (binaryReader))
{
sections [i] = section;
parseSuccess = true;
}
else
{
parseFailure = true;
}
}
}
while (parseFailure && parseSuccess);
if (parseFailure) throw new InvalidDataException ("Failed to parse all sections.");
}
private PriDescriptorSection _priDescriptorSection;
public PriDescriptorSection PriDescriptorSection
{
get
{
if (_priDescriptorSection == null)
{
_priDescriptorSection =
Sections.OfType<PriDescriptorSection> ().Single ();
}
return _priDescriptorSection;
}
}
public T GetSectionByRef<T> (SectionRef<T> sectionRef)
where T : Section
{
return (T)Sections [sectionRef.SectionIndex];
}
public ResourceMapItem GetResourceMapItemByRef (
ResourceMapItemRef resourceMapItemRef)
{
HierarchicalSchemaSection schema =
GetSectionByRef (resourceMapItemRef.SchemaSection);
return schema.Items [resourceMapItemRef.ItemIndex];
}
public ByteSpan GetDataItemByRef (DataItemRef dataItemRef)
{
DataItemSection section =
GetSectionByRef (dataItemRef.DataItemSection);
return section.DataItems [dataItemRef.ItemIndex];
}
public ReferencedFile GetReferencedFileByRef (
ReferencedFileRef referencedFileRef)
{
SectionRef<ReferencedFileSection> refSection =
PriDescriptorSection.ReferencedFileSections.First ();
ReferencedFileSection section = GetSectionByRef (refSection);
return section.ReferencedFiles [referencedFileRef.FileIndex];
}
public void Dispose ()
{
TableOfContents?.Clear ();
TableOfContents = null;
//Sections?.Clear ();
Sections = null;
}
}
}

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{676E9BD2-A704-4539-9A88-E46654FC94B6}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PriFormat</RootNamespace>
<AssemblyName>PriFormat</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ByteSpan.cs" />
<Compile Include="DataItemSection.cs" />
<Compile Include="Datas.cs" />
<Compile Include="DecisionInfoSection.cs" />
<Compile Include="HierarchicalSchemaSection.cs" />
<Compile Include="Polyfill.cs" />
<Compile Include="PriDescriptorSection.cs" />
<Compile Include="PriFile.cs" />
<Compile Include="PriReader.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReferencedFileSection.cs" />
<Compile Include="ResourceMapSection.cs" />
<Compile Include="ReverseMapSection.cs" />
<Compile Include="Section.cs" />
<Compile Include="StreamHelper.cs" />
<Compile Include="SubStream.cs" />
<Compile Include="TocEntry.cs" />
<Compile Include="UnknownSection.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

817
PriFormat/PriReader.cs Normal file
View File

@@ -0,0 +1,817 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
namespace PriFormat
{
internal enum SearchState
{
Pending,
Searching,
Found,
NotFound
}
internal sealed class SearchTask
{
public PriPath Path { get; }
public BaseResources Result { get; set; }
public SearchState State { get; set; }
public SearchTask (PriPath path)
{
Path = path;
State = SearchState.Pending;
}
}
public sealed class PriReader: IDisposable
{
private PriFile _pri;
private Stream _stream;
private readonly bool _fromFile;
private readonly object _lock = new object ();
private readonly Dictionary<PriPath, SearchTask> _tasks =
new Dictionary<PriPath, SearchTask> ();
private Thread _searchThread;
private bool _searchRunning;
private bool _disposed;
private readonly AutoResetEvent _searchWakeup = new AutoResetEvent (false);
private PriReader (string filePath)
{
_fromFile = true;
_stream = new FileStream (filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
_pri = PriFile.Parse (_stream);
}
private PriReader (Stream stream)
{
_fromFile = false;
_stream = stream;
_pri = PriFile.Parse (stream);
}
public static PriReader Open (string filePath) => new PriReader (filePath);
public static PriReader Open (Stream stream) => new PriReader (stream);
public void AddSearch (IEnumerable<string> resourceNames)
{
if (resourceNames == null) return;
bool added = false;
lock (_lock)
{
foreach (var name in resourceNames)
{
if (string.IsNullOrEmpty (name)) continue;
var path = new PriResourceIdentifier (name).Path;
if (!_tasks.ContainsKey (path))
{
_tasks [path] = new SearchTask (path);
added = true;
}
}
}
if (added)
EnsureSearchThread ();
}
public void AddSearch (string resname) { AddSearch (new string [] { resname }); }
private void EnsureSearchThread ()
{
lock (_lock)
{
if (_disposed) return;
if (_searchRunning) return;
_searchRunning = true;
_searchThread = new Thread (SearchThreadProc);
_searchThread.IsBackground = true;
_searchThread.Start ();
}
}
private void SearchThreadProc ()
{
try
{
while (true)
{
if (_disposed) return;
bool hasPending = false;
lock (_lock)
{
foreach (var task in _tasks.Values)
{
if (task.State == SearchState.Pending)
{
hasPending = true;
break;
}
}
}
if (!hasPending)
{
// 没任务了,休眠等待唤醒
_searchWakeup.WaitOne (200);
continue;
}
// 真正跑一次搜索
RunSearch (TimeSpan.FromSeconds (10));
}
}
finally
{
lock (_lock)
{
_searchRunning = false;
_searchThread = null;
}
}
}
public BaseResources GetValue (string resourceName)
{
if (string.IsNullOrEmpty (resourceName)) return null;
var path = new PriResourceIdentifier (resourceName).Path;
SearchTask task;
lock (_lock)
{
if (!_tasks.TryGetValue (path, out task))
{
task = new SearchTask (path);
_tasks [path] = task;
EnsureSearchThread ();
}
}
// 已有结果
if (task.State == SearchState.Found)
return task.Result;
// 等待搜索完成
while (true)
{
if (_disposed) return null;
lock (_lock)
{
if (task.State == SearchState.Found)
return task.Result;
if (task.State == SearchState.NotFound)
return null;
}
Thread.Sleep (50);
}
}
public void RunSearch (TimeSpan timeout)
{
var begin = DateTime.Now;
foreach (var rmsRef in _pri.PriDescriptorSection.ResourceMapSections)
{
var rms = _pri.GetSectionByRef (rmsRef);
if (rms == null || rms.HierarchicalSchemaReference != null) continue;
var decision = _pri.GetSectionByRef (rms.DecisionInfoSection);
foreach (var candidateSet in rms.CandidateSets.Values)
{
if (DateTime.Now - begin > timeout) return;
var item = _pri.GetResourceMapItemByRef (candidateSet.ResourceMapItem);
var fullName = item.FullName.Trim ('\\');
var parts = fullName.Split (new [] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
var itemPath = new PriPath (fullName, true);
SearchTask task;
lock (_lock)
{
if (!_tasks.TryGetValue (itemPath, out task))
continue;
if (task.State != SearchState.Pending)
continue;
task.State = SearchState.Searching;
}
var result = ReadCandidate (candidateSet, decision);
lock (_lock)
{
task.Result = result;
task.State = result != null
? SearchState.Found
: SearchState.NotFound;
}
}
}
lock (_lock)
{
foreach (var kv in _tasks)
{
if (kv.Value.State == SearchState.Pending)
{
kv.Value.State = SearchState.NotFound;
}
}
}
if (DateTime.Now - begin > timeout) return;
}
private BaseResources ReadCandidate (
CandidateSet candidateSet,
DecisionInfoSection decisionInfo)
{
string value = System.String.Empty;
int restype = 0; // 0 string, 1 file
Dictionary<StringQualifier, string> strdict = new Dictionary<StringQualifier, string> ();
Dictionary<FileQualifier, string> filedict = new Dictionary<FileQualifier, string> ();
foreach (var candidate in candidateSet.Candidates)
{
if (candidate.SourceFile != null)
{
}
else
{
var byteSpan = new ByteSpan ();
if (candidate.DataItem != null) byteSpan = _pri.GetDataItemByRef (candidate.DataItem);
else byteSpan = candidate.Data;
_stream.Seek (byteSpan.Offset, SeekOrigin.Begin);
var binaryReader = new BinaryReader (_stream, Encoding.Default);
{
var data = binaryReader.ReadBytes ((int)byteSpan.Length);
switch (candidate.Type)
{
case ResourceValueType.AsciiPath:
case ResourceValueType.AsciiString:
value = Encoding.ASCII.GetString (data).TrimEnd ('\0'); break;
case ResourceValueType.Utf8Path:
case ResourceValueType.Utf8String:
value = Encoding.UTF8.GetString (data).TrimEnd ('\0'); break;
case ResourceValueType.Path:
case ResourceValueType.String:
value = Encoding.Unicode.GetString (data).TrimEnd ('\0'); break;
case ResourceValueType.EmbeddedData:
value = Convert.ToBase64String (data); break;
}
}
}
var qualifierSet = decisionInfo.QualifierSets [candidate.QualifierSet];
var qualis = new Dictionary <QualifierType, object> ();
foreach (var quali in qualifierSet.Qualifiers)
{
var qtype = quali.Type;
var qvalue = quali.Value;
qualis.Add (qtype, qvalue);
}
if (qualis.ContainsKey (QualifierType.Language))
{
restype = 0;
strdict.Add (new StringQualifier (qualis [QualifierType.Language].ToString ()), value);
}
else
{
restype = 1;
if (qualis.ContainsKey (QualifierType.Scale))
{
var cons = qualis.ContainsKey (QualifierType.Contrast) ? qualis [QualifierType.Contrast].ToString () : "None";
Contrast cs = Contrast.None;
switch (cons?.Trim ()?.ToLower ())
{
case "white": cs = Contrast.White; break;
case "black": cs = Contrast.Black; break;
case "high": cs = Contrast.High; break;
case "low": cs = Contrast.Low; break;
case "none": cs = Contrast.None; break;
}
filedict.Add (new FileQualifier (Convert.ToInt32 (qualis [QualifierType.Scale]), cs), value);
}
}
}
if (strdict.Count > 0 && filedict.Count > 0)
{
if (strdict.Count >= filedict.Count) return new StringResources (strdict, true);
else return new FileResources (filedict, true);
}
else if (strdict.Count > 0) return new StringResources (strdict, true);
else if (filedict.Count > 0) return new FileResources (filedict, true);
return new StringResources ();
}
public void Dispose ()
{
_disposed = true;
_searchWakeup.Set ();
lock (_lock)
{
_tasks.Clear ();
}
_pri?.Dispose ();
_pri = null;
if (_fromFile)
_stream?.Dispose ();
_stream = null;
}
public string Resource (string resname) { return GetValue (resname)?.SuitableValue ?? ""; }
public Dictionary<string, string> Resources (IEnumerable<string> list)
{
var ret = new Dictionary<string, string> ();
AddSearch (list);
foreach (var item in list) ret [item] = Resource (item);
return ret;
}
public string Path (string resname) => Resource (resname);
public Dictionary<string, string> Paths (IEnumerable<string> resnames) => Resources (resnames);
public string String (string resname) => Resource (resname);
public Dictionary<string, string> Strings (IEnumerable<string> resnames) => Resources (resnames);
}
public sealed class PriReaderBundle: IDisposable
{
private sealed class PriInst
{
// 0b01 = lang, 0b10 = scale, 0b11 = both
public readonly byte Type;
public PriReader Reader;
public PriInst (byte type, Stream stream)
{
Type = (byte)(type & 0x03);
Reader = PriReader.Open (stream);
}
public bool IsValid
{
get { return (Type & 0x03) != 0 && Reader != null; }
}
}
private readonly List<PriInst> _priFiles = new List<PriInst> (3);
private readonly Dictionary<byte, PriInst> _mapPri = new Dictionary<byte, PriInst> ();
// -----------------------------
// Set
// -----------------------------
// type: 1 = language, 2 = scale, 3 = both
public bool Set (byte type, Stream priStream)
{
byte realType = (byte)(type & 0x03);
if (realType == 0 || priStream == null)
return false;
PriInst inst;
if (_mapPri.TryGetValue (realType, out inst))
{
inst.Reader.Dispose ();
inst.Reader = PriReader.Open (priStream);
}
else
{
inst = new PriInst (realType, priStream);
_priFiles.Add (inst);
_mapPri [realType] = inst;
}
return true;
}
// 如果你外部仍然是 IStream / IntPtr这里假定你已有封装成 Stream 的工具
public bool Set (byte type, IntPtr priStream)
{
if (priStream == IntPtr.Zero)
return false;
Stream stream = StreamHelper.FromIStream (priStream);
return Set (type, stream);
}
// -----------------------------
// 内部路由
// -----------------------------
private PriReader Get (byte type, bool mustReturn)
{
type = (byte)(type & 0x03);
PriInst inst;
if (_mapPri.TryGetValue (type, out inst))
return inst.Reader;
// fallback: both
if (type != 0x03 && _mapPri.TryGetValue (0x03, out inst))
return inst.Reader;
if (mustReturn && _priFiles.Count > 0)
return _priFiles [0].Reader;
return null;
}
private static bool IsMsResourcePrefix (string s)
{
try
{
return MSRUriHelper.IsMsResourceUri (s) || MSRUriHelper.IsFullMsResourceUri (s) || MSRUriHelper.IsRelativeMsResourceUri (s);
}
catch
{
return MSRUriHelper.IsMsResourceUri (s);
}
}
// -----------------------------
// AddSearch
// -----------------------------
public void AddSearch (IEnumerable<string> arr)
{
if (arr == null)
return;
List<string> langRes = new List<string> ();
List<string> scaleRes = new List<string> ();
foreach (string it in arr)
{
if (string.IsNullOrEmpty (it))
continue;
if (IsMsResourcePrefix (it))
langRes.Add (it);
else
scaleRes.Add (it);
}
PriReader langPri = Get (1, true);
PriReader scalePri = Get (2, true);
if (langPri != null && langRes.Count > 0)
langPri.AddSearch (langRes);
if (scalePri != null && scaleRes.Count > 0)
scalePri.AddSearch (scaleRes);
}
public void AddSearch (string resName)
{
if (string.IsNullOrEmpty (resName))
return;
if (IsMsResourcePrefix (resName))
{
PriReader langPri = Get (1, true);
if (langPri != null)
langPri.AddSearch (resName);
}
else
{
PriReader scalePri = Get (2, true);
if (scalePri != null)
scalePri.AddSearch (resName);
}
}
// -----------------------------
// Resource / Path / String
// -----------------------------
public string Resource (string resName)
{
if (string.IsNullOrEmpty (resName))
return string.Empty;
PriReader reader;
if (IsMsResourcePrefix (resName))
reader = Get (1, true);
else
reader = Get (2, true);
if (reader == null)
return string.Empty;
var res = reader.GetValue (resName);
return res != null ? res.ToString () : string.Empty;
}
public Dictionary<string, string> Resources (IEnumerable<string> resNames)
{
if (resNames == null)
throw new ArgumentNullException ("resNames");
AddSearch (resNames);
Dictionary<string, string> result = new Dictionary<string, string> ();
foreach (string name in resNames)
result [name] = Resource (name);
return result;
}
public string Path (string resName)
{
return Resource (resName);
}
public Dictionary<string, string> Paths (IEnumerable<string> resNames)
{
return Resources (resNames);
}
public string String (string resName)
{
return Resource (resName);
}
public Dictionary<string, string> Strings (IEnumerable<string> resNames)
{
return Resources (resNames);
}
public void Dispose ()
{
foreach (var inst in _priFiles)
{
inst.Reader.Dispose ();
}
_mapPri.Clear ();
_priFiles.Clear ();
}
}
public abstract class BaseQualifier { }
public class StringQualifier: BaseQualifier, IEquatable<StringQualifier>, IEquatable<string>
{
public string LocaleName { get; private set; }
public int LCID => CultureInfo.GetCultureInfo (LocaleName).LCID;
public StringQualifier (string localeName) { LocaleName = localeName; }
public StringQualifier (int lcid) { LocaleName = CultureInfo.GetCultureInfo (lcid).Name; }
public override string ToString () { return $"String Qualifier: {LocaleName} ({LCID})"; }
public bool Equals (StringQualifier other)
{
var ca = new CultureInfo (this.LocaleName);
var cb = new CultureInfo (other.LocaleName);
return string.Equals (ca.Name, cb.Name, StringComparison.OrdinalIgnoreCase);
}
public bool Equals (string other) { return this.Equals (new StringQualifier (other)); }
public override int GetHashCode () => CultureInfo.GetCultureInfo (LocaleName).Name.GetHashCode ();
}
public enum Contrast
{
None,
Black,
White,
High,
Low
};
public class FileQualifier: BaseQualifier, IEquatable<FileQualifier>, IEquatable<int>, IEquatable<Tuple<int, Contrast>>
{
public Contrast Contrast { get; private set; } = Contrast.None;
public int Scale { get; private set; } = 0;
public FileQualifier (int scale, Contrast contrast = Contrast.None)
{
Scale = scale;
this.Contrast = contrast;
}
public override string ToString () { return $"File Qualifier: Scale {Scale}, Contrast {this.Contrast}"; }
public bool Equals (FileQualifier other)
{
return this.Contrast == other.Contrast && this.Scale == other.Scale;
}
public bool Equals (int other)
{
return this.Scale == other && this.Contrast == Contrast.None;
}
public bool Equals (Tuple<int, Contrast> other)
{
return this.Contrast == other.Item2 && this.Scale == other.Item1;
}
public override int GetHashCode ()
{
unchecked
{
int hash = 17;
hash = hash * 31 + Scale.GetHashCode ();
hash = hash * 31 + Contrast.GetHashCode ();
return hash;
}
}
}
public abstract class BaseResources: IDisposable
{
public virtual void Dispose () { }
public virtual string SuitableValue { get; } = String.Empty;
public virtual Dictionary<BaseQualifier, string> AllValue { get; } = new Dictionary<BaseQualifier, string> ();
/// <summary>
/// 表示是否寻找过,如果真则不用再次寻找。
/// </summary>
public bool IsFind { get; set; }
}
public class StringResources: BaseResources, IDictionary<StringQualifier, string>, IDictionary<string, string>
{
private Dictionary<StringQualifier, string> dict = new Dictionary<StringQualifier, string> ();
public string this [string key]
{
get { return dict [new StringQualifier (key)]; }
set { }
}
public string this [StringQualifier key]
{
get { return dict [key]; }
set { }
}
public int Count => dict.Count;
public bool IsReadOnly => true;
public ICollection<StringQualifier> Keys => dict.Keys;
public ICollection<string> Values => dict.Values;
ICollection<string> IDictionary<string, string>.Keys => dict.Keys.Select (k => k.LocaleName).ToList ();
public void Add (KeyValuePair<string, string> item) { }
public void Add (KeyValuePair<StringQualifier, string> item) { }
public void Add (string key, string value) { }
public void Add (StringQualifier key, string value) { }
public void Clear () { }
public bool Contains (KeyValuePair<string, string> item)
{
string value;
if (TryGetValue (item.Key, out value)) return value == item.Value;
return false;
}
public bool Contains (KeyValuePair<StringQualifier, string> item) => dict.Contains (item);
public bool ContainsKey (string key)
{
foreach (var kv in dict)
{
if (string.Equals (kv.Key.LocaleName, key, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
public bool ContainsKey (StringQualifier key) => dict.ContainsKey (key);
public void CopyTo (KeyValuePair<string, string> [] array, int arrayIndex)
{
if (array == null) throw new ArgumentNullException ("array");
if (arrayIndex < 0 || arrayIndex > array.Length) throw new ArgumentOutOfRangeException ("arrayIndex");
if (array.Length - arrayIndex < dict.Count) throw new ArgumentException ("The destination array is not large enough.");
foreach (var kv in dict)
{
array [arrayIndex++] = new KeyValuePair<string, string> (
kv.Key.LocaleName, kv.Value);
}
}
public void CopyTo (KeyValuePair<StringQualifier, string> [] array, int arrayIndex)
{
if (array == null) throw new ArgumentNullException ("array");
if (arrayIndex < 0 || arrayIndex > array.Length) throw new ArgumentOutOfRangeException ("arrayIndex");
if (array.Length - arrayIndex < dict.Count) throw new ArgumentException ("The destination array is not large enough.");
foreach (var kv in dict)
{
array [arrayIndex++] = new KeyValuePair<StringQualifier, string> (
kv.Key, kv.Value);
}
}
public IEnumerator<KeyValuePair<StringQualifier, string>> GetEnumerator () => dict.GetEnumerator ();
public bool Remove (KeyValuePair<StringQualifier, string> item) { return false; }
public bool Remove (string key) => false;
public bool Remove (KeyValuePair<string, string> item) => false;
public bool Remove (StringQualifier key) { return false; }
public bool TryGetValue (string key, out string value)
{
foreach (var kv in dict)
{
if (string.Equals (kv.Key.LocaleName, key, StringComparison.OrdinalIgnoreCase))
{
value = kv.Value;
return true;
}
}
value = null;
return false;
}
public bool TryGetValue (StringQualifier key, out string value) => dict.TryGetValue (key, out value);
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator ()
{
foreach (var kv in dict)
{
yield return new KeyValuePair<string, string> (kv.Key.LocaleName, kv.Value);
}
}
IEnumerator IEnumerable.GetEnumerator () => dict.GetEnumerator ();
internal static bool LocaleNameEqualsIgnoreRegion (string a, string b)
{
var ca = new CultureInfo (a);
var cb = new CultureInfo (b);
return string.Equals (ca.TwoLetterISOLanguageName, cb.TwoLetterISOLanguageName, StringComparison.OrdinalIgnoreCase);
}
public static string GetCoincidentValue (Dictionary<StringQualifier, string> d, string localeName)
{
if (d == null) return null;
foreach (var kv in d)
{
if (kv.Key.LocaleName?.Trim ()?.ToLower () == localeName?.Trim ()?.ToLower ()) return kv.Value;
}
var targetLang = new StringQualifier (localeName);
foreach (var kv in d)
{
if (kv.Key.LCID == targetLang.LCID) return kv.Value;
}
foreach (var kv in d)
{
if (LocaleNameEqualsIgnoreRegion (kv.Key.LocaleName, localeName)) return kv.Value;
}
return String.Empty;
}
public static string GetSuitableValue (Dictionary<StringQualifier, string> d)
{
var ret = GetCoincidentValue (d, LocaleExt.GetComputerLocaleCode ());
if (String.IsNullOrEmpty (ret)) ret = GetCoincidentValue (d, "en-us");
if (String.IsNullOrEmpty (ret) && d.Count > 0) ret = d.ElementAt (0).Value;
return ret;
}
public override string SuitableValue => GetSuitableValue (dict);
public override Dictionary<BaseQualifier, string> AllValue => dict.ToDictionary (kv => (BaseQualifier)kv.Key, kv => kv.Value);
public override void Dispose ()
{
dict.Clear ();
dict = null;
base.Dispose ();
}
~StringResources () { Dispose (); }
public StringResources (Dictionary<StringQualifier, string> _dict, bool isfind = true)
{
dict = _dict;
IsFind = isfind;
}
public StringResources (Dictionary<string, string> _dict, bool isfind = true)
{
dict = _dict.ToDictionary (kv => new StringQualifier (kv.Key), kv => kv.Value);
IsFind = isfind;
}
public StringResources () { IsFind = false; }
}
public class FileResources: BaseResources, IDictionary<FileQualifier, string>
{
private Dictionary<FileQualifier, string> dict = new Dictionary<FileQualifier, string> ();
public string this [FileQualifier key]
{
get { return dict [key]; }
set { }
}
public int Count => dict.Count;
public bool IsReadOnly => false;
public ICollection<FileQualifier> Keys => dict.Keys;
public ICollection<string> Values => dict.Values;
public void Add (KeyValuePair<FileQualifier, string> item) { }
public void Add (FileQualifier key, string value) { }
public void Clear () { }
public bool Contains (KeyValuePair<FileQualifier, string> item) => dict.Contains (item);
public bool ContainsKey (FileQualifier key) => dict.ContainsKey (key);
public void CopyTo (KeyValuePair<FileQualifier, string> [] array, int arrayIndex) { }
public IEnumerator<KeyValuePair<FileQualifier, string>> GetEnumerator () => dict.GetEnumerator ();
public bool Remove (KeyValuePair<FileQualifier, string> item) => false;
public bool Remove (FileQualifier key) => false;
public bool TryGetValue (FileQualifier key, out string value) => dict.TryGetValue (key, out value);
IEnumerator IEnumerable.GetEnumerator () => dict.GetEnumerator ();
public static string GetCoincidentValue (Dictionary<FileQualifier, string> d, int scale, Contrast contrast = Contrast.None)
{
var td = d.OrderBy (k => k.Key.Contrast).ThenBy (k => k.Key.Scale);
foreach (var kv in td)
{
if (kv.Key.Contrast == contrast)
{
if (kv.Key.Scale >= scale) return kv.Value;
}
}
foreach (var kv in td)
{
if (kv.Key.Contrast == Contrast.None)
if (kv.Key.Scale >= scale) return kv.Value;
}
foreach (var kv in td)
{
if (kv.Key.Contrast == Contrast.Black)
if (kv.Key.Scale >= scale) return kv.Value;
}
foreach (var kv in td)
{
if (kv.Key.Scale >= scale) return kv.Value;
}
if (d.Count > 0) return d.ElementAt (0).Value;
return String.Empty;
}
public static string GetSuitableValue (Dictionary<FileQualifier, string> d, Contrast contrast = Contrast.None) => GetCoincidentValue (d, UIExt.DPI, contrast);
public override string SuitableValue => GetSuitableValue (dict);
public override Dictionary<BaseQualifier, string> AllValue => dict.ToDictionary (kv => (BaseQualifier)kv.Key, kv => kv.Value);
public override void Dispose ()
{
dict.Clear ();
dict = null;
base.Dispose ();
}
~FileResources () { Dispose (); }
public FileResources (Dictionary<FileQualifier, string> _dict, bool isfind = true)
{
dict = _dict;
IsFind = isfind;
}
public FileResources (Dictionary<Tuple<int, Contrast>, string> _dict, bool isfind = true)
{
dict = _dict.ToDictionary (kv => new FileQualifier (kv.Key.Item1, kv.Key.Item2), kv => kv.Value);
IsFind = isfind;
}
public FileResources () { IsFind = false; }
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("PriFormat")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PriFormat")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//将 ComVisible 设置为 false 将使此程序集中的类型
//对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型,
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible (true)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("676e9bd2-a704-4539-9a88-e46654fc94b6")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
namespace PriFormat
{
public class ReferencedFileSection: Section
{
public IList<ReferencedFile> ReferencedFiles { get; private set; }
internal const string Identifier = "[def_file_list]\0";
internal ReferencedFileSection (PriFile priFile)
: base (Identifier, priFile)
{
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
ushort numRoots = binaryReader.ReadUInt16 ();
ushort numFolders = binaryReader.ReadUInt16 ();
ushort numFiles = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
uint totalDataLength = binaryReader.ReadUInt32 ();
List<FolderInfo> folderInfos = new List<FolderInfo> (numFolders);
for (int i = 0; i < numFolders; i++)
{
binaryReader.ExpectUInt16 (0);
ushort parentFolder = binaryReader.ReadUInt16 ();
ushort numFoldersInFolder = binaryReader.ReadUInt16 ();
ushort firstFolderInFolder = binaryReader.ReadUInt16 ();
ushort numFilesInFolder = binaryReader.ReadUInt16 ();
ushort firstFileInFolder = binaryReader.ReadUInt16 ();
ushort folderNameLength = binaryReader.ReadUInt16 ();
ushort fullPathLength = binaryReader.ReadUInt16 ();
uint folderNameOffset = binaryReader.ReadUInt32 ();
folderInfos.Add (new FolderInfo (
parentFolder,
numFoldersInFolder,
firstFolderInFolder,
numFilesInFolder,
firstFileInFolder,
folderNameLength,
fullPathLength,
folderNameOffset));
}
List<FileInfo> fileInfos = new List<FileInfo> (numFiles);
for (int i = 0; i < numFiles; i++)
{
binaryReader.ReadUInt16 ();
ushort parentFolder = binaryReader.ReadUInt16 ();
ushort fullPathLength = binaryReader.ReadUInt16 ();
ushort fileNameLength = binaryReader.ReadUInt16 ();
uint fileNameOffset = binaryReader.ReadUInt32 ();
fileInfos.Add (new FileInfo (parentFolder, fullPathLength, fileNameLength, fileNameOffset));
}
long dataStartPosition = binaryReader.BaseStream.Position;
List<ReferencedFolder> referencedFolders = new List<ReferencedFolder> (numFolders);
for (int i = 0; i < numFolders; i++)
{
binaryReader.BaseStream.Seek (dataStartPosition + folderInfos [i].FolderNameOffset * 2, SeekOrigin.Begin);
string name = binaryReader.ReadString (Encoding.Unicode, folderInfos [i].FolderNameLength);
referencedFolders.Add (new ReferencedFolder (null, name));
}
for (int i = 0; i < numFolders; i++)
{
if (folderInfos [i].ParentFolder != 0xFFFF)
referencedFolders [i].Parent = referencedFolders [folderInfos [i].ParentFolder];
}
List<ReferencedFile> referencedFiles = new List<ReferencedFile> (numFiles);
for (int i = 0; i < numFiles; i++)
{
binaryReader.BaseStream.Seek (dataStartPosition + fileInfos [i].FileNameOffset * 2, SeekOrigin.Begin);
string name = binaryReader.ReadString (Encoding.Unicode, fileInfos [i].FileNameLength);
ReferencedFolder parentFolder;
if (fileInfos [i].ParentFolder != 0xFFFF)
parentFolder = referencedFolders [fileInfos [i].ParentFolder];
else
parentFolder = null;
referencedFiles.Add (new ReferencedFile (parentFolder, name));
}
for (int i = 0; i < numFolders; i++)
{
List<ReferencedEntry> children = new List<ReferencedEntry> (
folderInfos [i].NumFoldersInFolder + folderInfos [i].NumFilesInFolder);
for (int j = 0; j < folderInfos [i].NumFoldersInFolder; j++)
children.Add (referencedFolders [folderInfos [i].FirstFolderInFolder + j]);
for (int j = 0; j < folderInfos [i].NumFilesInFolder; j++)
children.Add (referencedFiles [folderInfos [i].FirstFileInFolder + j]);
referencedFolders [i].Children = children;
}
ReferencedFiles = new ReadOnlyCollection<ReferencedFile> (referencedFiles);
return true;
}
private struct FolderInfo
{
public ushort ParentFolder;
public ushort NumFoldersInFolder;
public ushort FirstFolderInFolder;
public ushort NumFilesInFolder;
public ushort FirstFileInFolder;
public ushort FolderNameLength;
public ushort FullPathLength;
public uint FolderNameOffset;
public FolderInfo (
ushort parentFolder,
ushort numFoldersInFolder,
ushort firstFolderInFolder,
ushort numFilesInFolder,
ushort firstFileInFolder,
ushort folderNameLength,
ushort fullPathLength,
uint folderNameOffset)
{
ParentFolder = parentFolder;
NumFoldersInFolder = numFoldersInFolder;
FirstFolderInFolder = firstFolderInFolder;
NumFilesInFolder = numFilesInFolder;
FirstFileInFolder = firstFileInFolder;
FolderNameLength = folderNameLength;
FullPathLength = fullPathLength;
FolderNameOffset = folderNameOffset;
}
}
private struct FileInfo
{
public ushort ParentFolder;
public ushort FullPathLength;
public ushort FileNameLength;
public uint FileNameOffset;
public FileInfo (ushort parentFolder, ushort fullPathLength, ushort fileNameLength, uint fileNameOffset)
{
ParentFolder = parentFolder;
FullPathLength = fullPathLength;
FileNameLength = fileNameLength;
FileNameOffset = fileNameOffset;
}
}
public override void Dispose ()
{
ReferencedFiles?.Clear ();
ReferencedFiles = null;
base.Dispose ();
}
~ReferencedFileSection () { Dispose (); }
}
public abstract class ReferencedEntry: IDisposable
{
public ReferencedFolder Parent { get; internal set; }
public string Name { get; private set; }
internal ReferencedEntry (ReferencedFolder parent, string name)
{
Parent = parent;
Name = name;
}
private string fullName;
public string FullName
{
get
{
if (fullName == null)
{
if (Parent == null)
fullName = Name;
else
fullName = Parent.FullName + "\\" + Name;
}
return fullName;
}
}
public virtual void Dispose ()
{
Parent = null;
}
~ReferencedEntry () { Dispose (); }
}
public sealed class ReferencedFolder: ReferencedEntry
{
internal ReferencedFolder (ReferencedFolder parent, string name)
: base (parent, name)
{
}
public IList<ReferencedEntry> Children { get; internal set; }
public override void Dispose ()
{
Children?.Clear ();
Children = null;
base.Dispose ();
}
~ReferencedFolder () { Dispose (); }
}
public sealed class ReferencedFile: ReferencedEntry
{
internal ReferencedFile (ReferencedFolder parent, string name)
: base (parent, name)
{
}
}
public class ReferencedFileRef
{
internal int fileIndex;
public int FileIndex => fileIndex;
internal ReferencedFileRef (int fileIndex)
{
this.fileIndex = fileIndex;
}
}
}

View File

@@ -0,0 +1,414 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace PriFormat
{
public class ResourceMapSection: Section
{
public HierarchicalSchemaReference HierarchicalSchemaReference { get; private set; }
public SectionRef<HierarchicalSchemaSection> SchemaSection { get; private set; }
public SectionRef<DecisionInfoSection> DecisionInfoSection { get; private set; }
public IDictionary<ushort, CandidateSet> CandidateSets { get; private set; }
readonly bool version2;
internal const string Identifier1 = "[mrm_res_map__]\0";
internal const string Identifier2 = "[mrm_res_map2_]\0";
internal ResourceMapSection (PriFile priFile, bool version2)
: base (version2 ? Identifier2 : Identifier1, priFile)
{
this.version2 = version2;
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
long sectionPosition = (binaryReader.BaseStream as SubStream) != null ?
((SubStream)binaryReader.BaseStream).Position : 0;
ushort environmentReferencesLength = binaryReader.ReadUInt16 ();
ushort numEnvironmentReferences = binaryReader.ReadUInt16 ();
if (!version2)
{
if (environmentReferencesLength == 0 || numEnvironmentReferences == 0)
throw new InvalidDataException ();
}
else
{
if (environmentReferencesLength != 0 || numEnvironmentReferences != 0)
throw new InvalidDataException ();
}
SchemaSection = new SectionRef<HierarchicalSchemaSection> (binaryReader.ReadUInt16 ());
ushort hierarchicalSchemaReferenceLength = binaryReader.ReadUInt16 ();
DecisionInfoSection = new SectionRef<DecisionInfoSection> (binaryReader.ReadUInt16 ());
ushort resourceValueTypeTableSize = binaryReader.ReadUInt16 ();
ushort ItemToItemInfoGroupCount = binaryReader.ReadUInt16 ();
ushort itemInfoGroupCount = binaryReader.ReadUInt16 ();
uint itemInfoCount = binaryReader.ReadUInt32 ();
uint numCandidates = binaryReader.ReadUInt32 ();
uint dataLength = binaryReader.ReadUInt32 ();
uint largeTableLength = binaryReader.ReadUInt32 ();
if (PriFile.GetSectionByRef (DecisionInfoSection) == null)
return false;
byte [] environmentReferencesData = binaryReader.ReadBytes (environmentReferencesLength);
byte [] schemaReferenceData = binaryReader.ReadBytes (hierarchicalSchemaReferenceLength);
if (schemaReferenceData.Length != 0)
{
using (BinaryReader r = new BinaryReader (new MemoryStream (schemaReferenceData, false)))
{
ushort majorVersion = r.ReadUInt16 ();
ushort minorVersion = r.ReadUInt16 ();
r.ExpectUInt32 (0);
uint checksum = r.ReadUInt32 ();
uint numScopes = r.ReadUInt32 ();
uint numItems = r.ReadUInt32 ();
HierarchicalSchemaVersionInfo versionInfo = new HierarchicalSchemaVersionInfo (
majorVersion, minorVersion, checksum, numScopes, numItems);
ushort stringDataLength = r.ReadUInt16 ();
r.ExpectUInt16 (0);
uint unknown1 = r.ReadUInt32 ();
uint unknown2 = r.ReadUInt32 ();
string uniqueName = r.ReadNullTerminatedString (Encoding.Unicode);
if (uniqueName.Length != stringDataLength - 1)
throw new InvalidDataException ();
HierarchicalSchemaReference = new HierarchicalSchemaReference (versionInfo, unknown1, unknown2, uniqueName);
}
}
List<ResourceValueType> resourceValueTypeTable = new List<ResourceValueType> (resourceValueTypeTableSize);
for (int i = 0; i < resourceValueTypeTableSize; i++)
{
binaryReader.ExpectUInt32 (4);
ResourceValueType resourceValueType = (ResourceValueType)binaryReader.ReadUInt32 ();
resourceValueTypeTable.Add (resourceValueType);
}
List<ItemToItemInfoGroup> itemToItemInfoGroups = new List<ItemToItemInfoGroup> (ItemToItemInfoGroupCount);
for (int i = 0; i < ItemToItemInfoGroupCount; i++)
{
ushort firstItem = binaryReader.ReadUInt16 ();
ushort itemInfoGroup = binaryReader.ReadUInt16 ();
itemToItemInfoGroups.Add (new ItemToItemInfoGroup (firstItem, itemInfoGroup));
}
List<ItemInfoGroup> itemInfoGroups = new List<ItemInfoGroup> (itemInfoGroupCount);
for (int i = 0; i < itemInfoGroupCount; i++)
{
ushort groupSize = binaryReader.ReadUInt16 ();
ushort firstItemInfo = binaryReader.ReadUInt16 ();
itemInfoGroups.Add (new ItemInfoGroup (groupSize, firstItemInfo));
}
List<ItemInfo> itemInfos = new List<ItemInfo> ((int)itemInfoCount);
for (int i = 0; i < itemInfoCount; i++)
{
ushort decision = binaryReader.ReadUInt16 ();
ushort firstCandidate = binaryReader.ReadUInt16 ();
itemInfos.Add (new ItemInfo (decision, firstCandidate));
}
byte [] largeTable = binaryReader.ReadBytes ((int)largeTableLength);
if (largeTable.Length != 0)
{
using (BinaryReader r = new BinaryReader (new MemoryStream (largeTable, false)))
{
uint ItemToItemInfoGroupCountLarge = r.ReadUInt32 ();
uint itemInfoGroupCountLarge = r.ReadUInt32 ();
uint itemInfoCountLarge = r.ReadUInt32 ();
for (int i = 0; i < ItemToItemInfoGroupCountLarge; i++)
{
uint firstItem = r.ReadUInt32 ();
uint itemInfoGroup = r.ReadUInt32 ();
itemToItemInfoGroups.Add (new ItemToItemInfoGroup (firstItem, itemInfoGroup));
}
for (int i = 0; i < itemInfoGroupCountLarge; i++)
{
uint groupSize = r.ReadUInt32 ();
uint firstItemInfo = r.ReadUInt32 ();
itemInfoGroups.Add (new ItemInfoGroup (groupSize, firstItemInfo));
}
for (int i = 0; i < itemInfoCountLarge; i++)
{
uint decision = r.ReadUInt32 ();
uint firstCandidate = r.ReadUInt32 ();
itemInfos.Add (new ItemInfo (decision, firstCandidate));
}
if (r.BaseStream.Position != r.BaseStream.Length)
throw new InvalidDataException ();
}
}
List<CandidateInfo> candidateInfos = new List<CandidateInfo> ((int)numCandidates);
for (int i = 0; i < numCandidates; i++)
{
byte type = binaryReader.ReadByte ();
if (type == 0x01)
{
ResourceValueType resourceValueType = resourceValueTypeTable [binaryReader.ReadByte ()];
ushort sourceFileIndex = binaryReader.ReadUInt16 ();
ushort valueLocation = binaryReader.ReadUInt16 ();
ushort dataItemSection = binaryReader.ReadUInt16 ();
candidateInfos.Add (new CandidateInfo (resourceValueType, sourceFileIndex, valueLocation, dataItemSection));
}
else if (type == 0x00)
{
ResourceValueType resourceValueType = resourceValueTypeTable [binaryReader.ReadByte ()];
ushort length = binaryReader.ReadUInt16 ();
uint stringOffset = binaryReader.ReadUInt32 ();
candidateInfos.Add (new CandidateInfo (resourceValueType, length, stringOffset));
}
else
{
throw new InvalidDataException ();
}
}
long stringDataStartOffset = binaryReader.BaseStream.Position;
Dictionary<ushort, CandidateSet> candidateSets = new Dictionary<ushort, CandidateSet> ();
for (int itemToItemInfoGroupIndex = 0; itemToItemInfoGroupIndex < itemToItemInfoGroups.Count; itemToItemInfoGroupIndex++)
{
ItemToItemInfoGroup itemToItemInfoGroup = itemToItemInfoGroups [itemToItemInfoGroupIndex];
ItemInfoGroup itemInfoGroup;
if (itemToItemInfoGroup.ItemInfoGroup < itemInfoGroups.Count)
itemInfoGroup = itemInfoGroups [(int)itemToItemInfoGroup.ItemInfoGroup];
else
itemInfoGroup = new ItemInfoGroup (1, (uint)(itemToItemInfoGroup.ItemInfoGroup - itemInfoGroups.Count));
for (uint itemInfoIndex = itemInfoGroup.FirstItemInfo; itemInfoIndex < itemInfoGroup.FirstItemInfo + itemInfoGroup.GroupSize; itemInfoIndex++)
{
ItemInfo itemInfo = itemInfos [(int)itemInfoIndex];
ushort decisionIndex = (ushort)itemInfo.Decision;
Decision decision = PriFile.GetSectionByRef (DecisionInfoSection).Decisions [decisionIndex];
List<Candidate> candidates = new List<Candidate> (decision.QualifierSets.Count);
for (int i = 0; i < decision.QualifierSets.Count; i++)
{
CandidateInfo candidateInfo = candidateInfos [(int)itemInfo.FirstCandidate + i];
if (candidateInfo.Type == 0x01)
{
ReferencedFileRef sourceFile = null;
if (candidateInfo.SourceFileIndex != 0)
sourceFile = new ReferencedFileRef (candidateInfo.SourceFileIndex - 1);
candidates.Add (new Candidate (decision.QualifierSets [i].Index, candidateInfo.ResourceValueType, sourceFile,
new DataItemRef (new SectionRef<DataItemSection> (candidateInfo.DataItemSection), candidateInfo.DataItemIndex)));
}
else if (candidateInfo.Type == 0x00)
{
ByteSpan data = new ByteSpan (sectionPosition + stringDataStartOffset + candidateInfo.DataOffset, candidateInfo.DataLength);
candidates.Add (new Candidate (decision.QualifierSets [i].Index, candidateInfo.ResourceValueType, data));
}
}
ushort resourceMapItemIndex = (ushort)(itemToItemInfoGroup.FirstItem + (itemInfoIndex - itemInfoGroup.FirstItemInfo));
CandidateSet candidateSet = new CandidateSet (
new ResourceMapItemRef (SchemaSection, resourceMapItemIndex),
decisionIndex,
candidates);
candidateSets.Add (resourceMapItemIndex, candidateSet);
}
}
CandidateSets = candidateSets;
return true;
}
private struct ItemToItemInfoGroup
{
public uint FirstItem;
public uint ItemInfoGroup;
public ItemToItemInfoGroup (uint firstItem, uint itemInfoGroup)
{
FirstItem = firstItem;
ItemInfoGroup = itemInfoGroup;
}
}
private struct ItemInfoGroup
{
public uint GroupSize;
public uint FirstItemInfo;
public ItemInfoGroup (uint groupSize, uint firstItemInfo)
{
GroupSize = groupSize;
FirstItemInfo = firstItemInfo;
}
}
private struct ItemInfo
{
public uint Decision;
public uint FirstCandidate;
public ItemInfo (uint decision, uint firstCandidate)
{
Decision = decision;
FirstCandidate = firstCandidate;
}
}
private struct CandidateInfo
{
public byte Type;
public ResourceValueType ResourceValueType;
// Type 1
public ushort SourceFileIndex;
public ushort DataItemIndex;
public ushort DataItemSection;
// Type 0
public ushort DataLength;
public uint DataOffset;
public CandidateInfo (ResourceValueType resourceValueType, ushort sourceFileIndex, ushort dataItemIndex, ushort dataItemSection)
{
Type = 0x01;
ResourceValueType = resourceValueType;
SourceFileIndex = sourceFileIndex;
DataItemIndex = dataItemIndex;
DataItemSection = dataItemSection;
DataLength = 0;
DataOffset = 0;
}
public CandidateInfo (ResourceValueType resourceValueType, ushort dataLength, uint dataOffset)
{
Type = 0x00;
ResourceValueType = resourceValueType;
SourceFileIndex = 0;
DataItemIndex = 0;
DataItemSection = 0;
DataLength = dataLength;
DataOffset = dataOffset;
}
}
public override void Dispose ()
{
HierarchicalSchemaReference = null;
CandidateSets?.Clear ();
CandidateSets = null;
base.Dispose ();
}
~ResourceMapSection () { Dispose (); }
}
public enum ResourceValueType
{
String,
Path,
EmbeddedData,
AsciiString,
Utf8String,
AsciiPath,
Utf8Path
}
public class CandidateSet: IDisposable
{
public ResourceMapItemRef ResourceMapItem { get; private set; }
public ushort DecisionIndex { get; private set; }
public IList<Candidate> Candidates { get; private set; }
internal CandidateSet (ResourceMapItemRef resourceMapItem, ushort decisionIndex, IList<Candidate> candidates)
{
ResourceMapItem = resourceMapItem;
DecisionIndex = decisionIndex;
Candidates = candidates;
}
public virtual void Dispose ()
{
Candidates?.Clear ();
Candidates = null;
}
~CandidateSet () { Dispose (); }
}
public class Candidate
{
public ushort QualifierSet { get; private set; }
public ResourceValueType Type { get; private set; }
public ReferencedFileRef SourceFile { get; private set; }
public DataItemRef DataItem { get; private set; }
public ByteSpan Data { get; private set; }
internal Candidate (ushort qualifierSet, ResourceValueType type, ReferencedFileRef sourceFile, DataItemRef dataItem)
{
QualifierSet = qualifierSet;
Type = type;
SourceFile = sourceFile;
DataItem = dataItem;
}
internal Candidate (ushort qualifierSet, ResourceValueType type, ByteSpan data)
{
QualifierSet = qualifierSet;
Type = type;
SourceFile = null;
DataItem = null;
Data = data;
}
}
public class HierarchicalSchemaReference
{
public HierarchicalSchemaVersionInfo VersionInfo { get; private set; }
public uint Unknown1 { get; private set; }
public uint Unknown2 { get; private set; }
public string UniqueName { get; private set; }
internal HierarchicalSchemaReference (HierarchicalSchemaVersionInfo versionInfo, uint unknown1, uint unknown2, string uniqueName)
{
VersionInfo = versionInfo;
Unknown1 = unknown1;
Unknown2 = unknown2;
UniqueName = uniqueName;
}
}
public struct ResourceMapItemRef
{
internal SectionRef<HierarchicalSchemaSection> schemaSection;
internal int itemIndex;
public SectionRef<HierarchicalSchemaSection> SchemaSection => schemaSection;
public int ItemIndex => itemIndex;
internal ResourceMapItemRef (SectionRef<HierarchicalSchemaSection> schemaSection, int itemIndex)
{
this.schemaSection = schemaSection;
this.itemIndex = itemIndex;
}
}
}

View File

@@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
namespace PriFormat
{
public class ReverseMapSection: Section
{
public uint [] Mapping { get; private set; }
public IList<ResourceMapScope> Scopes { get; private set; }
public IList<ResourceMapItem> Items { get; private set; }
internal const string Identifier = "[mrm_rev_map] \0";
internal ReverseMapSection (PriFile priFile)
: base (Identifier, priFile)
{
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
uint numItems = binaryReader.ReadUInt32 ();
binaryReader.ExpectUInt32 ((uint)(binaryReader.BaseStream.Length - 8));
uint [] mapping = new uint [numItems];
for (int i = 0; i < numItems; i++)
mapping [i] = binaryReader.ReadUInt32 ();
Mapping = mapping;
ushort maxFullPathLength = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
uint numEntries = binaryReader.ReadUInt32 ();
uint numScopes = binaryReader.ReadUInt32 ();
if (numEntries != numScopes + numItems)
throw new InvalidDataException ();
binaryReader.ExpectUInt32 (numItems);
uint unicodeDataLength = binaryReader.ReadUInt32 ();
binaryReader.ReadUInt32 (); // meaning unknown
List<ScopeAndItemInfo> scopeAndItemInfos = new List<ScopeAndItemInfo> ((int)(numScopes + numItems));
for (int i = 0; i < numScopes + numItems; i++)
{
ushort parent = binaryReader.ReadUInt16 ();
ushort fullPathLength = binaryReader.ReadUInt16 ();
char uppercaseFirstChar = (char)binaryReader.ReadUInt16 ();
byte nameLength2 = binaryReader.ReadByte ();
byte flags = binaryReader.ReadByte ();
uint nameOffset = binaryReader.ReadUInt16 () | (uint)((flags & 0xF) << 16);
ushort index = binaryReader.ReadUInt16 ();
scopeAndItemInfos.Add (new ScopeAndItemInfo (parent, fullPathLength, flags, nameOffset, index));
}
List<ScopeExInfo> scopeExInfos = new List<ScopeExInfo> ((int)numScopes);
for (int i = 0; i < numScopes; i++)
{
ushort scopeIndex = binaryReader.ReadUInt16 ();
ushort childCount = binaryReader.ReadUInt16 ();
ushort firstChildIndex = binaryReader.ReadUInt16 ();
binaryReader.ExpectUInt16 (0);
scopeExInfos.Add (new ScopeExInfo (scopeIndex, childCount, firstChildIndex));
}
ushort [] itemIndexPropertyToIndex = new ushort [numItems];
for (int i = 0; i < numItems; i++)
itemIndexPropertyToIndex [i] = binaryReader.ReadUInt16 ();
long unicodeDataOffset = binaryReader.BaseStream.Position;
long asciiDataOffset = binaryReader.BaseStream.Position + unicodeDataLength * 2;
ResourceMapScope [] scopes = new ResourceMapScope [numScopes];
ResourceMapItem [] items = new ResourceMapItem [numItems];
for (int i = 0; i < numScopes + numItems; i++)
{
long pos;
if (scopeAndItemInfos [i].NameInAscii)
pos = asciiDataOffset + scopeAndItemInfos [i].NameOffset;
else
pos = unicodeDataOffset + scopeAndItemInfos [i].NameOffset * 2;
binaryReader.BaseStream.Seek (pos, SeekOrigin.Begin);
string name;
if (scopeAndItemInfos [i].FullPathLength != 0)
name = binaryReader.ReadNullTerminatedString (
scopeAndItemInfos [i].NameInAscii ? Encoding.ASCII : Encoding.Unicode);
else
name = string.Empty;
ushort index = scopeAndItemInfos [i].Index;
if (scopeAndItemInfos [i].IsScope)
{
if (scopes [index] != null)
throw new InvalidDataException ();
scopes [index] = new ResourceMapScope (index, null, name);
}
else
{
if (items [index] != null)
throw new InvalidDataException ();
items [index] = new ResourceMapItem (index, null, name);
}
}
for (int i = 0; i < numScopes + numItems; i++)
{
ushort index = scopeAndItemInfos [i].Index;
ushort parent = scopeAndItemInfos [scopeAndItemInfos [i].Parent].Index;
if (parent != 0xFFFF)
{
if (scopeAndItemInfos [i].IsScope)
{
if (parent != index)
scopes [index].Parent = scopes [parent];
}
else
items [index].Parent = scopes [parent];
}
}
for (int i = 0; i < numScopes; i++)
{
List<ResourceMapEntry> children = new List<ResourceMapEntry> (scopeExInfos [i].ChildCount);
for (int j = 0; j < scopeExInfos [i].ChildCount; j++)
{
ScopeAndItemInfo saiInfo = scopeAndItemInfos [scopeExInfos [i].FirstChildIndex + j];
if (saiInfo.IsScope)
children.Add (scopes [saiInfo.Index]);
else
children.Add (items [saiInfo.Index]);
}
scopes [i].Children = children;
}
Scopes = new ReadOnlyCollection<ResourceMapScope> (scopes);
Items = new ReadOnlyCollection<ResourceMapItem> (items);
return true;
}
private struct ScopeAndItemInfo
{
public ushort Parent;
public ushort FullPathLength;
public byte Flags;
public uint NameOffset;
public ushort Index;
public ScopeAndItemInfo (ushort parent, ushort fullPathLength, byte flags, uint nameOffset, ushort index)
{
Parent = parent;
FullPathLength = fullPathLength;
Flags = flags;
NameOffset = nameOffset;
Index = index;
}
public bool IsScope
{
get { return (Flags & 0x10) != 0; }
}
public bool NameInAscii
{
get { return (Flags & 0x20) != 0; }
}
}
private struct ScopeExInfo
{
public ushort ScopeIndex;
public ushort ChildCount;
public ushort FirstChildIndex;
public ScopeExInfo (ushort scopeIndex, ushort childCount, ushort firstChildIndex)
{
ScopeIndex = scopeIndex;
ChildCount = childCount;
FirstChildIndex = firstChildIndex;
}
}
public override void Dispose ()
{
Mapping = null;
Scopes?.Clear ();
Scopes = null;
Items?.Clear ();
Items = null;
base.Dispose ();
}
~ReverseMapSection () { Dispose (); }
}
}

126
PriFormat/Section.cs Normal file
View File

@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace PriFormat
{
public abstract class Section: IDisposable
{
protected PriFile PriFile { get; private set; }
public string SectionIdentifier { get; private set; }
public uint SectionQualifier { get; private set; }
public uint Flags { get; private set; }
public uint SectionFlags { get; private set; }
public uint SectionLength { get; private set; }
protected Section (string sectionIdentifier, PriFile priFile)
{
if (sectionIdentifier == null)
throw new ArgumentNullException ("sectionIdentifier");
if (sectionIdentifier.Length != 16)
throw new ArgumentException (
"Section identifiers must be exactly 16 characters long.",
"sectionIdentifier");
SectionIdentifier = sectionIdentifier;
PriFile = priFile;
}
internal bool Parse (BinaryReader binaryReader)
{
// identifier
string identifier = new string (binaryReader.ReadChars (16));
if (identifier != SectionIdentifier)
throw new InvalidDataException ("Unexpected section identifier.");
SectionQualifier = binaryReader.ReadUInt32 ();
Flags = binaryReader.ReadUInt16 ();
SectionFlags = binaryReader.ReadUInt16 ();
SectionLength = binaryReader.ReadUInt32 ();
binaryReader.ExpectUInt32 (0);
// 跳到 section 尾部校验
long contentLength = SectionLength - 16 - 24;
binaryReader.BaseStream.Seek (contentLength, SeekOrigin.Current);
binaryReader.ExpectUInt32 (0xDEF5FADE);
binaryReader.ExpectUInt32 (SectionLength);
// 回到 section 内容起始位置
binaryReader.BaseStream.Seek (-8 - contentLength, SeekOrigin.Current);
//关键点SubStream + BinaryReader 生命周期
using (SubStream subStream = new SubStream (
binaryReader.BaseStream,
binaryReader.BaseStream.Position,
contentLength))
{
using (BinaryReader subReader =
new BinaryReader (subStream, Encoding.ASCII))
{
return ParseSectionContent (subReader);
}
}
}
protected abstract bool ParseSectionContent (BinaryReader binaryReader);
public override string ToString ()
{
return SectionIdentifier.TrimEnd ('\0', ' ') +
" length: " + SectionLength;
}
internal static Section CreateForIdentifier (
string sectionIdentifier,
PriFile priFile)
{
if (sectionIdentifier == null)
throw new ArgumentNullException ("sectionIdentifier");
switch (sectionIdentifier)
{
case PriDescriptorSection.Identifier:
return new PriDescriptorSection (priFile);
case HierarchicalSchemaSection.Identifier1:
return new HierarchicalSchemaSection (priFile, false);
case HierarchicalSchemaSection.Identifier2:
return new HierarchicalSchemaSection (priFile, true);
case DecisionInfoSection.Identifier:
return new DecisionInfoSection (priFile);
case ResourceMapSection.Identifier1:
return new ResourceMapSection (priFile, false);
case ResourceMapSection.Identifier2:
return new ResourceMapSection (priFile, true);
case DataItemSection.Identifier:
return new DataItemSection (priFile);
case ReverseMapSection.Identifier:
return new ReverseMapSection (priFile);
case ReferencedFileSection.Identifier:
return new ReferencedFileSection (priFile);
default:
return new UnknownSection (sectionIdentifier, priFile);
}
}
public virtual void Dispose ()
{
this.PriFile = null;
}
}
}

171
PriFormat/StreamHelper.cs Normal file
View File

@@ -0,0 +1,171 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace PriFormat
{
public static class StreamHelper
{
public static Stream FromIStream (IStream comStream)
{
if (comStream == null) return null;
return new ComIStreamBufferedReader (comStream);
}
public static Stream FromIStream (IntPtr comStreamPtr)
{
if (comStreamPtr == IntPtr.Zero) return null;
IStream comStream =
(IStream)Marshal.GetObjectForIUnknown (comStreamPtr);
return new ComIStreamBufferedReader (comStream);
}
}
internal sealed class ComIStreamBufferedReader: Stream
{
private readonly MemoryStream _memory;
private bool _disposed;
public ComIStreamBufferedReader (IStream comStream)
{
if (comStream == null)
throw new ArgumentNullException ("comStream");
_memory = LoadAll (comStream);
}
/// <summary>
/// 一次性把 IStream 全部复制到托管内存
/// </summary>
private static MemoryStream LoadAll (IStream stream)
{
// 保存原始位置
long originalPos = GetPosition (stream);
try
{
// Seek 到头
stream.Seek (0, 0 /* STREAM_SEEK_SET */, IntPtr.Zero);
// 获取长度
System.Runtime.InteropServices.ComTypes.STATSTG stat;
stream.Stat (out stat, 1); // STATFLAG_NONAME
long length = stat.cbSize;
if (length < 0 || length > int.MaxValue)
throw new NotSupportedException ("Stream too large to buffer.");
MemoryStream ms = new MemoryStream ((int)length);
byte [] buffer = new byte [64 * 1024]; // 64KB
IntPtr pcbRead = Marshal.AllocHGlobal (sizeof (int));
try
{
while (true)
{
stream.Read (buffer, buffer.Length, pcbRead);
int read = Marshal.ReadInt32 (pcbRead);
if (read <= 0)
break;
ms.Write (buffer, 0, read);
}
}
finally
{
Marshal.FreeHGlobal (pcbRead);
}
ms.Position = 0;
return ms;
}
finally
{
// 恢复 COM IStream 的原始位置
stream.Seek (originalPos, 0 /* STREAM_SEEK_SET */, IntPtr.Zero);
}
}
private static long GetPosition (IStream stream)
{
IntPtr posPtr = Marshal.AllocHGlobal (sizeof (long));
try
{
stream.Seek (0, 1 /* STREAM_SEEK_CUR */, posPtr);
return Marshal.ReadInt64 (posPtr);
}
finally
{
Marshal.FreeHGlobal (posPtr);
}
}
// ===== Stream 重写,全部委托给 MemoryStream =====
public override bool CanRead { get { return !_disposed; } }
public override bool CanSeek { get { return !_disposed; } }
public override bool CanWrite { get { return false; } }
public override long Length
{
get { EnsureNotDisposed (); return _memory.Length; }
}
public override long Position
{
get { EnsureNotDisposed (); return _memory.Position; }
set { EnsureNotDisposed (); _memory.Position = value; }
}
public override int Read (byte [] buffer, int offset, int count)
{
EnsureNotDisposed ();
return _memory.Read (buffer, offset, count);
}
public override long Seek (long offset, SeekOrigin origin)
{
EnsureNotDisposed ();
return _memory.Seek (offset, origin);
}
public override void Flush ()
{
// no-op
}
public override void SetLength (long value)
{
throw new NotSupportedException ();
}
public override void Write (byte [] buffer, int offset, int count)
{
throw new NotSupportedException ();
}
protected override void Dispose (bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_memory.Dispose ();
}
_disposed = true;
base.Dispose (disposing);
}
private void EnsureNotDisposed ()
{
if (_disposed)
throw new ObjectDisposedException ("ComIStreamBufferedReader");
}
}
}

110
PriFormat/SubStream.cs Normal file
View File

@@ -0,0 +1,110 @@
using System;
using System.IO;
namespace PriFormat
{
internal sealed class SubStream: Stream
{
private readonly Stream _baseStream;
private readonly long _baseOffset;
private readonly long _length;
private long _position;
public SubStream (Stream baseStream, long offset, long length)
{
if (baseStream == null)
throw new ArgumentNullException ("baseStream");
if (!baseStream.CanSeek)
throw new ArgumentException ("Base stream must be seekable.");
_baseStream = baseStream;
_baseOffset = offset;
_length = length;
_position = 0;
}
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return true; } }
public override bool CanWrite { get { return false; } }
public override long Length
{
get { return _length; }
}
public override long Position
{
get { return _position; }
set
{
if (value < 0 || value > _length)
throw new ArgumentOutOfRangeException ("value");
_position = value;
}
}
public override int Read (byte [] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException ("buffer");
if (offset < 0 || count < 0 || buffer.Length - offset < count)
throw new ArgumentOutOfRangeException ();
long remaining = _length - _position;
if (remaining <= 0)
return 0;
if (count > remaining)
count = (int)remaining;
_baseStream.Position = _baseOffset + _position;
int read = _baseStream.Read (buffer, offset, count);
_position += read;
return read;
}
public override long Seek (long offset, SeekOrigin origin)
{
long target;
switch (origin)
{
case SeekOrigin.Begin:
target = offset;
break;
case SeekOrigin.Current:
target = _position + offset;
break;
case SeekOrigin.End:
target = _length + offset;
break;
default:
throw new ArgumentException ("origin");
}
if (target < 0 || target > _length)
throw new IOException ("Seek out of range.");
_position = target;
return _position;
}
public override void Flush ()
{
// no-op (read-only)
}
public override void SetLength (long value)
{
throw new NotSupportedException ();
}
public override void Write (byte [] buffer, int offset, int count)
{
throw new NotSupportedException ();
}
}
}

35
PriFormat/TocEntry.cs Normal file
View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace PriFormat
{
public sealed class TocEntry
{
public string SectionIdentifier { get; private set; }
public ushort Flags { get; private set; }
public ushort SectionFlags { get; private set; }
public uint SectionQualifier { get; private set; }
public uint SectionOffset { get; private set; }
public uint SectionLength { get; private set; }
private TocEntry () { }
internal static TocEntry Parse (BinaryReader reader)
{
return new TocEntry
{
SectionIdentifier = new string (reader.ReadChars (16)),
Flags = reader.ReadUInt16 (),
SectionFlags = reader.ReadUInt16 (),
SectionQualifier = reader.ReadUInt32 (),
SectionOffset = reader.ReadUInt32 (),
SectionLength = reader.ReadUInt32 ()
};
}
public override string ToString ()
{
return SectionIdentifier.TrimEnd ('\0', ' ') + "\t length: " + SectionLength;
}
}
}

View File

@@ -0,0 +1,28 @@
using System.IO;
namespace PriFormat
{
public class UnknownSection: Section
{
public byte [] SectionContent { get; private set; }
internal UnknownSection (string sectionIdentifier, PriFile priFile)
: base (sectionIdentifier, priFile)
{
}
protected override bool ParseSectionContent (BinaryReader binaryReader)
{
int contentLength = (int)(binaryReader.BaseStream.Length - binaryReader.BaseStream.Position);
SectionContent = binaryReader.ReadBytes (contentLength);
return true;
}
public override void Dispose ()
{
SectionContent = null;
base.Dispose ();
}
}
}

View File

@@ -40,6 +40,7 @@
this.webui.Size = new System.Drawing.Size(661, 416);
this.webui.TabIndex = 0;
this.webui.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webui_DocumentCompleted);
this.webui.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.webui_PreviewKeyDown);
//
// WebAppForm
//

View File

@@ -97,5 +97,9 @@ namespace WAShell
{
webui.ObjectForScripting = null;
}
private void webui_PreviewKeyDown (object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.F5) e.IsInputKey = true;
}
}
}

View File

@@ -87,7 +87,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
@@ -118,7 +118,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>

View File

@@ -162,6 +162,160 @@ void read_package (const std::wstring &filepath)
}
}
}
void read_manifest (const std::wstring &filepath)
{
manifest_reader pr (filepath);
pr.enable_pri_convert (true);
pr.use_pri (true);
std::wcout << L"Is Valid: " << (pr.valid () ? L"true" : L"false") << std::endl;
std::wcout << L"Package Type: ";
switch (pr.package_type ())
{
case PKGTYPE_APPX: std::wcout << L"Appx"; break;
case PKGTYPE_BUNDLE: std::wcout << L"AppxBundle"; break;
}
std::wcout << std::endl;
std::wcout << L"Package Role: ";
switch (pr.package_role ())
{
case PKGROLE_APPLICATION: std::wcout << L"Application"; break;
case PKGROLE_FRAMEWORK: std::wcout << L"Framework"; break;
case PKGROLE_RESOURCE: std::wcout << L"Resource"; break;
case PKGROLE_UNKNOWN: std::wcout << L"Unknown"; break;
}
std::wcout << std::endl;
auto id = pr.get_identity ();
std::wcout << L"Identity" << std::endl;
std::wcout << L"\tName: " << id.name () << std::endl;
std::wcout << L"\tPublisher: " << id.publisher () << std::endl;
auto ver = id.version ();
std::wcout << L"\tVersion: " << ver.major << L"." << ver.minor << L"." << ver.build << L"." << ver.revision << std::endl;
std::wcout << L"\tPackage Family Name: " << id.package_family_name () << std::endl;
std::wcout << L"\tPackage Full Name: " << id.package_full_name () << std::endl;
std::wcout << L"\tResource Id: " << id.resource_id () << std::endl;
std::wcout << L"\tArchitecture: ";
DWORD archi = id.architecture ();
for (size_t i = 0xF; i > 0; i /= 2)
{
if (archi & i)
{
switch (i)
{
case PKG_ARCHITECTURE_ARM: std::wcout << "Arm"; break;
case PKG_ARCHITECTURE_ARM64: std::wcout << "Arm64"; break;
case PKG_ARCHITECTURE_NEUTRAL: std::wcout << "Neutral"; break;
case PKG_ARCHITECTURE_X64: std::wcout << "x64"; break;
case PKG_ARCHITECTURE_X86: std::wcout << "x86"; break;
default: continue;
}
std::wcout << L", ";
}
}
std::wcout << std::endl;
ver = id.version (true);
std::wcout << L"\tVersion (Appx): " << ver.major << L"." << ver.minor << L"." << ver.build << L"." << ver.revision << std::endl;
std::wcout << L"Properties" << std::endl;
auto prop = pr.get_properties ();
std::wcout << L"\tDisplay Name: " << prop.display_name () << std::endl;
std::wcout << L"\tDescription: " << prop.description () << std::endl;
std::wcout << L"\tPublisher Display Name: " << prop.publisher_display_name () << std::endl;
std::wcout << L"\tLogo: " << prop.logo () << std::endl;
std::wcout << L"\tLogo Base64: " << prop.logo_base64 () << std::endl;
std::wcout << L"\tFramework: " << (prop.framework () ? L"true" : L"false") << std::endl;
std::wcout << L"\tResource Package: " << (prop.resource_package () ? L"true" : L"false") << std::endl;
auto preq = pr.get_prerequisites ();
std::wcout << L"Prerequisites" << std::endl;
ver = preq.os_min_version ();
std::wcout << L"\tOS Min Version: " << ver.major << L"." << ver.minor << L"." << ver.build << L"." << ver.revision << std::endl;
ver = preq.os_max_version_tested ();
std::wcout << L"\tOS Max Version Tested: " << ver.major << L"." << ver.minor << L"." << ver.build << L"." << ver.revision << std::endl;
std::wcout << L"Resources" << std::endl;
auto res = pr.get_resources ();
{
std::vector <std::wstring> langs;
std::vector <UINT32> scales;
std::vector <DWORD> dx_levels;
res.languages (langs);
res.scales (scales);
res.dx_feature_level (dx_levels);
std::wcout << L"\tSupported Languages: ";
for (auto &it : langs)
{
std::wcout << it << L", ";
}
std::wcout << std::endl;
std::wcout << L"\tSupported Scales: ";
for (auto &it : scales)
{
std::wcout << it << L", ";
}
std::wcout << std::endl;
std::wcout << L"\tSupported Languages: ";
for (auto &it : dx_levels)
{
switch (it)
{
case PKG_RESOURCES_DXFEATURE_LEVEL9: std::wcout << L"9, "; break;
case PKG_RESOURCES_DXFEATURE_LEVEL10: std::wcout << L"10, "; break;
case PKG_RESOURCES_DXFEATURE_LEVEL11: std::wcout << L"11, "; break;
case PKG_RESOURCES_DXFEATURE_LEVEL12: std::wcout << L"12, "; break;
}
}
std::wcout << std::endl;
}
std::wcout << L"Capabilities" << std::endl;
auto cap = pr.get_capabilities ();
{
std::vector <std::wstring> caps;
std::vector <std::wstring> devicecaps;
cap.capabilities_name (caps);
cap.device_capabilities (devicecaps);
std::wcout << L"\tCapabilities: ";
for (auto &it : caps)
{
std::wcout << it << L", ";
}
std::wcout << std::endl;
std::wcout << L"\tDevice Capabilities: ";
for (auto &it : devicecaps)
{
std::wcout << it << L", ";
}
std::wcout << std::endl;
}
std::wcout << L"Applications" << std::endl;
auto apps = pr.get_applications ();
{
std::vector <manifest_reader::application> appmaps;
apps.get (appmaps);
size_t cnt = 1;
for (auto &it : appmaps)
{
std::wcout << L"\tApplication" << cnt ++ << std::endl;
for (auto &it_s : it)
{
std::wcout << L"\t\t" << it_s.first << L": " << it.newat (it_s.first) << std::endl;
std::wstring base64 = it.newat_base64 (it_s.first);
if (!base64.empty ()) std::wcout << L"\t\t" << it_s.first << L" (Base64): " << base64 << std::endl;
}
}
}
auto deps = pr.get_dependencies ();
std::wcout << L"Dependencies" << std::endl;
{
std::vector <manifest_reader::dependency> deparr;
deps.get (deparr);
size_t cnt = 1;
for (auto &it : deparr)
{
std::wcout << L"\tDependency" << cnt ++ << std::endl;
std::wcout << L"\t\tName: " << it.name << std::endl;
std::wcout << L"\t\tPublisher: " << it.publisher << std::endl;
ver = it.vermin;
std::wcout << L"\t\tMin Version: " << ver.major << L"." << ver.minor << L"." << ver.build << L"." << ver.revision << std::endl;
}
}
}
using cbfunc = std::function <void (int progress)>;
void ProgressCallback (DWORD dwProgress, void *pCustom)
{
@@ -197,13 +351,15 @@ int main (int argc, char *argv [])
std::wstring pkgPathStr = L"E:\\Profiles\\Bruce\\Desktop\\Discourse.appx";
//pkgPathStr = L"F:\\BaiduNetdiskDownload\\Collection4\\Microsoft.BingFinance_2015.709.2014.2069_neutral_~_8wekyb3d8bbwe\\FinanceApp_3.0.4.336_x86.appx";
//pkgPathStr = L"F:\\BaiduNetdiskDownload\\Collection4\\Microsoft.BingFinance_2015.709.2014.2069_neutral_~_8wekyb3d8bbwe.appxbundle";
pkgPathStr = L"E:\\Profiles\\Bruce\\Desktop\\н¨Îļþ¼Ð\\Microsoft.MSIXPackagingTool_2023.1212.538.0_neutral_~_8wekyb3d8bbwe.Msixbundle";
//pkgPathStr = L"E:\\Profiles\\Bruce\\Desktop\\н¨Îļþ¼Ð\\Microsoft.MSIXPackagingTool_2023.1212.538.0_neutral_~_8wekyb3d8bbwe.Msixbundle";
pkgPathStr = L"C:\\Program Files\\WindowsApps\\Microsoft.3DBuilder_20.0.4.0_x64__8wekyb3d8bbwe\\AppxManifest.xml";
if (pkgPathStr.empty ()) std::getline (std::wcin, pkgPathStr);
pkgPathStr.erase (
std::remove (pkgPathStr.begin (), pkgPathStr.end (), L'\"'),
pkgPathStr.end ()
);
read_package (pkgPathStr);
//read_package (pkgPathStr);
read_manifest (pkgPathStr);
system ("pause");
return 0;
}

View File

@@ -1754,3 +1754,44 @@ BOOL GetManifestPrerequisite (_In_ HPKGMANIFESTREAD hReader, _In_ LPCWSTR lpName
}
return FALSE;
}
LPWSTR GetManifestPrerequistieSystemVersionName (_In_ HPKGMANIFESTREAD hReader, _In_ LPCWSTR lpName)
{
auto ptr = ToPtrManifest (hReader);
if (!ptr) return nullptr;
switch (ptr->type ())
{
case PackageType::single: {
auto read = ptr->appx_reader ();
auto pre = read.prerequisites ();
auto ver = pre.get_version (lpName ? lpName : L"");
auto str = GetPrerequistOSVersionDescription (ver);
return _wcsdup (str.c_str ());
} break;
default:
break;
}
return nullptr;
}
BOOL PackageReaderGetFileRoot (LPWSTR lpFilePath) { return PathRemoveFileSpecW (lpFilePath); }
LPWSTR PackageReaderCombinePath (LPCWSTR lpLeft, LPCWSTR lpRight, LPWSTR lpBuf) { return PathCombineW (lpBuf, lpLeft, lpRight); }
HANDLE PackageReaderGetFileStream (LPCWSTR lpFilePath)
{
IStream *ptr = nullptr;
HRESULT hr = SHCreateStreamOnFileEx (lpFilePath, STGM_READ | STGM_SHARE_DENY_NONE, 0, FALSE, NULL, &ptr);
if (SUCCEEDED (hr)) return ptr;
else
{
if (ptr) ptr->Release ();
ptr = nullptr;
}
return nullptr;
}
void PackageReaderDestroyFileStream (HANDLE hStream)
{
auto ptr = (IStream *)hStream;
if (ptr)
{
ptr->Release ();
return;
}
}

View File

@@ -435,6 +435,16 @@ extern "C"
// 返回 TRUE 表示存在该前置条件。
PKGREAD_API BOOL GetManifestPrerequisite (_In_ HPKGMANIFESTREAD hReader, _In_ LPCWSTR lpName, _Outptr_ VERSION *pVerRet);
PKGREAD_API LPWSTR GetManifestPrerequistieSystemVersionName (_In_ HPKGMANIFESTREAD hReader, _In_ LPCWSTR lpName);
PKGREAD_API BOOL PackageReaderGetFileRoot (_In_ LPWSTR lpFilePath);
PKGREAD_API LPWSTR PackageReaderCombinePath (_In_ LPCWSTR lpLeft, _In_ LPCWSTR lpRight, _Outptr_ LPWSTR lpBuf);
PKGREAD_API HANDLE PackageReaderGetFileStream (LPCWSTR lpFilePath);
PKGREAD_API void PackageReaderDestroyFileStream (HANDLE hStream);
#ifdef _DEFAULT_INIT_VALUE_
#undef _DEFAULT_INIT_VALUE_
#endif
@@ -589,6 +599,8 @@ class package_reader
std::sort (prifilestreams.begin (), prifilestreams.end ());
auto last = std::unique (prifilestreams.begin (), prifilestreams.end ());
prifilestreams.erase (last, prifilestreams.end ());
bool lastvalue = resswitch;
resswitch = false;
try
{
std::vector <std::wstring> resnames;
@@ -619,6 +631,7 @@ class package_reader
pribundlereader.add_search (resnames);
}
catch (const std::exception &e) {}
resswitch = lastvalue;
#endif
}
typedef struct deconstr
@@ -1301,6 +1314,702 @@ class package_reader
#endif
}
};
class manifest_reader
{
private:
HPKGMANIFESTREAD hReader = nullptr;
std::wstring filepath = L"";
bool usepri = false;
bool resswitch = false;
#ifdef _PRI_READER_CLI_HEADER_
prifile prireader;
#endif
std::wstring get_fileroot ()
{
std::vector <wchar_t> root (filepath.capacity ());
wcscpy (root.data (), filepath.c_str ());
PackageReaderGetFileRoot (root.data ());
return root.data ();
}
static std::wstring path_combine (const std::wstring &l, const std::wstring &r)
{
std::vector <wchar_t> buffer (l.length () + r.length () + 10);
PackageReaderCombinePath (l.c_str (), r.c_str (), buffer.data ());
return buffer.data ();
}
void initpri ()
{
#ifdef _PRI_READER_CLI_HEADER_
prireader.destroy ();
switch (this->package_type ())
{
case PKGTYPE_APPX: {
prireader.create (path_combine (get_fileroot (), L"resources.pri"));
} break;
}
bool lastvalue = resswitch;
resswitch = false;
try
{
std::vector <std::wstring> resnames;
{
auto prop = get_properties ();
std::wstring temp = prop.description ();
if (IsMsResourcePrefix (temp.c_str ())) resnames.push_back (temp);
temp = prop.display_name ();
if (IsMsResourcePrefix (temp.c_str ())) resnames.push_back (temp);
temp = prop.publisher_display_name ();
if (IsMsResourcePrefix (temp.c_str ())) resnames.push_back (temp);
resnames.push_back (prop.logo ());
}
{
auto app = get_applications ();
std::vector <application> apps;
app.get (apps);
for (auto &it_map : apps)
{
for (auto &it_item : it_map)
{
if (std::find (g_filepathitems.begin (), g_filepathitems.end (), it_item.first) != g_filepathitems.end () && !it_item.second.empty ())
resnames.push_back (it_item.second);
else if (IsMsResourcePrefix (it_item.second.c_str ())) resnames.push_back (it_item.second);
}
}
}
prireader.add_search (resnames);
}
catch (const std::exception &e) {}
resswitch = lastvalue;
#endif
}
typedef struct deconstr
{
std::function <void ()> endtask = nullptr;
deconstr (std::function <void ()> pf): endtask (pf) {}
~deconstr () { if (endtask) endtask (); }
} destruct;
public:
class base_subitems
{
protected:
HPKGMANIFESTREAD &hReader;
public:
base_subitems (HPKGMANIFESTREAD &hReader): hReader (hReader) {}
};
class identity: public base_subitems
{
using base = base_subitems;
public:
using base::base;
std::wstring string_value (DWORD dwName) const
{
LPWSTR lpstr = nullptr;
deconstr rel ([&lpstr] () {
if (lpstr) free (lpstr);
lpstr = nullptr;
});
lpstr = GetManifestIdentityStringValue (hReader, dwName);
return lpstr ? lpstr : L"";
}
std::wstring name () const { return string_value (PKG_IDENTITY_NAME); }
std::wstring publisher () const { return string_value (PKG_IDENTITY_PUBLISHER); }
std::wstring package_family_name () const { return string_value (PKG_IDENTITY_PACKAGEFAMILYNAME); }
std::wstring package_full_name () const { return string_value (PKG_IDENTITY_PACKAGEFULLNAME); }
std::wstring resource_id () const { return string_value (PKG_IDENTITY_RESOURCEID); }
VERSION version (bool read_subpkg_ver = false) const { VERSION ver; GetManifestIdentityVersion (hReader, &ver); return ver; }
DWORD architecture () const { DWORD dw = 0; GetManifestIdentityArchitecture (hReader, &dw); return dw; }
};
class properties: public base_subitems
{
using base = base_subitems;
std::wstring root = L"";
public:
using base::base;
std::wstring string_value (const std::wstring &swName) const
{
LPWSTR lpstr = nullptr;
deconstr rel ([&lpstr] () {
if (lpstr) free (lpstr);
lpstr = nullptr;
});
lpstr = GetManifestPropertiesStringValue (hReader, swName.c_str ());
return lpstr ? lpstr : L"";
}
bool bool_value (const std::wstring &swName, bool bRetWhenFailed = false) const
{
BOOL ret = FALSE;
HRESULT hr = GetManifestPropertiesBoolValue (hReader, swName.c_str (), &ret);
if (FAILED (hr)) return bRetWhenFailed;
else return ret != FALSE;
}
std::wstring display_name (bool toprires = true)
{
std::wstring ret = string_value (PKG_PROPERTIES_DISPLAYNAME);
if (!toprires) return ret;
if (!enable_pri ()) return ret;
#ifdef _PRI_READER_CLI_HEADER_
else
{
if (!IsMsResourcePrefix (ret.c_str ())) return ret;
std::wstring privalue = pri_get_res (ret);
if (privalue.empty ()) return ret;
return privalue;
}
#endif
return ret;
}
std::wstring publisher_display_name (bool toprires = true)
{
std::wstring ret = string_value (PKG_PROPERTIES_PUBLISHER);
if (!toprires) return ret;
if (!enable_pri ()) return ret;
#ifdef _PRI_READER_CLI_HEADER_
else
{
if (!IsMsResourcePrefix (ret.c_str ())) return ret;
std::wstring privalue = pri_get_res (ret);
if (privalue.empty ()) return ret;
return privalue;
}
#endif
return ret;
}
std::wstring description (bool toprires = true)
{
std::wstring ret = string_value (PKG_PROPERTIES_DESCRIPTION);
if (!toprires) return ret;
if (!enable_pri ()) return ret;
#ifdef _PRI_READER_CLI_HEADER_
else
{
if (!IsMsResourcePrefix (ret.c_str ())) return ret;
std::wstring privalue = pri_get_res (ret);
if (privalue.empty ()) return ret;
return privalue;
}
#endif
return ret;
}
std::wstring logo (bool toprires = true)
{
std::wstring ret = string_value (PKG_PROPERTIES_LOGO);
if (!toprires) return ret;
if (!enable_pri ()) return ret;
#ifdef _PRI_READER_CLI_HEADER_
else
{
std::wstring privalue = pri_get_res (ret);
if (privalue.empty ()) return ret;
return privalue;
}
#endif
return ret;
}
std::wstring logo_base64 ()
{
switch (GetManifestType (hReader))
{
case PKGTYPE_APPX: {
auto path = path_combine (root, logo ());
HANDLE pic = PackageReaderGetFileStream (path.c_str ());
destruct relp ([&pic] () {
if (pic) PackageReaderDestroyFileStream (pic);
pic = nullptr;
});
LPWSTR lpstr = nullptr;
destruct rel ([&lpstr] () {
if (lpstr) free (lpstr);
lpstr = nullptr;
});
lpstr = StreamToBase64W (pic, nullptr, 0, nullptr);
return lpstr ? lpstr : L"";
} break;
}
return L"";
}
bool framework () const { return bool_value (PKG_PROPERTIES_FRAMEWORD); }
bool resource_package () const { return bool_value (PKG_PROPERTIES_IS_RESOURCE); }
#ifdef _PRI_READER_CLI_HEADER_
prifile *pbreader = nullptr;
bool *usepri = nullptr;
bool *resconvert = nullptr;
#endif
bool enable_pri () const
{
#ifdef _PRI_READER_CLI_HEADER_
if (!pbreader) return false;
if (!usepri || !*usepri) return false;
if (!resconvert) return false;
return *resconvert;
#else
return false;
#endif
}
std::wstring pri_get_res (const std::wstring &resname)
{
#ifdef _PRI_READER_CLI_HEADER_
if (resname.empty ()) return L"";
if (!pbreader) return L"";
return pbreader->resource (resname);
#else
return L"";
#endif
}
#ifdef _PRI_READER_CLI_HEADER_
properties (HPKGMANIFESTREAD &hReader, prifile *pri, bool *up, bool *resc, const std::wstring &pathroot):
base (hReader), pbreader (pri), usepri (up), resconvert (resc), root (pathroot) {}
#endif
};
class application: public std::map <std::wstring, std::wstring>
{
using base = std::map <std::wstring, std::wstring>;
std::wstring root = L"";
public:
using base::base;
application () = default;
std::wstring user_model_id () { return this->at (L"AppUserModelID"); }
friend bool operator == (application &a1, application &a2) { return !_wcsicmp (a1.user_model_id ().c_str (), a2.user_model_id ().c_str ()); }
friend bool operator != (application &a1, application &a2) { return _wcsicmp (a1.user_model_id ().c_str (), a2.user_model_id ().c_str ()); }
explicit operator bool () { return this->user_model_id ().empty (); }
std::wstring &operator [] (const std::wstring &key)
{
auto it = this->find (key);
if (it == this->end ())
{
it = this->insert (std::make_pair (key, L"")).first;
}
if (!enable_pri ()) return it->second;
#ifdef _PRI_READER_CLI_HEADER_
if (IsMsResourcePrefix (it->second.c_str ()))
{
std::wstring privalue = pri_get_res (it->second);
if (!privalue.empty ()) return privalue;
return it->second;
}
else if (std::find (g_filepathitems.begin (), g_filepathitems.end (), it->first) != g_filepathitems.end () && !it->second.empty ())
{
std::wstring privalue = pri_get_res (it->second);
if (!privalue.empty ()) return privalue;
return it->second;
}
#endif
return it->second;
}
typename base::iterator find_case_insensitive (const std::wstring &key)
{
for (auto it = this->begin (); it != this->end (); ++it)
{
if (_wcsicmp (it->first.c_str (), key.c_str ()) == 0)
return it;
}
return this->end ();
}
typename base::const_iterator find_case_insensitive (const std::wstring &key) const
{
for (auto it = this->begin (); it != this->end (); ++ it)
{
if (_wcsicmp (it->first.c_str (), key.c_str ()) == 0)
return it;
}
return this->end ();
}
std::wstring at (const std::wstring &key)
{
auto it = this->find_case_insensitive (key);
if (it == this->end ()) throw std::out_of_range ("application::at: key not found");
if (!enable_pri ()) return it->second;
#ifdef _PRI_READER_CLI_HEADER_
if (IsMsResourcePrefix (it->second.c_str ()))
{
std::wstring privalue = pri_get_res (it->second);
if (!privalue.empty ()) return privalue;
}
#endif
return it->second;
}
std::wstring newat (const std::wstring &key, bool to_pri_string = true)
{
auto it = this->find (key);
if (it == this->end ())
{
it = this->insert (std::make_pair (key, L"")).first;
}
if (!enable_pri () && to_pri_string) return it->second;
#ifdef _PRI_READER_CLI_HEADER_
if (IsMsResourcePrefix (it->second.c_str ()))
{
std::wstring privalue = pri_get_res (it->second);
if (!privalue.empty ()) return privalue;
return it->second;
}
else if (std::find (g_filepathitems.begin (), g_filepathitems.end (), it->first) != g_filepathitems.end () && !it->second.empty ())
{
std::wstring privalue = pri_get_res (it->second);
if (!privalue.empty ()) return privalue;
return it->second;
}
#endif
return it->second;
}
// 仅支持文件
std::wstring newat_base64 (const std::wstring &key)
{
#ifdef _PRI_READER_CLI_HEADER_
std::wstring value = newat (key);
if (std::find (g_filepathitems.begin (), g_filepathitems.end (), key) != g_filepathitems.end () && !value.empty ())
{
switch (GetManifestType (hReader))
{
case PKGTYPE_APPX: {
auto filepath = path_combine (root, value);
HANDLE pic = PackageReaderGetFileStream (filepath.c_str ());
destruct relp ([&pic] () {
if (pic) PackageReaderDestroyFileStream (pic);
pic = nullptr;
});
LPWSTR lpstr = nullptr;
destruct rel ([&lpstr] () {
if (lpstr) free (lpstr);
lpstr = nullptr;
});
lpstr = StreamToBase64W (pic, nullptr, 0, nullptr);
return lpstr ? lpstr : L"";
} break;
}
return L"";
}
else return L"";
#else
return L"";
#endif
}
#ifdef _PRI_READER_CLI_HEADER_
HPKGMANIFESTREAD hReader = nullptr;
prifile *pbreader = nullptr;
bool *usepri = nullptr;
bool *resconvert = nullptr;
#endif
bool enable_pri () const
{
#ifdef _PRI_READER_CLI_HEADER_
if (!pbreader) return false;
if (!usepri || !*usepri) return false;
if (!resconvert) return false;
return *resconvert;
#else
return false;
#endif
}
std::wstring pri_get_res (const std::wstring &resname)
{
#ifdef _PRI_READER_CLI_HEADER_
if (resname.empty ()) return L"";
if (!pbreader) return L"";
return pbreader->resource (resname);
#else
return L"";
#endif
}
#ifdef _PRI_READER_CLI_HEADER_
application (HPKGMANIFESTREAD hReader, prifile *pri, bool *up, bool *resc, const std::wstring &pathroot):
hReader (hReader), pbreader (pri), usepri (up), resconvert (resc), root (pathroot) {}
#endif
};
class applications
{
private:
HPKGMANIFESTREAD &hReader;
HAPPENUMERATOR hList = nullptr;
std::wstring root = L"";
public:
applications (HPKGMANIFESTREAD &hReader): hReader (hReader)
{
hList = GetManifestApplications (hReader);
}
~applications ()
{
if (hList) DestroyPackageApplications (hList);
hList = nullptr;
}
size_t get (std::vector <application> &apps)
{
apps.clear ();
if (!hList) return 0;
HLIST_PVOID hMapList = ApplicationsToMap (hList);
deconstr endt ([&hMapList] {
if (hMapList) DestroyApplicationsMap (hMapList);
hMapList = nullptr;
});
if (!hMapList) return 0;
for (size_t i = 0; i < hMapList->dwSize; i ++)
{
HLIST_PVOID &hKeyValues = ((HLIST_PVOID *)hMapList->alpVoid) [i];
#ifdef _PRI_READER_CLI_HEADER_
application app (hReader, pbreader, usepri, resconvert, root);
#else
application app;
#endif
for (size_t j = 0; j < hKeyValues->dwSize; j ++)
{
HPAIR_PVOID &hPair = ((HPAIR_PVOID *)hKeyValues->alpVoid) [j];
LPWSTR lpKey = (LPWSTR)hPair->lpKey;
LPWSTR lpValue = (LPWSTR)hPair->lpValue;
if (!lpKey || !*lpKey) continue;
app [lpKey] = lpValue ? lpValue : L"";
}
apps.push_back (app);
}
return apps.size ();
}
#ifdef _PRI_READER_CLI_HEADER_
prifile *pbreader = nullptr;
bool *usepri = nullptr;
bool *resconvert = nullptr;
applications (HPKGMANIFESTREAD &hReader, prifile *pri, bool *up, bool *resc, const std::wstring &pathroot):
hReader (hReader), pbreader (pri), usepri (up), resconvert (resc), root (pathroot) { hList = GetManifestApplications (hReader); }
#endif
};
class capabilities: public base_subitems
{
using base = base_subitems;
public:
using base::base;
size_t capabilities_name (std::vector <std::wstring> &output) const
{
output.clear ();
HLIST_PVOID hList = GetManifestCapabilitiesList (hReader);
deconstr endt ([&hList] () {
if (hList) DestroyCapabilitiesList (hList);
hList = nullptr;
});
if (!hList) return 0;
for (size_t i = 0; i < hList->dwSize; i ++)
{
LPWSTR lpstr = (LPWSTR)hList->alpVoid [i];
if (!lpstr) continue;
output.push_back (lpstr);
}
return output.size ();
}
size_t device_capabilities (std::vector <std::wstring> &output) const
{
output.clear ();
HLIST_PVOID hList = GetManifestDeviceCapabilitiesList (hReader);
deconstr endt ([&hList] () {
if (hList) DestroyDeviceCapabilitiesList (hList);
hList = nullptr;
});
if (!hList) return 0;
for (size_t i = 0; i < hList->dwSize; i ++)
{
LPWSTR lpstr = (LPWSTR)hList->alpVoid [i];
if (!lpstr) continue;
output.push_back (lpstr);
}
return output.size ();
}
};
struct dependency
{
std::wstring name, publisher;
VERSION vermin;
dependency (const std::wstring &name = L"", const std::wstring &pub = L"", const VERSION &ver = VERSION ()):
name (name), publisher (pub), vermin (ver) {}
};
class dependencies: public base_subitems
{
using base = base_subitems;
public:
using base::base;
size_t get (std::vector <dependency> &output) const
{
auto hList = GetManifestDependencesInfoList (hReader);
deconstr rel ([&hList] () {
DestroyDependencesInfoList (hList);
});
if (!hList) return 0;
for (size_t i = 0; i < hList->dwSize; i ++)
{
DEPENDENCY_INFO &depinf = ((DEPENDENCY_INFO *)hList->aDepInfo) [i];
if (!depinf.lpName || !*depinf.lpName) continue;
output.push_back (dependency (depinf.lpName, depinf.lpPublisher ? depinf.lpPublisher : L"", depinf.verMin));
}
return output.size ();
}
};
class resources: public base_subitems
{
using base = base_subitems;
public:
using base::base;
size_t languages (std::vector <std::wstring> &langs) const
{
langs.clear ();
auto hList = GetManifestResourcesLanguages (hReader);
deconstr rel ([&hList] () {
if (hList) DestroyResourcesLanguagesList (hList);
hList = nullptr;
});
if (!hList) return 0;
for (size_t i = 0; i < hList->dwSize; i ++)
{
LPWSTR lpstr = ((LPWSTR *)hList->alpVoid) [i];
if (lpstr && *lpstr) langs.push_back (std::wstring (lpstr));
}
return langs.size ();
}
size_t languages (std::vector <LCID> &langs) const
{
langs.clear ();
auto hList = GetManifestResourcesLanguagesToLcid (hReader);
deconstr rel ([&hList] () {
if (hList) DestroyResourcesLanguagesLcidList (hList);
hList = nullptr;
});
if (!hList) return 0;
for (size_t i = 0; i < hList->dwSize; i ++)
{
if (hList->aLcid [i]) langs.push_back (hList->aLcid [i]);
}
return langs.size ();
}
size_t scales (std::vector <UINT32> &output) const
{
output.clear ();
auto hList = GetManifestResourcesScales (hReader);
deconstr rel ([&hList] () {
if (hList) DestroyResourcesScalesList (hList);
});
if (!hList) return 0;
for (size_t i = 0; i < hList->dwSize; i ++)
{
UINT32 s = hList->aUI32 [i];
if (s) output.push_back (s);
}
return output.size ();
}
DWORD dx_feature_level () const { return GetManifestResourcesDxFeatureLevels (hReader); }
// 向数组添加的是以 PKG_RESOURCES_DXFEATURE_* 前缀的常量
size_t dx_feature_level (std::vector <DWORD> &ret)
{
DWORD dx = dx_feature_level ();
if (dx & PKG_RESOURCES_DXFEATURE_LEVEL9) ret.push_back (PKG_RESOURCES_DXFEATURE_LEVEL9);
else if (dx & PKG_RESOURCES_DXFEATURE_LEVEL10) ret.push_back (PKG_RESOURCES_DXFEATURE_LEVEL10);
else if (dx & PKG_RESOURCES_DXFEATURE_LEVEL11) ret.push_back (PKG_RESOURCES_DXFEATURE_LEVEL11);
else if (dx & PKG_RESOURCES_DXFEATURE_LEVEL12) ret.push_back (PKG_RESOURCES_DXFEATURE_LEVEL12);
return ret.size ();
}
};
class prerequisites: public base_subitems
{
using base = base_subitems;
public:
using base::base;
VERSION get_version (const std::wstring &name) const
{
VERSION ver;
GetManifestPrerequisite (hReader, name.c_str (), &ver);
return ver;
}
VERSION os_min_version () const { return get_version (PKG_PREREQUISITE_OS_MIN_VERSION); }
VERSION os_max_version_tested () const { return get_version (PKG_PREREQUISITE_OS_MAX_VERSION_TESTED); }
std::wstring get_description (const std::wstring &name) const
{
LPWSTR lpstr = GetManifestPrerequistieSystemVersionName (hReader, name.c_str ());
deconstr relt ([&lpstr] () {
if (lpstr) free (lpstr);
lpstr = nullptr;
});
return lpstr ? lpstr : L"";
}
std::wstring os_min_version_description () const { return get_description (PKG_PREREQUISITE_OS_MIN_VERSION); }
std::wstring os_max_version_tested_description () const { return get_description (PKG_PREREQUISITE_OS_MAX_VERSION_TESTED); }
};
manifest_reader (): hReader (CreateManifestReader ()) {}
manifest_reader (const std::wstring &fpath): hReader (CreateManifestReader ())
{
file (fpath);
}
~manifest_reader ()
{
DestroyManifestReader (hReader);
hReader = nullptr;
#ifdef _PRI_READER_CLI_HEADER_
prireader.destroy ();
#endif
}
std::wstring file () const { return filepath; }
bool file (const std::wstring &path)
{
return LoadManifestFromFile (hReader, (filepath = path).c_str ());
}
// PKGTYPE_* 前缀常量
WORD package_type () const { return GetManifestType (hReader); }
bool valid () const { return hReader && IsManifestValid (hReader); }
// PKGROLE_* 前缀常量
WORD package_role () const { return GetManifestRole (hReader); }
identity get_identity () { return identity (hReader); }
properties get_properties ()
{
return properties (hReader
#ifdef _PRI_READER_CLI_HEADER_
,
&prireader,
&usepri,
&resswitch,
get_fileroot ()
#endif
);
}
applications get_applications ()
{
return applications (hReader
#ifdef _PRI_READER_CLI_HEADER_
,
&prireader,
&usepri,
&resswitch,
get_fileroot ()
#endif
);
}
capabilities get_capabilities () { return capabilities (hReader); }
dependencies get_dependencies () { return dependencies (hReader); }
resources get_resources () { return resources (hReader); }
prerequisites get_prerequisites () { return prerequisites (hReader); }
// 是否允许使用 PRI
bool use_pri () const
{
#ifdef _PRI_READER_CLI_HEADER_
return usepri;
#else
return false;
#endif
}
// 是否允许使用 PRI
bool use_pri (bool value)
{
#ifdef _PRI_READER_CLI_HEADER_
bool laststatus = usepri;
usepri = value;
if (laststatus ^ usepri) initpri ();
return usepri;
#else
return usepri = false;
#endif
}
// 是否自动从 PRI 中提取资源
bool enable_pri_convert () const { return resswitch; }
// 是否自动从 PRI 中提取资源
bool enable_pri_convert (bool value)
{
#ifdef _PRI_READER_CLI_HEADER_
return resswitch = value;
#else
return resswitch = false;
#endif
}
};
#endif
#endif

View File

@@ -48,16 +48,11 @@ ref class PriFileInst
PriFile ^inst = nullptr;
OpenType opentype = OpenType::Unknown;
IStream *isptr = nullptr;
System::IO::FileStream ^fsptr = nullptr;
System::IO::Stream ^fsptr = nullptr;
operator PriFile ^ () { return inst; }
operator IStream * () { return isptr; }
operator System::IO::FileStream ^ () { return fsptr; }
operator System::IO::Stream ^ () { return fsptr; }
explicit operator bool () { return inst && (int)opentype && ((bool)isptr ^ (fsptr != nullptr)); }
operator System::IO::Stream ^ ()
{
if (isptr) return gcnew ComStreamWrapper (ComIStreamToCliIStream (isptr));
else return fsptr;
}
size_t Seek (int64_t offset, System::IO::SeekOrigin origin)
{
if (isptr)
@@ -317,11 +312,13 @@ PCSPRIFILE CreatePriFileInstanceFromStream (PCOISTREAM pStream)
{
HRESULT hr = S_OK;
if (pStream) hr = ((IStream *)pStream)->Seek (LARGE_INTEGER {}, STREAM_SEEK_SET, nullptr);
auto pri = PriFile::Parse (ComIStreamToCliIStream (reinterpret_cast <IStream *> (pStream)));
System::IO::Stream ^stream = nullptr;
auto pri = PriFile::Parse (ComIStreamToCliIStream (reinterpret_cast <IStream *> (pStream)), stream);
PriFileInst ^inst = gcnew PriFileInst ();
inst->inst = pri;
inst->opentype = OpenType::IStream;
inst->isptr = reinterpret_cast <IStream *> (pStream);
inst->fsptr = stream;
auto handle = System::Runtime::InteropServices::GCHandle::Alloc (inst);
IntPtr token = System::Runtime::InteropServices::GCHandle::ToIntPtr (handle);
return reinterpret_cast <PCSPRIFILE> (token.ToPointer ());

View File

@@ -0,0 +1,30 @@
========================================================================
动态链接库priread 项目概述
========================================================================
应用程序向导已为您创建了此 priread DLL。
本文件概要介绍组成 priread 应用程序的每个文件的内容。
priread.vcxproj
这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。
priread.vcxproj.filters
这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。
priread.cpp
这是主 DLL 源文件。
/////////////////////////////////////////////////////////////////////////////
其他标准文件:
StdAfx.h, StdAfx.cpp
这些文件用于生成名为 priread.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。
/////////////////////////////////////////////////////////////////////////////
其他注释:
应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。
/////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,19 @@
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

View File

@@ -0,0 +1,163 @@
#pragma once
#include <WinNls.h>
#include <string>
static std::wstring StringToWString (const std::string &str, UINT codePage = CP_ACP)
{
if (str.empty ()) return std::wstring ();
int len = MultiByteToWideChar (codePage, 0, str.c_str (), -1, nullptr, 0);
if (len == 0) return std::wstring ();
std::wstring wstr (len - 1, L'\0');
MultiByteToWideChar (codePage, 0, str.c_str (), -1, &wstr [0], len);
return wstr;
}
#undef GetLocaleInfo
std::string GetLocaleInfoA (LCID code, LCTYPE type)
{
char buf [LOCALE_NAME_MAX_LENGTH] = {0};
GetLocaleInfoA (code, type, buf, LOCALE_NAME_MAX_LENGTH);
return buf;
}
std::wstring GetLocaleInfoW (LCID code, LCTYPE type)
{
WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0};
GetLocaleInfoW (code, type, buf, LOCALE_NAME_MAX_LENGTH);
return buf;
}
void GetLocaleInfo (LCID code, LCTYPE type, std::wstring &output)
{
output = GetLocaleInfoW (code, type);
}
void GetLocaleInfo (LCID code, LCTYPE type, std::string &output)
{
output = GetLocaleInfoA (code, type);
}
int GetLocaleInfoEx (std::wstring lpLocaleName, LCTYPE type, std::wstring &output)
{
WCHAR buf [LOCALE_NAME_MAX_LENGTH] = {0};
int res = GetLocaleInfoEx (lpLocaleName.c_str (), type, buf, LOCALE_NAME_MAX_LENGTH);
if (&output) output = std::wstring (buf);
return res;
}
#undef SetLocaleInfo
BOOL SetLocaleInfoA (LCID code, LCTYPE type, const std::string &lcData)
{
return SetLocaleInfoA (code, type, lcData.c_str ());
}
BOOL SetLocaleInfoW (LCID code, LCTYPE type, const std::wstring &lcData)
{
return SetLocaleInfoW (code, type, lcData.c_str ());
}
BOOL SetLocaleInfo (LCID code, LCTYPE type, const std::wstring &lcData)
{
return SetLocaleInfoW (code, type, lcData);
}
BOOL SetLocaleInfo (LCID code, LCTYPE type, const std::string &lcData)
{
return SetLocaleInfoA (code, type, lcData);
}
std::string GetLocaleRestrictedCodeFromLcidA (LCID lcid)
{
return GetLocaleInfoA (lcid, 89);
}
std::wstring GetLocaleRestrictedCodeFromLcidW (LCID lcid)
{
return GetLocaleInfoW (lcid, 89);
}
void GetLocaleRestrictedCodeFromLcid (LCID lcid, std::string &ret)
{
ret = GetLocaleRestrictedCodeFromLcidA (lcid);
}
void GetLocaleRestrictedCodeFromLcid (LCID lcid, std::wstring &ret)
{
ret = GetLocaleRestrictedCodeFromLcidW (lcid);
}
std::string GetLocaleElaboratedCodeFromLcidA (LCID lcid)
{
return GetLocaleInfoA (lcid, 90);
}
std::wstring GetLocaleElaboratedCodeFromLcidW (LCID lcid)
{
return GetLocaleInfoW (lcid, 90);
}
void GetLocaleElaboratedCodeFromLcid (LCID lcid, std::wstring &ret)
{
ret = GetLocaleElaboratedCodeFromLcidW (lcid);
}
void GetLocaleElaboratedCodeFromLcid (LCID lcid, std::string &ret)
{
ret = GetLocaleElaboratedCodeFromLcidA (lcid);
}
LCID LocaleCodeToLcidW (LPCWSTR localeCode)
{
BYTE buf [LOCALE_NAME_MAX_LENGTH * sizeof (WCHAR)] = {0};
int res = GetLocaleInfoEx (localeCode, LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE, (LPWSTR)buf, LOCALE_NAME_MAX_LENGTH);
LCID lcid = *((LCID *)buf);
return lcid;
}
LCID LocaleCodeToLcidA (LPCSTR localeCode)
{
std::wstring lcWide = StringToWString (std::string (localeCode));
return LocaleCodeToLcidW (lcWide.c_str ());
}
LCID LocaleCodeToLcid (const std::wstring &loccode)
{
return LocaleCodeToLcidW (loccode.c_str ());
}
LCID LocaleCodeToLcid (const std::string &loccode)
{
return LocaleCodeToLcidA (loccode.c_str ());
}
std::string GetLocaleRestrictedCodeA (LPCSTR lc)
{
return GetLocaleInfoA (LocaleCodeToLcidA (lc), 89);
}
std::string GetLocaleRestrictedCodeA (const std::string &lc)
{
return GetLocaleInfoA (LocaleCodeToLcidA (lc.c_str ()), 89);
}
std::wstring GetLocaleRestrictedCodeW (LPCWSTR lc)
{
return GetLocaleInfoW (LocaleCodeToLcidW (lc), 89);
}
std::wstring GetLocaleRestrictedCodeW (const std::wstring &lc)
{
return GetLocaleInfoW (LocaleCodeToLcidW (lc.c_str ()), 89);
}
std::wstring GetLocaleRestrictedCode (const std::wstring &lc) { return GetLocaleRestrictedCodeW (lc); }
std::string GetLocaleRestrictedCode (const std::string &lc) { return GetLocaleRestrictedCodeA (lc); }
std::string GetLocaleElaboratedCodeA (LPCSTR lc)
{
return GetLocaleInfoA (LocaleCodeToLcidA (lc), 90);
}
std::string GetLocaleElaboratedCodeA (const std::string &lc)
{
return GetLocaleInfoA (LocaleCodeToLcidA (lc.c_str ()), 90);
}
std::wstring GetLocaleElaboratedCodeW (LPCWSTR lc)
{
return GetLocaleInfoW (LocaleCodeToLcidW (lc), 90);
}
std::wstring GetLocaleElaboratedCodeW (const std::wstring &lc)
{
return GetLocaleInfoW (LocaleCodeToLcidW (lc.c_str ()), 90);
}
std::wstring GetLocaleElaboratedCode (const std::wstring &lc) { return GetLocaleElaboratedCodeW (lc); }
std::string GetLocaleElaboratedCode (const std::string &lc) { return GetLocaleElaboratedCodeA (lc); }
std::string LcidToLocaleCodeA (LCID lcid, char divide = '-')
{
return GetLocaleRestrictedCodeFromLcidA (lcid) + divide + GetLocaleElaboratedCodeFromLcidA (lcid);
}
std::wstring LcidToLocaleCodeW (LCID lcid, WCHAR divide = L'-')
{
return GetLocaleRestrictedCodeFromLcidW (lcid) + divide + GetLocaleElaboratedCodeFromLcidW (lcid);
}
std::wstring LcidToLocaleCode (LCID lcid, WCHAR divide = L'-') { return LcidToLocaleCodeW (lcid, divide); }
std::string LcidToLocaleCode (LCID lcid, char divide = '-') { return LcidToLocaleCodeA (lcid, divide); }

View File

@@ -0,0 +1,456 @@
#pragma once
#include <string>
#include <locale>
#include <cctype>
namespace l0km
{
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> inline std::basic_string<E, TR, AL> toupper (const std::basic_string <E, TR, AL> &src)
{
std::basic_string <E, TR, AL> dst = src;
static const std::locale loc;
const std::ctype <E> &ctype = std::use_facet <std::ctype <E>> (loc);
for (typename std::basic_string <E, TR, AL>::size_type i = 0; i < src.size (); ++ i)
{
dst [i] = ctype.toupper (src [i]);
}
return dst;
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> inline std::basic_string <E, TR, AL> tolower (const std::basic_string <E, TR, AL> &src)
{
std::basic_string <E, TR, AL> dst = src;
static const std::locale loc;
const std::ctype <E> &ctype = std::use_facet <std::ctype <E>> (loc);
for (typename std::basic_string <E, TR, AL>::size_type i = 0; i < src.size (); ++ i)
{
dst [i] = ctype.tolower (src [i]);
}
return dst;
}
inline char toupper (char ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <char>> (loc).toupper (ch);
}
inline char tolower (char ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <char>> (loc).tolower (ch);
}
inline wchar_t toupper (wchar_t ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <wchar_t>> (loc).toupper (ch);
}
inline wchar_t tolower (wchar_t ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <wchar_t>> (loc).tolower (ch);
}
inline int toupper (int ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <int>> (loc).toupper (ch);
}
inline int tolower (int ch)
{
if (ch < -1) return ch;
static const std::locale loc;
return std::use_facet <std::ctype <int>> (loc).tolower (ch);
}
}
template <typename ct> bool is_blank (ct &ch)
{
return ch == ct (' ') || ch == ct ('\t') || ch == ct ('\n');
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> std::basic_string <E, TR, AL> NormalizeString (const std::basic_string <E, TR, AL> &str, bool upper = false, bool includemidblank = false)
{
typedef std::basic_string <E, TR, AL> string_type;
string_type result;
if (str.empty ()) return result;
auto begin_it = str.begin ();
auto end_it = str.end ();
while (begin_it != end_it && is_blank (*begin_it)) ++begin_it;
while (end_it != begin_it && is_blank (*(end_it - 1))) --end_it;
bool in_space = false;
for (auto it = begin_it; it != end_it; ++ it)
{
if (is_blank (*it))
{
if (includemidblank)
{
if (!in_space)
{
result.push_back (E (' '));
in_space = true;
}
}
else
{
result.push_back (*it);
in_space = true;
}
}
else
{
result.push_back (*it);
in_space = false;
}
}
if (upper) return l0km::toupper (result);
else return l0km::tolower (result);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> bool IsNormalizeStringEquals (const std::basic_string <E, TR, AL> &l, const std::basic_string <E, TR, AL> &r, bool includemidblank = false)
{
auto _local_strlen = [] (const E *p) -> size_t {
size_t cnt = 0;
while (*(p + cnt)) { cnt ++; }
return cnt;
};
const E *pl = l.c_str ();
const E *pr = r.c_str ();
while (*pl && is_blank (*pl)) ++ pl;
while (*pr && is_blank (*pr)) ++ pr;
const E *el = l.c_str () + _local_strlen (l.c_str ());
const E *er = r.c_str () + _local_strlen (r.c_str ());
while (el > pl && is_blank (*(el - 1))) --el;
while (er > pr && is_blank (*(er - 1))) --er;
while (pl < el && pr < er)
{
if (includemidblank)
{
if (is_blank (*pl) && is_blank (*pr))
{
while (pl < el && is_blank (*pl)) ++pl;
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
else if (is_blank (*pl))
{
while (pl < el && is_blank (*pl)) ++pl;
continue;
}
else if (is_blank (*pr))
{
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
}
if (l0km::tolower (*pl) != l0km::tolower (*pr)) return false;
++ pl;
++ pr;
}
while (pl < el && is_blank (*pl)) ++ pl;
while (pr < er && is_blank (*pr)) ++ pr;
return pl == el && pr == er;
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> int64_t NormalizeStringCompare (const std::basic_string <E, TR, AL> &l, const std::basic_string <E, TR, AL> &r, bool includemidblank = false)
{
auto _local_strlen = [] (const E *p) -> size_t {
size_t cnt = 0;
while (*(p + cnt)) { cnt ++; }
return cnt;
};
const E *pl = l.c_str ();
const E *pr = r.c_str ();
while (*pl && is_blank (*pl)) ++ pl;
while (*pr && is_blank (*pr)) ++ pr;
const E *el = l.c_str () + _local_strlen (l.c_str ());
const E *er = r.c_str () + _local_strlen (r.c_str ());
while (el > pl && is_blank (*(el - 1))) -- el;
while (er > pr && is_blank (*(er - 1))) -- er;
while (pl < el && pr < er)
{
if (includemidblank)
{
if (is_blank (*pl) && is_blank (*pr))
{
while (pl < el && is_blank (*pl)) ++pl;
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
else if (is_blank (*pl))
{
while (pl < el && is_blank (*pl)) ++pl;
continue;
}
else if (is_blank (*pr))
{
while (pr < er && is_blank (*pr)) ++pr;
continue;
}
}
E chl = l0km::tolower (*pl);
E chr = l0km::tolower (*pr);
if (chl != chr) return (int64_t)chl - (int64_t)chr;
++ pl;
++ pr;
}
while (pl < el && is_blank (*pl)) ++ pl;
while (pr < er && is_blank (*pr)) ++ pr;
if (pl == el && pr == er) return 0;
if (pl == el) return -1;
if (pr == er) return 1;
return (int64_t)l0km::tolower (*pl) - (int64_t)l0km::tolower (*pr);
}
template <typename CharT> bool IsNormalizeStringEquals (const CharT *l, const CharT *r, bool includemidblank = false)
{
if (!l || !r) return l == r;
auto skip_blank = [] (const CharT *&p)
{
while (*p && is_blank (*p)) ++ p;
};
const CharT *p1 = l;
const CharT *p2 = r;
skip_blank (p1);
skip_blank (p2);
while (*p1 && *p2)
{
CharT ch1 = l0km::tolower (*p1);
CharT ch2 = l0km::tolower (*p2);
if (ch1 != ch2) return false;
++ p1;
++ p2;
if (includemidblank)
{
if (is_blank (*p1) || is_blank (*p2))
{
skip_blank (p1);
skip_blank (p2);
}
}
}
skip_blank (p1);
skip_blank (p2);
return *p1 == 0 && *p2 == 0;
}
template <typename CharT> int64_t NormalizeStringCompare (const CharT *l, const CharT *r, bool includemidblank = false)
{
if (!l || !r) return l ? 1 : (r ? -1 : 0);
auto skip_blank = [] (const CharT *&p)
{
while (*p && is_blank (*p)) ++ p;
};
const CharT *p1 = l;
const CharT *p2 = r;
skip_blank (p1);
skip_blank (p2);
while (*p1 && *p2)
{
CharT ch1 = l0km::tolower (*p1);
CharT ch2 = l0km::tolower (*p2);
if (ch1 != ch2) return (ch1 < ch2) ? -1 : 1;
++ p1;
++ p2;
if (includemidblank)
{
if (is_blank (*p1) || is_blank (*p2))
{
skip_blank (p1);
skip_blank (p2);
}
}
}
skip_blank (p1);
skip_blank (p2);
if (*p1 == 0 && *p2 == 0) return 0;
if (*p1 == 0) return -1;
return 1;
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> bool IsNormalizeStringEmpty (const std::basic_string <E, TR, AL> &str)
{
return IsNormalizeStringEquals (str, std::basic_string <E, TR, AL> ());
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>> std::basic_string <E, TR, AL> StringTrim (const std::basic_string <E, TR, AL> &str, bool includemidblank = false)
{
typedef std::basic_string <E, TR, AL> string_type;
typedef typename string_type::size_type size_type;
if (str.empty ()) return string_type ();
size_type first = 0;
size_type last = str.size ();
while (first < last && is_blank (str [first])) ++first;
while (last > first && is_blank (str [last - 1])) --last;
if (first == last) return string_type ();
string_type result;
result.reserve (last - first);
bool in_space = false;
for (size_type i = first; i < last; ++ i)
{
if (is_blank (str [i]))
{
if (includemidblank)
{
if (!in_space)
{
result.push_back (E (' '));
in_space = true;
}
}
else
{
result.push_back (str [i]);
in_space = true;
}
}
else
{
result.push_back (str [i]);
in_space = false;
}
}
return result;
}
template <typename E, typename TR = std::char_traits<E>, typename AL = std::allocator <E>> size_t GetNormalizeStringLength (const std::basic_string <E, TR, AL> &str, bool includemidblank = false)
{
typedef typename std::basic_string <E, TR, AL>::size_type size_type;
if (str.empty ()) return 0;
size_type first = 0, last = str.size ();
while (first < last && is_blank (str [first])) ++first;
while (last > first && is_blank (str [last - 1])) --last;
if (first == last) return 0;
size_t length = 0;
bool in_space = false;
for (size_type i = first; i < last; ++i)
{
if (is_blank (str [i]))
{
if (includemidblank)
{
if (!in_space)
{
++ length;
in_space = true;
}
}
else
{
++ length;
in_space = true;
}
}
else
{
++ length;
in_space = false;
}
}
return length;
}
namespace std
{
template <typename ct, typename tr = std::char_traits <ct>, typename al = std::allocator <ct>> class basic_nstring: public std::basic_string <ct, tr, al>
{
using base = std::basic_string <ct, tr, al>;
bool default_upper = false, default_include_blank_in_str = false;
public:
using typename base::size_type;
using typename base::value_type;
using base::base;
basic_nstring (): base (), default_upper (false), default_include_blank_in_str (false) {}
basic_nstring (const ct *pStr): base (pStr), default_upper (false), default_include_blank_in_str (false) {}
basic_nstring (const base &str): base (str) {}
basic_nstring (base &&str): base (std::move (str)) {}
basic_nstring (const ct *data, size_type count): base (data, count), default_upper (false), default_include_blank_in_str (false) {}
template <std::size_t N> basic_nstring (const ct (&arr) [N]) : base (arr, N) {}
template <typename InputIt> basic_nstring (InputIt first, InputIt last): base (first, last), default_upper (false), default_include_blank_in_str (false) {}
bool upper_default () const { return this->default_upper; }
bool upper_default (bool value) { return this->default_upper = value; }
bool include_blank_in_str_middle () const { return this->default_include_blank_in_str; }
bool include_blank_in_str_middle (bool value) { return this->default_include_blank_in_str = value; }
base normalize (bool upper, bool includemidblank) const
{
return NormalizeString <ct, tr, al> (*this, upper, includemidblank);
}
base normalize (bool upper) const
{
return this->normalize (upper, default_include_blank_in_str);
}
base normalize () const { return this->normalize (default_upper); }
base upper (bool includemidblank) const
{
return NormalizeString <ct, tr, al> (*this, true, includemidblank);
}
base upper () const { return this->upper (default_include_blank_in_str); }
base lower (bool includemidblank) const
{
return NormalizeString <ct, tr, al> (*this, false, includemidblank);
}
base lower () const { return this->lower (default_include_blank_in_str); }
base trim (bool includemidblank) const
{
return StringTrim <ct, tr, al> (*this, includemidblank);
}
base trim () const { return this->trim (default_include_blank_in_str); }
size_t length (bool includemidblank) const { return GetNormalizeStringLength (*this, includemidblank); }
size_t length () const { return length (default_include_blank_in_str); }
bool empty () const
{
return IsNormalizeStringEmpty (*this);
}
bool equals (const base &another, bool includemidblank) const
{
return IsNormalizeStringEquals <ct, tr, al> (*this, another, includemidblank);
}
bool equals (const base &another) const { return equals (another, default_include_blank_in_str); }
int64_t compare (const base &another, bool includemidblank) const
{
return NormalizeStringCompare <ct, tr, al> (*this, another, includemidblank);
}
int64_t compare (const base &another) const { return compare (another, default_include_blank_in_str); }
base &string () { return *this; }
base to_string (bool upper, bool includemidblank) const { return this->normalize (upper, includemidblank); }
base to_string (bool upper) const { return this->normalize (upper, default_include_blank_in_str); }
base to_string () const { return this->normalize (default_upper); }
bool operator == (const base &other) const { return equals (other, false); }
bool operator != (const base &other) const { return !equals (other, false); }
bool operator < (const base &other) const { return compare (other, false) < 0; }
bool operator > (const base &other) const { return compare (other, false) > 0; }
bool operator <= (const base &other) const { return compare (other, false) <= 0; }
bool operator >= (const base &other) const { return compare (other, false) >= 0; }
int64_t operator - (const base &other) const { return compare (other, false); }
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static bool equals (const std::basic_string <E> &l, const std::basic_string <E> &r, bool remove_mid_blank = false)
{
return IsNormalizeStringEquals <E, TR, AL> (l, r, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static int64_t compare (const std::basic_string <E> &l, const std::basic_string <E> &r, bool remove_mid_blank = false)
{
return NormalizeStringCompare <E, TR, AL> (l, r, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_string <E, TR, AL> normalize (const std::basic_string <E> &str, bool to_upper = false, bool remove_mid_blank = false)
{
return NormalizeString <E, TR, AL> (str, to_upper, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_string <E, TR, AL> trim (const std::basic_string <E> &str, bool remove_mid_blank = false)
{
return StringTrim <E, TR, AL> (str, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static size_t length (const std::basic_string <E> &str, bool remove_mid_blank = false)
{
return GetNormalizeStringLength <E, TR, AL> (str, remove_mid_blank);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static bool empty (const std::basic_string <E> &str)
{
return IsNormalizeStringEmpty <E, TR, AL> (str);
}
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_nstring <E, TR, AL> to_nstring (std::basic_string <E> &str) { return std::basic_nstring <E> (str); }
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_nstring <E, TR, AL> toupper (const std::basic_nstring <E, TR, AL> &str) { return l0km::toupper (str); }
template <typename E, typename TR = std::char_traits <E>, typename AL = std::allocator <E>>
static std::basic_nstring <E, TR, AL> tolower (const std::basic_nstring <E, TR, AL> &str) { return l0km::tolower (str); }
};
typedef basic_nstring <char> nstring;
typedef basic_nstring <wchar_t> wnstring;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
// priread.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "priread.h"
// 这是导出变量的一个示例
PRIREAD_API int npriread=0;
// 这是导出函数的一个示例。
PRIREAD_API int fnpriread(void)
{
return 42;
}
// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 priread.h
Cpriread::Cpriread()
{
return;
}

View File

@@ -0,0 +1,22 @@
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 PRIREAD_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// PRIREAD_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef PRIREAD_EXPORTS
#define PRIREAD_API __declspec(dllexport)
#else
#define PRIREAD_API __declspec(dllimport)
#endif
// 此类是从 priread.dll 导出的
class PRIREAD_API Cpriread {
public:
Cpriread(void);
// TODO: 在此添加您的方法。
};
extern PRIREAD_API int npriread;
PRIREAD_API int fnpriread(void);

View File

@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{99D714D9-F40D-425B-BAFA-8B41C17971A5}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>priread</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;PRIREAD_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;PRIREAD_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;PRIREAD_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;PRIREAD_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="localeex.h" />
<ClInclude Include="nstring.h" />
<ClInclude Include="prifile.h" />
<ClInclude Include="priread.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="themeinfo.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</PrecompiledHeader>
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
</PrecompiledHeader>
</ClCompile>
<ClCompile Include="priread.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="priread.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="prifile.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="nstring.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="localeex.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="themeinfo.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="priread.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,8 @@
// stdafx.cpp : 只包括标准包含文件的源文件
// priread.pch 将作为预编译头
// stdafx.obj 将包含预编译类型信息
#include "stdafx.h"
// TODO: 在 STDAFX.H 中引用任何所需的附加头文件,
//而不是在此文件中引用

View File

@@ -0,0 +1,20 @@
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头中排除极少使用的资料
// Windows 头文件:
#include <windows.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 构造函数将是显式的
#include <atlbase.h>
#include <atlstr.h>
// TODO: 在此处引用程序需要的其他头文件

View File

@@ -0,0 +1,8 @@
#pragma once
// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h并将
// 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
#include <SDKDDKVer.h>

View File

@@ -0,0 +1,40 @@
#pragma once
#include <Windows.h>
bool IsHighContrastEnabled ()
{
HIGHCONTRAST hc = {sizeof (HIGHCONTRAST)};
if (SystemParametersInfo (SPI_GETHIGHCONTRAST, sizeof (hc), &hc, 0)) return (hc.dwFlags & HCF_HIGHCONTRASTON) != 0;
return false;
}
enum class HighContrastTheme
{
None,
Black,
White,
Other
};
HighContrastTheme GetHighContrastTheme ()
{
HIGHCONTRAST hc = {sizeof (HIGHCONTRAST)};
if (!SystemParametersInfo (SPI_GETHIGHCONTRAST, sizeof (hc), &hc, 0)) return HighContrastTheme::None;
if (!(hc.dwFlags & HCF_HIGHCONTRASTON)) return HighContrastTheme::None;
COLORREF bgColor = GetSysColor (COLOR_WINDOW);
COLORREF textColor = GetSysColor (COLOR_WINDOWTEXT);
int brightnessBg = (GetRValue (bgColor) + GetGValue (bgColor) + GetBValue (bgColor)) / 3;
int brightnessText = (GetRValue (textColor) + GetGValue (textColor) + GetBValue (textColor)) / 3;
if (brightnessBg < brightnessText) return HighContrastTheme::Black;
else if (brightnessBg > brightnessText) return HighContrastTheme::White;
else return HighContrastTheme::Other;
}
int GetDPI ()
{
HDC hDC = GetDC (NULL);
int DPI_A = (int)(((double)GetDeviceCaps (hDC, 118) / (double)GetDeviceCaps (hDC, 8)) * 100);
int DPI_B = (int)(((double)GetDeviceCaps (hDC, 88) / 96) * 100);
ReleaseDC (NULL, hDC);
if (DPI_A == 100) return DPI_B;
else if (DPI_B == 100) return DPI_A;
else if (DPI_A == DPI_B) return DPI_A;
else return 0;
}

View File

@@ -7,10 +7,16 @@
try {
if (swJson) ret = JSON.parse(swJson);
} catch (e) {}
try {
if (ret && typeof ret.jsontext !== "undefined") {
ret["json"] = JSON.parse(ret.jsontext);
delete ret.jsontext;
}
} catch (e) {}
if (callback) callback(ret);
}
global.Package = {
reader: function(pkgPath) { external.Package.reader(pkgPath); },
reader: function(pkgPath) { return external.Package.reader(pkgPath); },
manager: {
add: function(swPkgPath, uOptions) {
return new Promise(function(resolve, reject, progress) {
@@ -124,5 +130,37 @@
});
},
},
manifest: function(swManifestPath) { return external.Package.manifest(swManifestPath); },
manifestFromInstallLocation: function(swInstallLocation) { return external.Package.fromInstallLocation(swInstallLocation); },
readFromPackage: function(swPkgPath, bUsePri) {
if (bUsePri === null || bUsePri === void 0) bUsePri = false;
return new Promise(function(resolve, reject) {
external.Package.readFromPackageAsync(swPkgPath, bUsePri, function(result) {
parseJsonCallback(result, resolve);
}, function(error) {
parseJsonCallback(error, reject);
});
});
},
readFromManifest: function(swPkgPath, bUsePri) {
if (bUsePri === null || bUsePri === void 0) bUsePri = false;
return new Promise(function(resolve, reject) {
external.Package.readFromManifestAsync(swPkgPath, bUsePri, function(result) {
parseJsonCallback(result, resolve);
}, function(error) {
parseJsonCallback(error, reject);
});
});
},
readFromInstallLocation: function(swPkgPath, bUsePri) {
if (bUsePri === null || bUsePri === void 0) bUsePri = false;
return new Promise(function(resolve, reject) {
external.Package.readFromInstallLocationAsync(swPkgPath, bUsePri, function(result) {
parseJsonCallback(result, resolve);
}, function(error) {
parseJsonCallback(error, reject);
});
});
},
};
})(this);