You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
2.9 KiB
99 lines
2.9 KiB
System.Resources.ResourceReader is unsuitable for deserializing resources in the ILSpy context,
|
|
because it tries to deserialize custom resource types, which fails if the types are in a non-GAC assembly.
|
|
|
|
So we are instead using our own "class ResourcesFile", which is based on the
|
|
.NET Core ResourceReader implementation.
|
|
|
|
struct ResourcesFileFormat {
|
|
int32 magicNum; [check == ResourceManager.MagicNumber]
|
|
|
|
// ResourceManager header:
|
|
int32 resMgrHeaderVersion; [check >= 0]
|
|
int32 numBytesToSkip; [check >= 0]
|
|
if (resMgrHeaderVersion <= 1) {
|
|
string readerType;
|
|
string resourceSetType;
|
|
} else {
|
|
byte _[numBytesToSkip];
|
|
}
|
|
|
|
// RuntimeResourceSet header:
|
|
int32 version; [check in (1, 2)]
|
|
int32 numResources; [check >=0]
|
|
int32 numTypes; [check >=0]
|
|
string typeName[numTypes];
|
|
.align 8;
|
|
int32 nameHashes[numResources];
|
|
int32 namePositions[numResources]; [check >= 0]
|
|
int32 dataSectionOffset; [check >= current position in file]
|
|
byte remainderOfFile[];
|
|
}
|
|
|
|
// normal strings in this file format are stored as:
|
|
struct string {
|
|
compressedint len;
|
|
byte value[len]; // interpret as UTF-8
|
|
}
|
|
|
|
// NameEntry #i is stored starting at remainderOfFile[namePositions[i]]
|
|
// (that is, namePositions is interpreted relative to the start of the remainderOfFile array)
|
|
struct NameEntry {
|
|
compressedint len;
|
|
byte name[len]; // interpret as UTF-16
|
|
int32 dataOffset; [check >= 0]
|
|
}
|
|
|
|
// Found at position ResourcesFileFormat.dataSectionOffset+NameEntry.dataOffset in the file.
|
|
struct ValueEntry {
|
|
if (version == 1) {
|
|
compressedint typeIndex;
|
|
if (typeIndex == -1) {
|
|
// no value stored; value is implicitly null
|
|
} else {
|
|
switch (typeName[typeIndex]) {
|
|
case string:
|
|
case int, uint, long, ulong, sbyte, byte, short, ushort:
|
|
case float:
|
|
case double:
|
|
T value; // value directly stored
|
|
case DateTime:
|
|
int64 value; // new DateTime(_store.ReadInt64())
|
|
case TimeSpan:
|
|
int64 value; // new TimeSpan(_store.ReadInt64())
|
|
case decimal:
|
|
int32 value[4];
|
|
default:
|
|
byte data[...]; // BinaryFormatter-serialized data using typeName[typeIndex]
|
|
}
|
|
}
|
|
} else if (version == 2) {
|
|
compressedint typeCode;
|
|
// note: unlike v1, no lookup into the typeName array!
|
|
switch (typeCode) {
|
|
case null:
|
|
// no value stored; value is implicitly null
|
|
case string:
|
|
case bool:
|
|
case int, uint, long, ulong, sbyte, byte, short, ushort:
|
|
T value;
|
|
case char:
|
|
uint16 value;
|
|
case float:
|
|
case double:
|
|
case decimal:
|
|
T value;
|
|
case DateTime:
|
|
int64 value; // DateTime.FromBinary(_store.ReadInt64())
|
|
case TimeSpan:
|
|
int64 value; // new TimeSpan(_store.ReadInt64())
|
|
case ResourceTypeCode.ByteArray:
|
|
int32 len;
|
|
byte value[len];
|
|
case ResourceTypeCode.Stream:
|
|
int32 len;
|
|
byte value[len];
|
|
case >= ResourceTypeCode.StartOfUserTypes:
|
|
byte data[...]; // BinaryFormatter-serialized data using typeName[typeCode - ResourceTypeCode.StartOfUserTypes]
|
|
}
|
|
}
|
|
}
|