mirror of
https://github.com/modernw/App-Installer-For-Windows-8.x-Reset.git
synced 2026-04-11 17:57:19 +10:00
Update about Manager.
This commit is contained in:
@@ -4,6 +4,8 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace AppxPackage
|
namespace AppxPackage
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@@ -229,4 +231,31 @@ namespace AppxPackage.Info
|
|||||||
return a._value < value;
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ using System.Text;
|
|||||||
using AppxPackage.Info;
|
using AppxPackage.Info;
|
||||||
using NativeWrappers;
|
using NativeWrappers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
//using PriFormat;
|
||||||
namespace AppxPackage
|
namespace AppxPackage
|
||||||
{
|
{
|
||||||
public static class DataUrlHelper
|
public static class DataUrlHelper
|
||||||
@@ -543,13 +545,14 @@ namespace AppxPackage
|
|||||||
private MRApplication ReadSingleApplication (IntPtr hKeyValues)
|
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);
|
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 pairCount = Marshal.ReadInt32 (hKeyValues);
|
||||||
int baseOffset = Marshal.SizeOf (typeof (uint));
|
IntPtr arrayBase = IntPtr.Add (hKeyValues, sizeof (uint));
|
||||||
int pairSize = Marshal.SizeOf (typeof (PackageReadHelper.PAIR_PVOID));
|
for (int i = 0; i < pairCount; i++)
|
||||||
for (int j = 0; j < pairCount; j++)
|
|
||||||
{
|
{
|
||||||
IntPtr pPair = IntPtr.Add (hKeyValues, baseOffset + j * pairSize);
|
IntPtr pPairPtr = Marshal.ReadIntPtr (arrayBase, i * IntPtr.Size);
|
||||||
var pair = (PackageReadHelper.PAIR_PVOID)Marshal.PtrToStructure (pPair, typeof (PackageReadHelper.PAIR_PVOID));
|
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;
|
if (pair.lpKey == IntPtr.Zero) continue;
|
||||||
string key = Marshal.PtrToStringUni (pair.lpKey);
|
string key = Marshal.PtrToStringUni (pair.lpKey);
|
||||||
if (string.IsNullOrEmpty (key)) continue;
|
if (string.IsNullOrEmpty (key)) continue;
|
||||||
@@ -849,12 +852,12 @@ namespace AppxPackage
|
|||||||
private string m_filePath = string.Empty;
|
private string m_filePath = string.Empty;
|
||||||
private bool m_usePRI = false;
|
private bool m_usePRI = false;
|
||||||
private bool m_enablePRI = false;
|
private bool m_enablePRI = false;
|
||||||
private PriReader m_pri = new PriReader ();
|
private PriReader m_pri = null;
|
||||||
public IntPtr Instance => m_hReader;
|
public IntPtr Instance => m_hReader;
|
||||||
public string FileRoot{ get { return Path.GetPathRoot (m_filePath); } }
|
public string FileRoot{ get { return Path.GetPathRoot (m_filePath); } }
|
||||||
private void InitPri ()
|
private void InitPri ()
|
||||||
{
|
{
|
||||||
m_pri.Dispose ();
|
m_pri?.Dispose ();
|
||||||
if (!m_usePRI) return;
|
if (!m_usePRI) return;
|
||||||
#region Get PRI IStream
|
#region Get PRI IStream
|
||||||
switch (Type)
|
switch (Type)
|
||||||
@@ -862,45 +865,12 @@ namespace AppxPackage
|
|||||||
case PackageType.Appx:
|
case PackageType.Appx:
|
||||||
{
|
{
|
||||||
var pripath = Path.Combine (FileRoot, "resources.pri");
|
var pripath = Path.Combine (FileRoot, "resources.pri");
|
||||||
m_pri.Create (pripath);
|
m_pri = new PriReader (pripath);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
try
|
return;
|
||||||
{
|
|
||||||
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) { }
|
|
||||||
}
|
}
|
||||||
public PackageType Type
|
public PackageType Type
|
||||||
{
|
{
|
||||||
@@ -979,6 +949,25 @@ namespace AppxPackage
|
|||||||
Newtonsoft.Json.Formatting.Indented
|
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 ()
|
private object BuildJsonObject ()
|
||||||
{
|
{
|
||||||
return new
|
return new
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using AppxPackage.Info;
|
using AppxPackage.Info;
|
||||||
using NativeWrappers;
|
using NativeWrappers;
|
||||||
|
//using PriFormat;
|
||||||
namespace AppxPackage
|
namespace AppxPackage
|
||||||
{
|
{
|
||||||
internal static partial class ConstData
|
internal static partial class ConstData
|
||||||
@@ -220,6 +222,9 @@ namespace AppxPackage
|
|||||||
if (hr.Succeeded) return ret != 0;
|
if (hr.Succeeded) return ret != 0;
|
||||||
else return defaultValue;
|
else return defaultValue;
|
||||||
}
|
}
|
||||||
|
#if DEBUG
|
||||||
|
public string Debug_StringValue (string attr) => StringValue (attr);
|
||||||
|
#endif
|
||||||
protected string StringResValue (string attr)
|
protected string StringResValue (string attr)
|
||||||
{
|
{
|
||||||
var res = StringValue (attr);
|
var res = StringValue (attr);
|
||||||
@@ -237,6 +242,7 @@ namespace AppxPackage
|
|||||||
protected string PathResValue (string attr)
|
protected string PathResValue (string attr)
|
||||||
{
|
{
|
||||||
var res = StringValue (attr);
|
var res = StringValue (attr);
|
||||||
|
//var id = new PriFormat.PriResourceIdentifier (res);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (m_usePri && m_enablePri)
|
if (m_usePri && m_enablePri)
|
||||||
@@ -267,7 +273,7 @@ namespace AppxPackage
|
|||||||
{
|
{
|
||||||
IntPtr base64Head = IntPtr.Zero;
|
IntPtr base64Head = IntPtr.Zero;
|
||||||
var base64s = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
|
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);
|
return PackageReadHelper.GetStringAndFreeFromPkgRead (base64s);
|
||||||
}
|
}
|
||||||
catch (Exception) { return ""; }
|
catch (Exception) { return ""; }
|
||||||
@@ -288,7 +294,7 @@ namespace AppxPackage
|
|||||||
pic = PackageReadHelper.GetFileFromPayloadPackage (pkg, logopath);
|
pic = PackageReadHelper.GetFileFromPayloadPackage (pkg, logopath);
|
||||||
IntPtr base64Head = IntPtr.Zero;
|
IntPtr base64Head = IntPtr.Zero;
|
||||||
var lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
|
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 && !string.IsNullOrEmpty (PackageReadHelper.GetStringFromPkgRead (lpstr))))
|
||||||
{
|
{
|
||||||
if (lpstr != IntPtr.Zero) PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr);
|
if (lpstr != IntPtr.Zero) PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr);
|
||||||
@@ -300,7 +306,7 @@ namespace AppxPackage
|
|||||||
{
|
{
|
||||||
pic1 = PackageReadHelper.GetFileFromPayloadPackage (pkg1, logopath);
|
pic1 = PackageReadHelper.GetFileFromPayloadPackage (pkg1, logopath);
|
||||||
lpstr = PackageReadHelper.StreamToBase64W (pic1, null, 0, out base64Head);
|
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) { }
|
catch (Exception) { }
|
||||||
@@ -478,11 +484,7 @@ namespace AppxPackage
|
|||||||
pic = PackageReadHelper.GetAppxFileFromAppxPackage (m_hReader, value);
|
pic = PackageReadHelper.GetAppxFileFromAppxPackage (m_hReader, value);
|
||||||
IntPtr base64Head = IntPtr.Zero;
|
IntPtr base64Head = IntPtr.Zero;
|
||||||
IntPtr lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
|
IntPtr lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
|
||||||
if (base64Head != IntPtr.Zero)
|
//if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
|
||||||
{
|
|
||||||
PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head);
|
|
||||||
base64Head = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
return lpstr != IntPtr.Zero
|
return lpstr != IntPtr.Zero
|
||||||
? PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr)
|
? PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr)
|
||||||
: "";
|
: "";
|
||||||
@@ -508,16 +510,12 @@ namespace AppxPackage
|
|||||||
{
|
{
|
||||||
IntPtr header = IntPtr.Zero;
|
IntPtr header = IntPtr.Zero;
|
||||||
PackageReadHelper.GetSuitablePackageFromBundle (m_hReader, out header, out pkg);
|
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;
|
header = IntPtr.Zero;
|
||||||
pic = PackageReadHelper.GetFileFromPayloadPackage (pkg, value);
|
pic = PackageReadHelper.GetFileFromPayloadPackage (pkg, value);
|
||||||
IntPtr base64Head = IntPtr.Zero;
|
IntPtr base64Head = IntPtr.Zero;
|
||||||
IntPtr lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
|
IntPtr lpstr = PackageReadHelper.StreamToBase64W (pic, null, 0, out base64Head);
|
||||||
if (base64Head != IntPtr.Zero)
|
//if (base64Head != IntPtr.Zero) { PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head); base64Head = IntPtr.Zero; }
|
||||||
{
|
|
||||||
PackageReadHelper.GetStringAndFreeFromPkgRead (base64Head);
|
|
||||||
base64Head = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
return lpstr != IntPtr.Zero
|
return lpstr != IntPtr.Zero
|
||||||
? PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr)
|
? PackageReadHelper.GetStringAndFreeFromPkgRead (lpstr)
|
||||||
: "";
|
: "";
|
||||||
@@ -667,13 +665,14 @@ namespace AppxPackage
|
|||||||
private PRApplication ReadSingleApplication (IntPtr hKeyValues)
|
private PRApplication ReadSingleApplication (IntPtr hKeyValues)
|
||||||
{
|
{
|
||||||
var app = new PRApplication (ref m_hReader, ref m_priBundle, ref m_usePri, ref m_enablePri);
|
var app = new PRApplication (ref m_hReader, ref m_priBundle, ref m_usePri, ref m_enablePri);
|
||||||
uint pairCount = (uint)Marshal.ReadInt32 (hKeyValues);
|
int pairCount = Marshal.ReadInt32 (hKeyValues);
|
||||||
int baseOffset = Marshal.SizeOf (typeof (uint));
|
IntPtr arrayBase = IntPtr.Add (hKeyValues, sizeof (uint));
|
||||||
int pairSize = Marshal.SizeOf (typeof (PackageReadHelper.PAIR_PVOID));
|
for (int i = 0; i < pairCount; i++)
|
||||||
for (int j = 0; j < pairCount; j++)
|
|
||||||
{
|
{
|
||||||
IntPtr pPair = IntPtr.Add (hKeyValues, baseOffset + j * pairSize);
|
IntPtr pPairPtr = Marshal.ReadIntPtr (arrayBase, i * IntPtr.Size);
|
||||||
var pair = (PackageReadHelper.PAIR_PVOID) Marshal.PtrToStructure (pPair, typeof (PackageReadHelper.PAIR_PVOID));
|
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;
|
if (pair.lpKey == IntPtr.Zero) continue;
|
||||||
string key = Marshal.PtrToStringUni (pair.lpKey);
|
string key = Marshal.PtrToStringUni (pair.lpKey);
|
||||||
if (string.IsNullOrEmpty (key)) continue;
|
if (string.IsNullOrEmpty (key)) continue;
|
||||||
@@ -1064,41 +1063,11 @@ namespace AppxPackage
|
|||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
try
|
return;
|
||||||
{
|
|
||||||
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) { }
|
|
||||||
}
|
}
|
||||||
|
#if DEBUG
|
||||||
|
public PriReaderBundle PriInstance => m_priBundle;
|
||||||
|
#endif
|
||||||
public PackageType Type
|
public PackageType Type
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -1176,6 +1145,26 @@ namespace AppxPackage
|
|||||||
Newtonsoft.Json.Formatting.Indented
|
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 ()
|
private object BuildJsonObject ()
|
||||||
{
|
{
|
||||||
return new
|
return new
|
||||||
|
|||||||
@@ -401,6 +401,8 @@ namespace NativeWrappers
|
|||||||
);
|
);
|
||||||
[DllImport (DllName, CallingConvention = CallConv)]
|
[DllImport (DllName, CallingConvention = CallConv)]
|
||||||
public static extern void PackageReaderFreeString (IntPtr p);
|
public static extern void PackageReaderFreeString (IntPtr p);
|
||||||
|
[DllImport (DllName, CallingConvention = CallConv, CharSet = CharSet.Unicode)]
|
||||||
|
public static extern IntPtr GetManifestPrerequistieSystemVersionName (IntPtr hReader, string lpName);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ namespace AppxPackage
|
|||||||
[return: MarshalAs (UnmanagedType.Bool)]
|
[return: MarshalAs (UnmanagedType.Bool)]
|
||||||
public static extern bool IsMsResourceUri ([MarshalAs (UnmanagedType.LPWStr)] string pResUri);
|
public static extern bool IsMsResourceUri ([MarshalAs (UnmanagedType.LPWStr)] string pResUri);
|
||||||
[DllImport (DLL, CallingConvention = CallingConvention.Cdecl)]
|
[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)
|
public static string PtrToString (IntPtr ptr)
|
||||||
{
|
{
|
||||||
if (ptr == IntPtr.Zero) return null;
|
if (ptr == IntPtr.Zero) return null;
|
||||||
@@ -66,8 +66,6 @@ namespace AppxPackage
|
|||||||
PriFormatFreeString (ptr); // 如果 DLL 返回的内存要求 free
|
PriFormatFreeString (ptr); // 如果 DLL 返回的内存要求 free
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
[DllImport (DLL, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void FreePriString (IntPtr p);
|
|
||||||
}
|
}
|
||||||
public static class LpcwstrListHelper
|
public static class LpcwstrListHelper
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using System;
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Runtime.InteropServices.ComTypes;
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace AppxPackage
|
namespace AppxPackage
|
||||||
{
|
{
|
||||||
@@ -75,17 +77,20 @@ namespace AppxPackage
|
|||||||
public void AddSearch (string uri) { AddSearch (new string [] { uri }); }
|
public void AddSearch (string uri) { AddSearch (new string [] { uri }); }
|
||||||
public string Resource (string resName)
|
public string Resource (string resName)
|
||||||
{
|
{
|
||||||
IntPtr ret = IntPtr.Zero;
|
var task = Task.Factory.StartNew (() => {
|
||||||
try
|
IntPtr ret = IntPtr.Zero;
|
||||||
{
|
try
|
||||||
ret = PriFileHelper.GetPriResource (m_hPriFile, resName);
|
{
|
||||||
if (ret == IntPtr.Zero) return string.Empty;
|
ret = PriFileHelper.GetPriResource (m_hPriFile, resName);
|
||||||
return PriFileHelper.PtrToString (ret);
|
if (ret == IntPtr.Zero) return string.Empty;
|
||||||
}
|
return PriFileHelper.PtrToString (ret);
|
||||||
finally
|
}
|
||||||
{
|
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)
|
public Dictionary<string, string> Resources (IEnumerable<string> resnames)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using System.Drawing;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using AppxPackage;
|
using AppxPackage;
|
||||||
using ModernNotice;
|
using ModernNotice;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Bridge
|
namespace Bridge
|
||||||
{
|
{
|
||||||
@@ -494,10 +495,183 @@ namespace Bridge
|
|||||||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||||||
public class _I_Package
|
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 AppxPackage.PackageReader Reader (string packagePath) { return new AppxPackage.PackageReader (packagePath); }
|
||||||
public _I_PackageManager Manager => new _I_PackageManager ();
|
public _I_PackageManager Manager => new _I_PackageManager ();
|
||||||
public AppxPackage.ManifestReader Manifest (string manifestPath) { return new AppxPackage.ManifestReader (manifestPath); }
|
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 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)]
|
[ComVisible (true)]
|
||||||
[ClassInterface (ClassInterfaceType.AutoDual)]
|
[ClassInterface (ClassInterfaceType.AutoDual)]
|
||||||
|
|||||||
@@ -93,10 +93,6 @@
|
|||||||
<Project>{ffd3fd52-37a8-4f43-883c-de8d996cb0e0}</Project>
|
<Project>{ffd3fd52-37a8-4f43-883c-de8d996cb0e0}</Project>
|
||||||
<Name>DataUtils</Name>
|
<Name>DataUtils</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\PriFileFormat\PriFile.csproj">
|
|
||||||
<Project>{ef4012d4-ef08-499c-b803-177739350b2d}</Project>
|
|
||||||
<Name>PriFile</Name>
|
|
||||||
</ProjectReference>
|
|
||||||
<ProjectReference Include="..\PrivateInit\PrivateInit.csproj">
|
<ProjectReference Include="..\PrivateInit\PrivateInit.csproj">
|
||||||
<Project>{8e708d9a-6325-4aa9-b5a5-d1b5eca8eef7}</Project>
|
<Project>{8e708d9a-6325-4aa9-b5a5-d1b5eca8eef7}</Project>
|
||||||
<Name>PrivateInit</Name>
|
<Name>PrivateInit</Name>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
namespace Manager
|
namespace Manager
|
||||||
{
|
{
|
||||||
public partial class ManagerShell: WAShell.WebAppForm
|
public partial class ManagerShell: WAShell.WebAppForm
|
||||||
@@ -65,10 +66,6 @@ namespace Manager
|
|||||||
{
|
{
|
||||||
var root = Path.GetDirectoryName (DataUtils.Utilities.GetCurrentProgramPath ());
|
var root = Path.GetDirectoryName (DataUtils.Utilities.GetCurrentProgramPath ());
|
||||||
WebUI.Navigate (Path.Combine (root, "html\\manager.html"));
|
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)
|
private void ManagerShell_Resize (object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace PriFileFormat
|
|||||||
public class ComStreamWrapper: Stream
|
public class ComStreamWrapper: Stream
|
||||||
{
|
{
|
||||||
private IStream comStream;
|
private IStream comStream;
|
||||||
|
private object _sync = new object ();
|
||||||
public ComStreamWrapper (IStream stream)
|
public ComStreamWrapper (IStream stream)
|
||||||
{
|
{
|
||||||
if (stream == null)
|
if (stream == null)
|
||||||
@@ -30,20 +31,102 @@ namespace PriFileFormat
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
System.Runtime.InteropServices.ComTypes.STATSTG stat;
|
lock (_sync)
|
||||||
comStream.Stat (out stat, 1); // STATFLAG_NONAME = 1
|
{
|
||||||
return stat.cbSize;
|
System.Runtime.InteropServices.ComTypes.STATSTG stat;
|
||||||
|
comStream.Stat (out stat, 1); // STATFLAG_NONAME = 1
|
||||||
|
return stat.cbSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public override long Position
|
public override long Position
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
IntPtr posPtr = Marshal.AllocHGlobal (sizeof (long));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// SEEK_CUR = 1
|
||||||
|
comStream.Seek (0, 1, posPtr);
|
||||||
|
return Marshal.ReadInt64 (posPtr);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
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.");
|
||||||
|
|
||||||
|
IntPtr bytesRead = Marshal.AllocHGlobal (sizeof (int));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
comStream.Read (buffer, count, bytesRead);
|
||||||
|
return Marshal.ReadInt32 (bytesRead);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
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.");
|
||||||
|
|
||||||
|
IntPtr bytesWritten = Marshal.AllocHGlobal (sizeof (int));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
comStream.Write (buffer, count, bytesWritten);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal (bytesWritten);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override long Seek (long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
int originInt = 0;
|
||||||
|
switch (origin)
|
||||||
|
{
|
||||||
|
case SeekOrigin.Begin: originInt = 0; break; // STREAM_SEEK_SET
|
||||||
|
case SeekOrigin.Current: originInt = 1; break; // STREAM_SEEK_CUR
|
||||||
|
case SeekOrigin.End: originInt = 2; break; // STREAM_SEEK_END
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr posPtr = Marshal.AllocHGlobal (sizeof (long));
|
IntPtr posPtr = Marshal.AllocHGlobal (sizeof (long));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// SEEK_CUR = 1
|
comStream.Seek (offset, originInt, posPtr);
|
||||||
comStream.Seek (0, 1, posPtr);
|
|
||||||
return Marshal.ReadInt64 (posPtr);
|
return Marshal.ReadInt64 (posPtr);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -51,71 +134,13 @@ namespace PriFileFormat
|
|||||||
Marshal.FreeHGlobal (posPtr);
|
Marshal.FreeHGlobal (posPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
|
||||||
{
|
|
||||||
// SEEK_SET = 0
|
|
||||||
comStream.Seek (value, 0, IntPtr.Zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public override void Flush ()
|
|
||||||
{
|
|
||||||
comStream.Commit (0); // STGC_DEFAULT = 0
|
|
||||||
}
|
|
||||||
public override int Read (byte [] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (offset != 0)
|
|
||||||
throw new NotSupportedException ("Offset != 0 not supported in this wrapper.");
|
|
||||||
|
|
||||||
IntPtr bytesRead = Marshal.AllocHGlobal (sizeof (int));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
comStream.Read (buffer, count, bytesRead);
|
|
||||||
return Marshal.ReadInt32 (bytesRead);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal (bytesRead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public override void Write (byte [] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (offset != 0)
|
|
||||||
throw new NotSupportedException ("Offset != 0 not supported in this wrapper.");
|
|
||||||
|
|
||||||
IntPtr bytesWritten = Marshal.AllocHGlobal (sizeof (int));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
comStream.Write (buffer, count, bytesWritten);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal (bytesWritten);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public override long Seek (long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
int originInt = 0;
|
|
||||||
switch (origin)
|
|
||||||
{
|
|
||||||
case SeekOrigin.Begin: originInt = 0; break; // STREAM_SEEK_SET
|
|
||||||
case SeekOrigin.Current: originInt = 1; break; // STREAM_SEEK_CUR
|
|
||||||
case SeekOrigin.End: originInt = 2; break; // STREAM_SEEK_END
|
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr posPtr = Marshal.AllocHGlobal (sizeof (long));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
comStream.Seek (offset, originInt, posPtr);
|
|
||||||
return Marshal.ReadInt64 (posPtr);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal (posPtr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public override void SetLength (long value)
|
public override void SetLength (long value)
|
||||||
{
|
{
|
||||||
comStream.SetSize (value);
|
lock (_sync)
|
||||||
|
{
|
||||||
|
comStream.SetSize (value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
~ComStreamWrapper () { comStream = null;}
|
~ComStreamWrapper () { comStream = null;}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ namespace PriFileFormat
|
|||||||
return priFile;
|
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);
|
ComStreamWrapper csw = new ComStreamWrapper (stream);
|
||||||
|
output = csw;
|
||||||
PriFile priFile = new PriFile ();
|
PriFile priFile = new PriFile ();
|
||||||
priFile.ParseInternal (csw, true);
|
priFile.ParseInternal (csw, true);
|
||||||
return priFile;
|
return priFile;
|
||||||
|
|||||||
BIN
PriFormat.zip
Normal file
BIN
PriFormat.zip
Normal file
Binary file not shown.
22
PriFormat/ByteSpan.cs
Normal file
22
PriFormat/ByteSpan.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
PriFormat/DataItemSection.cs
Normal file
82
PriFormat/DataItemSection.cs
Normal 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
819
PriFormat/Datas.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
243
PriFormat/DecisionInfoSection.cs
Normal file
243
PriFormat/DecisionInfoSection.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
347
PriFormat/HierarchicalSchemaSection.cs
Normal file
347
PriFormat/HierarchicalSchemaSection.cs
Normal 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
63
PriFormat/Polyfill.cs
Normal 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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
139
PriFormat/PriDescriptorSection.cs
Normal file
139
PriFormat/PriDescriptorSection.cs
Normal 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
144
PriFormat/PriFile.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
PriFormat/PriFormat.csproj
Normal file
69
PriFormat/PriFormat.csproj
Normal 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
817
PriFormat/PriReader.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
36
PriFormat/Properties/AssemblyInfo.cs
Normal file
36
PriFormat/Properties/AssemblyInfo.cs
Normal 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")]
|
||||||
248
PriFormat/ReferencedFileSection.cs
Normal file
248
PriFormat/ReferencedFileSection.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
414
PriFormat/ResourceMapSection.cs
Normal file
414
PriFormat/ResourceMapSection.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
210
PriFormat/ReverseMapSection.cs
Normal file
210
PriFormat/ReverseMapSection.cs
Normal 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
126
PriFormat/Section.cs
Normal 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
171
PriFormat/StreamHelper.cs
Normal 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
110
PriFormat/SubStream.cs
Normal 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
35
PriFormat/TocEntry.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
PriFormat/UnknownSection.cs
Normal file
28
PriFormat/UnknownSection.cs
Normal 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 ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
WAShell/WebAppForm.Designer.cs
generated
1
WAShell/WebAppForm.Designer.cs
generated
@@ -40,6 +40,7 @@
|
|||||||
this.webui.Size = new System.Drawing.Size(661, 416);
|
this.webui.Size = new System.Drawing.Size(661, 416);
|
||||||
this.webui.TabIndex = 0;
|
this.webui.TabIndex = 0;
|
||||||
this.webui.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webui_DocumentCompleted);
|
this.webui.DocumentCompleted += new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(this.webui_DocumentCompleted);
|
||||||
|
this.webui.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.webui_PreviewKeyDown);
|
||||||
//
|
//
|
||||||
// WebAppForm
|
// WebAppForm
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -97,5 +97,9 @@ namespace WAShell
|
|||||||
{
|
{
|
||||||
webui.ObjectForScripting = null;
|
webui.ObjectForScripting = null;
|
||||||
}
|
}
|
||||||
|
private void webui_PreviewKeyDown (object sender, PreviewKeyDownEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.KeyCode == Keys.F5) e.IsInputKey = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@
|
|||||||
</PrecompiledHeader>
|
</PrecompiledHeader>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
<Optimization>MaxSpeed</Optimization>
|
<Optimization>MaxSpeed</Optimization>
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
|||||||
160
dlltest/main.cpp
160
dlltest/main.cpp
@@ -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)>;
|
using cbfunc = std::function <void (int progress)>;
|
||||||
void ProgressCallback (DWORD dwProgress, void *pCustom)
|
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";
|
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\\FinanceApp_3.0.4.336_x86.appx";
|
||||||
//pkgPathStr = L"F:\\BaiduNetdiskDownload\\Collection4\\Microsoft.BingFinance_2015.709.2014.2069_neutral_~_8wekyb3d8bbwe.appxbundle";
|
//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);
|
if (pkgPathStr.empty ()) std::getline (std::wcin, pkgPathStr);
|
||||||
pkgPathStr.erase (
|
pkgPathStr.erase (
|
||||||
std::remove (pkgPathStr.begin (), pkgPathStr.end (), L'\"'),
|
std::remove (pkgPathStr.begin (), pkgPathStr.end (), L'\"'),
|
||||||
pkgPathStr.end ()
|
pkgPathStr.end ()
|
||||||
);
|
);
|
||||||
read_package (pkgPathStr);
|
//read_package (pkgPathStr);
|
||||||
|
read_manifest (pkgPathStr);
|
||||||
system ("pause");
|
system ("pause");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1754,3 +1754,44 @@ BOOL GetManifestPrerequisite (_In_ HPKGMANIFESTREAD hReader, _In_ LPCWSTR lpName
|
|||||||
}
|
}
|
||||||
return FALSE;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -435,6 +435,16 @@ extern "C"
|
|||||||
// 返回 TRUE 表示存在该前置条件。
|
// 返回 TRUE 表示存在该前置条件。
|
||||||
PKGREAD_API BOOL GetManifestPrerequisite (_In_ HPKGMANIFESTREAD hReader, _In_ LPCWSTR lpName, _Outptr_ VERSION *pVerRet);
|
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_
|
#ifdef _DEFAULT_INIT_VALUE_
|
||||||
#undef _DEFAULT_INIT_VALUE_
|
#undef _DEFAULT_INIT_VALUE_
|
||||||
#endif
|
#endif
|
||||||
@@ -589,6 +599,8 @@ class package_reader
|
|||||||
std::sort (prifilestreams.begin (), prifilestreams.end ());
|
std::sort (prifilestreams.begin (), prifilestreams.end ());
|
||||||
auto last = std::unique (prifilestreams.begin (), prifilestreams.end ());
|
auto last = std::unique (prifilestreams.begin (), prifilestreams.end ());
|
||||||
prifilestreams.erase (last, prifilestreams.end ());
|
prifilestreams.erase (last, prifilestreams.end ());
|
||||||
|
bool lastvalue = resswitch;
|
||||||
|
resswitch = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector <std::wstring> resnames;
|
std::vector <std::wstring> resnames;
|
||||||
@@ -619,6 +631,7 @@ class package_reader
|
|||||||
pribundlereader.add_search (resnames);
|
pribundlereader.add_search (resnames);
|
||||||
}
|
}
|
||||||
catch (const std::exception &e) {}
|
catch (const std::exception &e) {}
|
||||||
|
resswitch = lastvalue;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
typedef struct deconstr
|
typedef struct deconstr
|
||||||
@@ -1301,6 +1314,702 @@ class package_reader
|
|||||||
#endif
|
#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
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -48,16 +48,11 @@ ref class PriFileInst
|
|||||||
PriFile ^inst = nullptr;
|
PriFile ^inst = nullptr;
|
||||||
OpenType opentype = OpenType::Unknown;
|
OpenType opentype = OpenType::Unknown;
|
||||||
IStream *isptr = nullptr;
|
IStream *isptr = nullptr;
|
||||||
System::IO::FileStream ^fsptr = nullptr;
|
System::IO::Stream ^fsptr = nullptr;
|
||||||
operator PriFile ^ () { return inst; }
|
operator PriFile ^ () { return inst; }
|
||||||
operator IStream * () { return isptr; }
|
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)); }
|
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)
|
size_t Seek (int64_t offset, System::IO::SeekOrigin origin)
|
||||||
{
|
{
|
||||||
if (isptr)
|
if (isptr)
|
||||||
@@ -317,11 +312,13 @@ PCSPRIFILE CreatePriFileInstanceFromStream (PCOISTREAM pStream)
|
|||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
if (pStream) hr = ((IStream *)pStream)->Seek (LARGE_INTEGER {}, STREAM_SEEK_SET, nullptr);
|
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 ();
|
PriFileInst ^inst = gcnew PriFileInst ();
|
||||||
inst->inst = pri;
|
inst->inst = pri;
|
||||||
inst->opentype = OpenType::IStream;
|
inst->opentype = OpenType::IStream;
|
||||||
inst->isptr = reinterpret_cast <IStream *> (pStream);
|
inst->isptr = reinterpret_cast <IStream *> (pStream);
|
||||||
|
inst->fsptr = stream;
|
||||||
auto handle = System::Runtime::InteropServices::GCHandle::Alloc (inst);
|
auto handle = System::Runtime::InteropServices::GCHandle::Alloc (inst);
|
||||||
IntPtr token = System::Runtime::InteropServices::GCHandle::ToIntPtr (handle);
|
IntPtr token = System::Runtime::InteropServices::GCHandle::ToIntPtr (handle);
|
||||||
return reinterpret_cast <PCSPRIFILE> (token.ToPointer ());
|
return reinterpret_cast <PCSPRIFILE> (token.ToPointer ());
|
||||||
|
|||||||
30
priread (never using)/ReadMe.txt
Normal file
30
priread (never using)/ReadMe.txt
Normal 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:”注释来指示应添加或自定义的源代码部分。
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
19
priread (never using)/dllmain.cpp
Normal file
19
priread (never using)/dllmain.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
163
priread (never using)/localeex.h
Normal file
163
priread (never using)/localeex.h
Normal 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); }
|
||||||
456
priread (never using)/nstring.h
Normal file
456
priread (never using)/nstring.h
Normal 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;
|
||||||
|
}
|
||||||
2671
priread (never using)/prifile.h
Normal file
2671
priread (never using)/prifile.h
Normal file
File diff suppressed because it is too large
Load Diff
22
priread (never using)/priread.cpp
Normal file
22
priread (never using)/priread.cpp
Normal 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;
|
||||||
|
}
|
||||||
22
priread (never using)/priread.h
Normal file
22
priread (never using)/priread.h
Normal 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);
|
||||||
182
priread (never using)/priread.vcxproj
Normal file
182
priread (never using)/priread.vcxproj
Normal 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>
|
||||||
54
priread (never using)/priread.vcxproj.filters
Normal file
54
priread (never using)/priread.vcxproj.filters
Normal 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>
|
||||||
8
priread (never using)/stdafx.cpp
Normal file
8
priread (never using)/stdafx.cpp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// stdafx.cpp : 只包括标准包含文件的源文件
|
||||||
|
// priread.pch 将作为预编译头
|
||||||
|
// stdafx.obj 将包含预编译类型信息
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
// TODO: 在 STDAFX.H 中引用任何所需的附加头文件,
|
||||||
|
//而不是在此文件中引用
|
||||||
20
priread (never using)/stdafx.h
Normal file
20
priread (never using)/stdafx.h
Normal 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: 在此处引用程序需要的其他头文件
|
||||||
8
priread (never using)/targetver.h
Normal file
8
priread (never using)/targetver.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
|
||||||
|
|
||||||
|
// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
|
||||||
|
// 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
|
||||||
|
|
||||||
|
#include <SDKDDKVer.h>
|
||||||
40
priread (never using)/themeinfo.h
Normal file
40
priread (never using)/themeinfo.h
Normal 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;
|
||||||
|
}
|
||||||
@@ -7,10 +7,16 @@
|
|||||||
try {
|
try {
|
||||||
if (swJson) ret = JSON.parse(swJson);
|
if (swJson) ret = JSON.parse(swJson);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
if (ret && typeof ret.jsontext !== "undefined") {
|
||||||
|
ret["json"] = JSON.parse(ret.jsontext);
|
||||||
|
delete ret.jsontext;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
if (callback) callback(ret);
|
if (callback) callback(ret);
|
||||||
}
|
}
|
||||||
global.Package = {
|
global.Package = {
|
||||||
reader: function(pkgPath) { external.Package.reader(pkgPath); },
|
reader: function(pkgPath) { return external.Package.reader(pkgPath); },
|
||||||
manager: {
|
manager: {
|
||||||
add: function(swPkgPath, uOptions) {
|
add: function(swPkgPath, uOptions) {
|
||||||
return new Promise(function(resolve, reject, progress) {
|
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);
|
})(this);
|
||||||
Reference in New Issue
Block a user