mirror of
https://github.com/kwsch/NHSE.git
synced 2026-03-21 17:24:30 -05:00
Update to .NET 10 (#683)
This commit is contained in:
parent
63bd5f643c
commit
fa3f0e3cc8
115
.editorconfig
Normal file
115
.editorconfig
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
root = true
|
||||
|
||||
# All Files
|
||||
[*]
|
||||
charset = utf-8-bom
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# XML Project Files
|
||||
[*.{csproj,slnx,props}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Code Files
|
||||
[*.cs]
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
end_of_line = crlf
|
||||
csharp_prefer_braces = when_multiline:warning
|
||||
dotnet_diagnostic.IDE0047.severity = none
|
||||
dotnet_diagnostic.IDE0048.severity = none
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggest
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggest
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggest
|
||||
dotnet_style_parentheses_in_other_operators = always_for_clarity:suggest
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_style_namespace_declarations = block_scoped:silent
|
||||
csharp_style_prefer_method_group_conversion = true:silent
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
csharp_style_prefer_primary_constructors = true:suggestion
|
||||
csharp_prefer_system_threading_lock = true:suggestion
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
dotnet_diagnostic.WFO1000.severity = none
|
||||
|
||||
[*.{cs,vb}]
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
|
||||
[*.{cs,vb}]
|
||||
#### Naming styles ####
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||
|
||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
|
||||
# IDE0130: Namespace does not match folder structure
|
||||
dotnet_diagnostic.IDE0130.severity = none
|
||||
5
.github/README-es.md
vendored
5
.github/README-es.md
vendored
|
|
@ -11,11 +11,6 @@ Editor de partidas guardadas para Animal Crossing: New Horizons
|
|||
Permite editar partidas guardadas extraídas del Nintendo Switch.
|
||||
* Debes extraer las partidas guardadas de la consola tu mismo, este programa no las extrae por ti.
|
||||
|
||||
## Véase también
|
||||
|
||||
[MyHorizons](https://github.com/Cuyler36/MyHorizons) de [Cuyler36](https://github.com/Cuyler36/)
|
||||
* Algunas partes del código fueron adaptadas del proyecto MyHorizons de Cuyler36 (arriba)
|
||||
|
||||
## Otro
|
||||
|
||||
Dirígete a la [Wiki](https://github.com/kwsch/NHSE/wiki) para más información.
|
||||
|
|
|
|||
5
.github/README-fr.md
vendored
5
.github/README-fr.md
vendored
|
|
@ -10,11 +10,6 @@ NHSE
|
|||
Modifiez les données de sauvegarde extraites de votre Nintendo Switch.
|
||||
* Veuillez préparer vos propres données de sauvegarde. Ce programme n'extrait pas les données de sauvegarde de la console.
|
||||
|
||||
## Voir également
|
||||
|
||||
[MyHorizons](https://github.com/Cuyler36/MyHorizons) par [Cuyler36](https://github.com/Cuyler36/)
|
||||
* Une partie du code est fortement adaptée du projet de Cuyler36 ci-dessus.
|
||||
|
||||
## Autre
|
||||
|
||||
Consultez le [Wiki](https://github.com/kwsch/NHSE/wiki) pour plus d'informations.
|
||||
|
|
|
|||
5
.github/README-it.md
vendored
5
.github/README-it.md
vendored
|
|
@ -11,11 +11,6 @@ Modificatore di salvataggi per Animal Crossing: New Horizons
|
|||
Puoi modificare i salvataggi esportati dalla tua Nintendo Switch.
|
||||
* Esporta i tuoi salvataggi; Questo programma non può esportare direttamente i salvataggi dalla tua console.
|
||||
|
||||
## Guarda Anche
|
||||
|
||||
[MyHorizons](https://github.com/Cuyler36/MyHorizons) di [Cuyler36](https://github.com/Cuyler36/)
|
||||
* Alcune porzioni del codice sono completamente adattate dal progetto di Cuyler36.
|
||||
|
||||
## Altro
|
||||
|
||||
Per ulteriori informazioni, consultare la nostra [Wiki](https://github.com/kwsch/NHSE/wiki) (in inglese).
|
||||
|
|
|
|||
5
.github/README-jp.md
vendored
5
.github/README-jp.md
vendored
|
|
@ -11,11 +11,6 @@ NHSE
|
|||
Nintendo Switch から抽出したセーブデータを編集します。
|
||||
* 自分のセーブデータを用意してください。このプログラムはコンソールからセーブデータを抽出しません。
|
||||
|
||||
## 参考
|
||||
|
||||
[MyHorizons](https://github.com/Cuyler36/MyHorizons) by [Cuyler36](https://github.com/Cuyler36/)
|
||||
* コードの一部は上記の Cuyler36 のプロジェクトから大いに翻案されています。
|
||||
|
||||
## その他
|
||||
|
||||
詳しくは [Wiki](https://github.com/kwsch/NHSE/wiki) を参照してください。
|
||||
|
|
|
|||
4
.github/README-zh-CN.md
vendored
4
.github/README-zh-CN.md
vendored
|
|
@ -10,10 +10,6 @@ NHSE
|
|||
|
||||
可以编辑你从Nintendo Switch中导出的存档。
|
||||
* 请自行解决存档导出问题,本程序并不能直接从你的Switch中导出存档。
|
||||
## 参见
|
||||
|
||||
[MyHorizons](https://github.com/Cuyler36/MyHorizons) by [Cuyler36](https://github.com/Cuyler36/)
|
||||
* 代码的某些部分完全改编自Cuyler36的项目。
|
||||
|
||||
## 其他
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<LangVersion>10</LangVersion>
|
||||
<LangVersion>14</LangVersion>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Product>NHSE</Product>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
using System.Drawing;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class AcreTileColor
|
||||
{
|
||||
public static class AcreTileColor
|
||||
public static readonly byte[] AcreTiles = ResourceUtil.GetBinaryResource("outside.bin");
|
||||
|
||||
public static int GetAcreTileColor(ushort acre, int x, int y)
|
||||
{
|
||||
public static readonly byte[] AcreTiles = ResourceUtil.GetBinaryResource("outside.bin");
|
||||
if (acre > (ushort)OutsideAcre.FldOutNGardenRFront00)
|
||||
return Color.Transparent.ToArgb();
|
||||
var baseOfs = acre * 32 * 32 * 4;
|
||||
|
||||
public static int GetAcreTileColor(ushort acre, int x, int y)
|
||||
{
|
||||
if (acre > (ushort)OutsideAcre.FldOutNGardenRFront00)
|
||||
return Color.Transparent.ToArgb();
|
||||
var baseOfs = acre * 32 * 32 * 4;
|
||||
|
||||
// 64x64
|
||||
var shift = (4 * ((y * 64) + x));
|
||||
var ofs = baseOfs + shift;
|
||||
var tile = AcreTiles[ofs];
|
||||
return CollisionUtil.Dict[tile].ToArgb();
|
||||
}
|
||||
// 64x64
|
||||
var shift = (4 * ((y * 64) + x));
|
||||
var ofs = baseOfs + shift;
|
||||
var tile = AcreTiles[ofs];
|
||||
return CollisionUtil.Dict[tile].ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +1,53 @@
|
|||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class ColorUtil
|
||||
{
|
||||
public static class ColorUtil
|
||||
public static Color GetColor(int index)
|
||||
{
|
||||
public static Color GetColor(int index)
|
||||
{
|
||||
var arr = Colors;
|
||||
if ((uint) index < arr.Length)
|
||||
return arr[index];
|
||||
var arr = Colors;
|
||||
if ((uint) index < arr.Length)
|
||||
return arr[index];
|
||||
|
||||
// loop back and blend with something else
|
||||
index %= arr.Length;
|
||||
var c = arr[index];
|
||||
return Blend(Color.Red, c, 0.2f);
|
||||
}
|
||||
|
||||
private static readonly Color[] Colors = new[]
|
||||
{
|
||||
0xD9D9D9, 0xCCD9E8, 0x7F7F7F, 0xD5D5D5, 0xF7F7F7, 0xCFCFCF, 0xB4B4B4, 0xF1F1F1,
|
||||
0xFFFFFF, 0x7F7F7F, 0x7F7F7F, 0xB6B6B6, 0x7FBBEB, 0xFFFFFF, 0x7FB2E5, 0xF9FBFD,
|
||||
0xDFE6ED, 0x7F7F7F, 0xFFFFF0, 0x7F7F7F, 0xF7F7F7, 0x7F7F7F, 0xE3E3E3, 0xFFFFFF,
|
||||
0xB1B1B1, 0x7F7F7F, 0xFFFFFF, 0xF7FBFF, 0xFCF5EB, 0x7FFFFF, 0xBFFFE9, 0xF7FFFF,
|
||||
0xFAFAED, 0xFFF1E1, 0x7F7F7F, 0xFFF5E6, 0x7F7FFF, 0xC495F0, 0xD29494, 0xEEDBC3,
|
||||
0xAFCECF, 0xBFFF7F, 0xE8B48E, 0xFFBFA7, 0xB1CAF6, 0xFFFBED, 0xED899D, 0x7FFFFF,
|
||||
0x7F7FC5, 0x7FC5C5, 0xDBC285, 0xD4D4D4, 0x7FB17F, 0xDEDBB5, 0xC57FC5, 0xAAB597,
|
||||
0xFFC57F, 0xCC98E5, 0xC57F7F, 0xF4CABC, 0xC7DDC5, 0xA39EC5, 0x97A7A7, 0x7FE6E8,
|
||||
0xC97FE9, 0xFF89C9, 0x7FDFFF, 0xB4B4B4, 0x8EC7FF, 0xD89090, 0xFFFCF7, 0x90C590,
|
||||
0xFF7FFF, 0xEDEDED, 0xFBFBFF, 0xFFEB7F, 0xECD28F, 0xBFBFBF, 0x7FBF7F, 0xD6FF97,
|
||||
0xF7FFF7, 0xFFB4D9, 0xE6ADAD, 0xA57FC0, 0xFFFFF7, 0xF7F2C5, 0xF2F2FC, 0xFFF7FA,
|
||||
0xBDFD7F, 0xFFFCE6, 0xD6EBF2, 0xF7BFBF, 0xEFFFFF, 0xFCFCE8, 0xE9E9E9, 0xC7F6C7,
|
||||
0xFFDAE0, 0xFFCFBC, 0x8FD8D4, 0xC3E6FC, 0xBBC3CC, 0xD7E1EE, 0xFFFFEF, 0x7FFF7F,
|
||||
0x98E698, 0xFCF7F2, 0xFF7FFF, 0xBF7F7F, 0xB2E6D4, 0x7F7FE6, 0xDCAAE9, 0xC9B7ED,
|
||||
0x9DD9B8, 0xBDB3F6, 0x7FFCCC, 0xA3E8E5, 0xE38AC2, 0x8C8CB7, 0xFAFFFC, 0xFFF1F0,
|
||||
0xFFF1DA, 0xFFEED6, 0x7F7FBF, 0xFEFAF2, 0xBFBF7F, 0xB5C691, 0xFFD27F, 0xFFA27F,
|
||||
0xECB7EA, 0xF6F3D4, 0xCBFDCB, 0xD7F6F6, 0xEDB7C9, 0xFFF7EA, 0xFFECDC, 0xE6C29F,
|
||||
0xFFDFE5, 0xEECFEE, 0xD7EFF2, 0xBF7FBF, 0xFF7F7F, 0xDDC7C7, 0xA0B4F0, 0xC5A289,
|
||||
0xFCBFB8, 0xF9D1AF, 0x96C5AB, 0xFFFAF6, 0xCFA896, 0xDFDFDF, 0xC3E6F5, 0xB4ACE6,
|
||||
0xB7BFC7, 0xFFFCFC, 0x7FFFBF, 0xA2C0D9, 0xE8D9C5, 0x7FBFBF, 0xEBDFEB, 0xFFB1A3,
|
||||
0x9FEFE7, 0xF6C0F6, 0xFAEED9, 0xFFFFFF, 0xFAFAFA, 0xFFFF7F, 0xCCE698, 0xF7F7F7,
|
||||
0xFFFFFF, 0xCFCFCF, 0xDCE8F4, 0xEBF1F8, 0xF7F7F7, 0x99CCFF,
|
||||
}.Select(z => Color.FromArgb(z | -0x1000000)).ToArray();
|
||||
|
||||
public static Color Blend(Color color, Color backColor, double amount)
|
||||
{
|
||||
byte r = (byte)((color.R * amount) + (backColor.R * (1 - amount)));
|
||||
byte g = (byte)((color.G * amount) + (backColor.G * (1 - amount)));
|
||||
byte b = (byte)((color.B * amount) + (backColor.B * (1 - amount)));
|
||||
return Color.FromArgb(r, g, b);
|
||||
}
|
||||
// loop back and blend with something else
|
||||
index %= arr.Length;
|
||||
var c = arr[index];
|
||||
return Blend(Color.Red, c, 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Color[] Colors = new[]
|
||||
{
|
||||
0xD9D9D9, 0xCCD9E8, 0x7F7F7F, 0xD5D5D5, 0xF7F7F7, 0xCFCFCF, 0xB4B4B4, 0xF1F1F1,
|
||||
0xFFFFFF, 0x7F7F7F, 0x7F7F7F, 0xB6B6B6, 0x7FBBEB, 0xFFFFFF, 0x7FB2E5, 0xF9FBFD,
|
||||
0xDFE6ED, 0x7F7F7F, 0xFFFFF0, 0x7F7F7F, 0xF7F7F7, 0x7F7F7F, 0xE3E3E3, 0xFFFFFF,
|
||||
0xB1B1B1, 0x7F7F7F, 0xFFFFFF, 0xF7FBFF, 0xFCF5EB, 0x7FFFFF, 0xBFFFE9, 0xF7FFFF,
|
||||
0xFAFAED, 0xFFF1E1, 0x7F7F7F, 0xFFF5E6, 0x7F7FFF, 0xC495F0, 0xD29494, 0xEEDBC3,
|
||||
0xAFCECF, 0xBFFF7F, 0xE8B48E, 0xFFBFA7, 0xB1CAF6, 0xFFFBED, 0xED899D, 0x7FFFFF,
|
||||
0x7F7FC5, 0x7FC5C5, 0xDBC285, 0xD4D4D4, 0x7FB17F, 0xDEDBB5, 0xC57FC5, 0xAAB597,
|
||||
0xFFC57F, 0xCC98E5, 0xC57F7F, 0xF4CABC, 0xC7DDC5, 0xA39EC5, 0x97A7A7, 0x7FE6E8,
|
||||
0xC97FE9, 0xFF89C9, 0x7FDFFF, 0xB4B4B4, 0x8EC7FF, 0xD89090, 0xFFFCF7, 0x90C590,
|
||||
0xFF7FFF, 0xEDEDED, 0xFBFBFF, 0xFFEB7F, 0xECD28F, 0xBFBFBF, 0x7FBF7F, 0xD6FF97,
|
||||
0xF7FFF7, 0xFFB4D9, 0xE6ADAD, 0xA57FC0, 0xFFFFF7, 0xF7F2C5, 0xF2F2FC, 0xFFF7FA,
|
||||
0xBDFD7F, 0xFFFCE6, 0xD6EBF2, 0xF7BFBF, 0xEFFFFF, 0xFCFCE8, 0xE9E9E9, 0xC7F6C7,
|
||||
0xFFDAE0, 0xFFCFBC, 0x8FD8D4, 0xC3E6FC, 0xBBC3CC, 0xD7E1EE, 0xFFFFEF, 0x7FFF7F,
|
||||
0x98E698, 0xFCF7F2, 0xFF7FFF, 0xBF7F7F, 0xB2E6D4, 0x7F7FE6, 0xDCAAE9, 0xC9B7ED,
|
||||
0x9DD9B8, 0xBDB3F6, 0x7FFCCC, 0xA3E8E5, 0xE38AC2, 0x8C8CB7, 0xFAFFFC, 0xFFF1F0,
|
||||
0xFFF1DA, 0xFFEED6, 0x7F7FBF, 0xFEFAF2, 0xBFBF7F, 0xB5C691, 0xFFD27F, 0xFFA27F,
|
||||
0xECB7EA, 0xF6F3D4, 0xCBFDCB, 0xD7F6F6, 0xEDB7C9, 0xFFF7EA, 0xFFECDC, 0xE6C29F,
|
||||
0xFFDFE5, 0xEECFEE, 0xD7EFF2, 0xBF7FBF, 0xFF7F7F, 0xDDC7C7, 0xA0B4F0, 0xC5A289,
|
||||
0xFCBFB8, 0xF9D1AF, 0x96C5AB, 0xFFFAF6, 0xCFA896, 0xDFDFDF, 0xC3E6F5, 0xB4ACE6,
|
||||
0xB7BFC7, 0xFFFCFC, 0x7FFFBF, 0xA2C0D9, 0xE8D9C5, 0x7FBFBF, 0xEBDFEB, 0xFFB1A3,
|
||||
0x9FEFE7, 0xF6C0F6, 0xFAEED9, 0xFFFFFF, 0xFAFAFA, 0xFFFF7F, 0xCCE698, 0xF7F7F7,
|
||||
0xFFFFFF, 0xCFCFCF, 0xDCE8F4, 0xEBF1F8, 0xF7F7F7, 0x99CCFF,
|
||||
}.Select(z => Color.FromArgb(z | -0x1000000)).ToArray();
|
||||
|
||||
public static Color Blend(Color color, Color backColor, double amount)
|
||||
{
|
||||
byte r = (byte)((color.R * amount) + (backColor.R * (1 - amount)));
|
||||
byte g = (byte)((color.G * amount) + (backColor.G * (1 - amount)));
|
||||
byte b = (byte)((color.B * amount) + (backColor.B * (1 - amount)));
|
||||
return Color.FromArgb(r, g, b);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +1,78 @@
|
|||
using System.Drawing;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class FieldItemColor
|
||||
{
|
||||
public static class FieldItemColor
|
||||
public static Color GetItemColor(Item item)
|
||||
{
|
||||
public static Color GetItemColor(Item item)
|
||||
{
|
||||
if (item.DisplayItemId >= Item.FieldItemMin)
|
||||
return GetItemColor60000(item);
|
||||
var kind = ItemInfo.GetItemKind(item);
|
||||
return ColorUtil.GetColor((int)kind);
|
||||
}
|
||||
|
||||
private static Color GetItemColor60000(Item item)
|
||||
{
|
||||
var id = item.DisplayItemId;
|
||||
if (id == Item.NONE)
|
||||
return Color.Transparent;
|
||||
|
||||
if (!FieldItemList.Items.TryGetValue(id, out var def))
|
||||
return Color.DarkGreen;
|
||||
|
||||
var kind = def.Kind;
|
||||
if (kind.IsTree())
|
||||
return GetTreeColor(id);
|
||||
if (kind.IsFlower())
|
||||
return Color.HotPink;
|
||||
if (kind.IsWeed())
|
||||
return Color.DarkOliveGreen;
|
||||
if (kind.IsFence())
|
||||
return Color.LightCoral;
|
||||
if (kind == FieldItemKind.UnitIconHole)
|
||||
return Color.Black;
|
||||
if (kind.IsBush())
|
||||
return Color.LightGreen;
|
||||
if (kind.IsStone())
|
||||
return Color.LightGray;
|
||||
|
||||
return Color.DarkGreen; // shouldn't reach here, but ok
|
||||
}
|
||||
|
||||
private static Color GetTreeColor(ushort id)
|
||||
{
|
||||
if (0xEC9C <= id && id <= 0xECA0) // money tree
|
||||
return Color.Gold;
|
||||
|
||||
return id switch
|
||||
{
|
||||
// Fruit
|
||||
0xEA61 => Color.Red, // "PltTreeApple"
|
||||
0xEA62 => Color.Orange, // "PltTreeOrange"
|
||||
0xEAC8 => Color.Lime, // "PltTreePear"
|
||||
0xEAC9 => Color.DarkRed, // "PltTreeCherry"
|
||||
0xEACA => Color.PeachPuff, // "PltTreePeach"
|
||||
|
||||
// Cedar
|
||||
0xEA69 => Color.SaddleBrown, // "PltTreeCedar4"
|
||||
0xEAB6 => Color.SaddleBrown, // "PltTreeCedar2"
|
||||
0xEAB7 => Color.SaddleBrown, // "PltTreeCedar1"
|
||||
0xEAB8 => Color.SaddleBrown, // "PltTreeCedar3"
|
||||
|
||||
// Palm
|
||||
0xEA77 => Color.LightGoldenrodYellow, // "PltTreePalm4"
|
||||
0xEAC0 => Color.LightGoldenrodYellow, // "PltTreePalm2"
|
||||
0xEAC1 => Color.LightGoldenrodYellow, // "PltTreePalm1"
|
||||
0xEAC2 => Color.LightGoldenrodYellow, // "PltTreePalm3"
|
||||
|
||||
0xEA76 => Color.MediumSeaGreen, // "PltTreeBamboo4"
|
||||
0xEAC4 => Color.MediumSeaGreen, // "PltTreeBamboo0"
|
||||
0xEAC5 => Color.MediumSeaGreen, // "PltTreeBamboo2"
|
||||
0xEAC6 => Color.MediumSeaGreen, // "PltTreeBamboo1"
|
||||
0xEAC7 => Color.MediumSeaGreen, // "PltTreeBamboo3"
|
||||
|
||||
_ => Color.SandyBrown,
|
||||
};
|
||||
}
|
||||
if (item.DisplayItemId >= Item.FieldItemMin)
|
||||
return GetItemColor60000(item);
|
||||
var kind = ItemInfo.GetItemKind(item);
|
||||
return ColorUtil.GetColor((int)kind);
|
||||
}
|
||||
}
|
||||
|
||||
private static Color GetItemColor60000(Item item)
|
||||
{
|
||||
var id = item.DisplayItemId;
|
||||
if (id == Item.NONE)
|
||||
return Color.Transparent;
|
||||
|
||||
if (!FieldItemList.Items.TryGetValue(id, out var def))
|
||||
return Color.DarkGreen;
|
||||
|
||||
var kind = def.Kind;
|
||||
if (kind.IsTree)
|
||||
return GetTreeColor(id);
|
||||
if (kind.IsFlower)
|
||||
return Color.HotPink;
|
||||
if (kind.IsWeed)
|
||||
return Color.DarkOliveGreen;
|
||||
if (kind.IsFence)
|
||||
return Color.LightCoral;
|
||||
if (kind == FieldItemKind.UnitIconHole)
|
||||
return Color.Black;
|
||||
if (kind.IsBush)
|
||||
return Color.LightGreen;
|
||||
if (kind.IsStone)
|
||||
return Color.LightGray;
|
||||
|
||||
return Color.DarkGreen; // shouldn't reach here, but ok
|
||||
}
|
||||
|
||||
private static Color GetTreeColor(ushort id)
|
||||
{
|
||||
if (id is >= 0xEC9C and <= 0xECA0) // money tree
|
||||
return Color.Gold;
|
||||
|
||||
return id switch
|
||||
{
|
||||
// Fruit
|
||||
0xEA61 => Color.Red, // "PltTreeApple"
|
||||
0xEA62 => Color.Orange, // "PltTreeOrange"
|
||||
0xEAC8 => Color.Lime, // "PltTreePear"
|
||||
0xEAC9 => Color.DarkRed, // "PltTreeCherry"
|
||||
0xEACA => Color.PeachPuff, // "PltTreePeach"
|
||||
|
||||
// Cedar
|
||||
0xEA69 => Color.SaddleBrown, // "PltTreeCedar4"
|
||||
0xEAB6 => Color.SaddleBrown, // "PltTreeCedar2"
|
||||
0xEAB7 => Color.SaddleBrown, // "PltTreeCedar1"
|
||||
0xEAB8 => Color.SaddleBrown, // "PltTreeCedar3"
|
||||
|
||||
// Palm
|
||||
0xEA77 => Color.LightGoldenrodYellow, // "PltTreePalm4"
|
||||
0xEAC0 => Color.LightGoldenrodYellow, // "PltTreePalm2"
|
||||
0xEAC1 => Color.LightGoldenrodYellow, // "PltTreePalm1"
|
||||
0xEAC2 => Color.LightGoldenrodYellow, // "PltTreePalm3"
|
||||
|
||||
0xEA76 => Color.MediumSeaGreen, // "PltTreeBamboo4"
|
||||
0xEAC4 => Color.MediumSeaGreen, // "PltTreeBamboo0"
|
||||
0xEAC5 => Color.MediumSeaGreen, // "PltTreeBamboo2"
|
||||
0xEAC6 => Color.MediumSeaGreen, // "PltTreeBamboo1"
|
||||
0xEAC7 => Color.MediumSeaGreen, // "PltTreeBamboo3"
|
||||
|
||||
_ => Color.SandyBrown,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,26 @@
|
|||
using System.Drawing;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
public static class ItemColor
|
||||
{
|
||||
public static Color GetItemColor(Item item)
|
||||
{
|
||||
if (item.ItemId == Item.NONE)
|
||||
return Color.Transparent;
|
||||
var kind = ItemInfo.GetItemKind(item);
|
||||
if (kind == ItemKind.Unknown)
|
||||
return Color.LimeGreen;
|
||||
return ColorUtil.GetColor((int)kind);
|
||||
}
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static Color GetItemColor(ushort item)
|
||||
{
|
||||
if (item == Item.NONE)
|
||||
return Color.Transparent;
|
||||
var kind = ItemInfo.GetItemKind(item);
|
||||
if (kind == ItemKind.Unknown)
|
||||
return Color.LimeGreen;
|
||||
return ColorUtil.GetColor((int)kind);
|
||||
}
|
||||
public static class ItemColor
|
||||
{
|
||||
public static Color GetItemColor(Item item)
|
||||
{
|
||||
if (item.ItemId == Item.NONE)
|
||||
return Color.Transparent;
|
||||
var kind = ItemInfo.GetItemKind(item);
|
||||
if (kind == ItemKind.Unknown)
|
||||
return Color.LimeGreen;
|
||||
return ColorUtil.GetColor((int)kind);
|
||||
}
|
||||
}
|
||||
|
||||
public static Color GetItemColor(ushort item)
|
||||
{
|
||||
if (item == Item.NONE)
|
||||
return Color.Transparent;
|
||||
var kind = ItemInfo.GetItemKind(item);
|
||||
if (kind == ItemKind.Unknown)
|
||||
return Color.LimeGreen;
|
||||
return ColorUtil.GetColor((int)kind);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,240 +3,237 @@
|
|||
using static NHSE.Core.TerrainUnitModel;
|
||||
using static NHSE.Core.LandAngles;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class TerrainTileColor
|
||||
{
|
||||
public static class TerrainTileColor
|
||||
private static readonly Color River = Color.FromArgb(128, 215, 195);
|
||||
private static readonly Color Grass = Color.ForestGreen;
|
||||
|
||||
public static Color GetTileColor(TerrainTile tile, int relativeX, int relativeY)
|
||||
{
|
||||
private static readonly Color River = Color.FromArgb(128, 215, 195);
|
||||
private static readonly Color Grass = Color.ForestGreen;
|
||||
if (tile.UnitModelRoad.IsRoad)
|
||||
return GetRoadColor(tile.UnitModelRoad);
|
||||
var baseColor = GetTileDefaultColor(tile.UnitModel, tile.LandMakingAngle, relativeX, relativeY);
|
||||
if (tile.Elevation == 0)
|
||||
return baseColor;
|
||||
|
||||
public static Color GetTileColor(TerrainTile tile, int relativeX, int relativeY)
|
||||
{
|
||||
if (tile.UnitModelRoad.IsRoad())
|
||||
return GetRoadColor(tile.UnitModelRoad);
|
||||
var baseColor = GetTileDefaultColor(tile.UnitModel, tile.LandMakingAngle, relativeX, relativeY);
|
||||
if (tile.Elevation == 0)
|
||||
return baseColor;
|
||||
|
||||
return ColorUtil.Blend(baseColor, Color.White, 1.4d / (tile.Elevation + 1));
|
||||
}
|
||||
|
||||
private static Color GetRoadColor(TerrainUnitModel mdl)
|
||||
{
|
||||
if (mdl.IsRoadBrick())
|
||||
return Color.Firebrick;
|
||||
if (mdl.IsRoadDarkSoil())
|
||||
return Color.SaddleBrown;
|
||||
if (mdl.IsRoadSoil())
|
||||
return Color.Peru;
|
||||
if (mdl.IsRoadStone())
|
||||
return Color.DarkGray;
|
||||
if (mdl.IsRoadPattern())
|
||||
return Color.Ivory;
|
||||
if (mdl.IsRoadTile())
|
||||
return Color.SteelBlue;
|
||||
if (mdl.IsRoadSand())
|
||||
return Color.SandyBrown;
|
||||
return Color.BurlyWood;
|
||||
}
|
||||
|
||||
/// <summary>Notes about rivers the number is how many sides / diagonals are water.</summary>
|
||||
private static Color GetRiverColor(TerrainUnitModel mdl, LandAngles landAngle, int relativeX, int relativeY)
|
||||
{
|
||||
return mdl switch
|
||||
{
|
||||
// River0A single "hole" of water land all sides. Rotation does nothing
|
||||
River0A when (relativeX < 4 || relativeX >= 12 || relativeY < 4 || relativeY >= 12) =>
|
||||
Grass,
|
||||
// River1A narrow channel end opening on bottom, land on other sides
|
||||
River1A => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || relativeX >= 12 || relativeY < 4 => Grass,
|
||||
Rotate90ClockAnverse when relativeX < 4 || relativeY < 4 || relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when relativeX < 4 || relativeX >= 12 || relativeY >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || relativeY >= 12 || relativeX >= 12 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River2A narrow water channel opening on top and bottom, land left and right
|
||||
River2A => landAngle switch
|
||||
{
|
||||
Default when relativeX is < 4 or >= 12 => Grass,
|
||||
Rotate90ClockAnverse when relativeY is >= 12 or < 4 => Grass,
|
||||
Rotate180ClockAnverse when relativeX is < 4 or >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeY is < 4 or >= 12 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River2B narrow 45 channel angled land top left with nub bottom right
|
||||
River2B => landAngle switch
|
||||
{
|
||||
Default when IsPointInMultiTriangle(relativeX, relativeY, new(4, 15), new(0, 0), new(15, 4), new(0, 15), new(15, 0)) || IsNubOnBottomRight(relativeX, relativeY) || relativeX < 4 || relativeY < 4 => Grass,
|
||||
Rotate90ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(4, 0), new(0, 15), new(15, 12), new(0, 0), new(15, 15)) || IsNubOnTopRight(relativeX, relativeY) || relativeX < 4 || relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 12), new(15, 15), new(12, 0), new(0, 15), new(15, 0)) || IsNubOnTopLeft(relativeX, relativeY) || relativeX >= 12 || relativeY >= 12 => Grass,
|
||||
Rotate270ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 4), new(15, 0), new(12, 15), new(0, 0), new(15, 15)) || IsNubOnBottomLeft(relativeX, relativeY) || relativeX >= 12 || relativeY < 4 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River2C narrow 90 channel corner land top left with nub bottom right
|
||||
River2C => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeX < 4 || relativeY >= 12 || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeX >= 12 || relativeY < 4 || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River3A narrow 3 way land left side, nub top right and bottom right
|
||||
River3A => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || IsNubOnTopRight(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River3B river 45 corner angled land top left, no nub
|
||||
River3B => landAngle switch
|
||||
{
|
||||
Default when IsPointInMultiTriangle(relativeX, relativeY, new(4, 15), new(0, 0), new(15, 4), new(0, 15), new(15, 0)) => Grass,
|
||||
Rotate90ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(4, 0), new(0, 15), new(15, 12), new(0, 0), new(15, 15)) => Grass,
|
||||
Rotate180ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 12), new(15, 15), new(12, 0), new(0, 15), new(15, 0)) => Grass,
|
||||
Rotate270ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 4), new(15, 0), new(12, 15), new(0, 0), new(15, 15)) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River3C river 90 corner corner land top left, no nub
|
||||
River3C => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || relativeY < 4 => Grass,
|
||||
Rotate90ClockAnverse when relativeX < 4 || relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || relativeY >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeX >= 12 || relativeY < 4 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River4A river side with nub top land left side with nub top right only
|
||||
River4A => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River4B river side with nub bottom land left side with nub bottom right only
|
||||
River4B => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River4C narrow 4 way nub on all 4 corners, 4 sides water. rotation does nothing
|
||||
River4C when (IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY)) => Grass,
|
||||
// River5A river corner to 2 narrow Nub on top left, top right, and bottom right. 2 narrows meet a river
|
||||
River5A => landAngle switch
|
||||
{
|
||||
Default when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River5B river side land on left side
|
||||
River5B => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River6A river 2 opposing nubs nub on top left and bottom right
|
||||
River6A => landAngle switch
|
||||
{
|
||||
Default when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River6B river 2 nubs same side nub on bottom left and bottom right corner, where 1 narrow meets river bottom side
|
||||
River6B => landAngle switch
|
||||
{
|
||||
Default when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnTopRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River7A river 1 nub nub on bottom left corner, fills gaps of diagonal bank
|
||||
River7A => landAngle switch
|
||||
{
|
||||
Default when IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River8A river is no land, just water. Rotation doesn't matter
|
||||
River8A => River,
|
||||
_ => River
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsNubOnTopLeft(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(0, 4), new(0, 0), new(4, 0));
|
||||
private static bool IsNubOnTopRight(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(12, 0), new(15, 0), new(15, 4));
|
||||
private static bool IsNubOnBottomLeft(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(0, 12), new(0, 15), new(4, 15));
|
||||
private static bool IsNubOnBottomRight(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(12, 15), new(15, 15), new(15, 12));
|
||||
|
||||
private static bool IsPointInMultiTriangle(int px, int py, Coordinate a, Coordinate b, Coordinate c, Coordinate vortexA, Coordinate vortexB)
|
||||
{
|
||||
return IsPointInTriangle(px, py, a, vortexA, b)
|
||||
|| IsPointInTriangle(px, py, a, b, c)
|
||||
|| IsPointInTriangle(px, py, c, b, vortexB);
|
||||
}
|
||||
|
||||
private static bool IsPointInTriangle(int px, int py, Coordinate a, Coordinate b, Coordinate c)
|
||||
{
|
||||
Coordinate p = new(px, py);
|
||||
float areaTotal = GetTriangleArea(a, b, c);
|
||||
float area1 = GetTriangleArea(p, b, c);
|
||||
float area2 = GetTriangleArea(a, p, c);
|
||||
float area3 = GetTriangleArea(a, b, p);
|
||||
|
||||
return Math.Abs(areaTotal - (area1 + area2 + area3)) < 0.0001f;
|
||||
}
|
||||
|
||||
private static float GetTriangleArea(Coordinate A, Coordinate B, Coordinate C)
|
||||
{
|
||||
return Math.Abs((A.X * (B.Y - C.Y) +
|
||||
B.X * (C.Y - A.Y) +
|
||||
C.X * (A.Y - B.Y)) / 2.0f);
|
||||
}
|
||||
|
||||
private readonly record struct Coordinate(int X, int Y);
|
||||
|
||||
private static readonly Color CliffBase = ColorUtil.Blend(Grass, Color.Black, 0.6d);
|
||||
|
||||
private static Color GetTileDefaultColor(TerrainUnitModel mdl, ushort landAngle, int relativeX, int relativeY)
|
||||
{
|
||||
var angle = (LandAngles)landAngle;
|
||||
if (mdl.IsRiver())
|
||||
return GetRiverColor(mdl, angle, relativeX, relativeY);
|
||||
if (mdl.IsFall())
|
||||
return Color.DeepSkyBlue;
|
||||
if (mdl.IsCliff())
|
||||
return CliffBase;
|
||||
return Grass;
|
||||
}
|
||||
|
||||
private static readonly char[] Numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
|
||||
public static string GetTileName(TerrainTile tile)
|
||||
{
|
||||
var name = tile.UnitModel.ToString();
|
||||
var num = name.IndexOfAny(Numbers);
|
||||
if (num < 0)
|
||||
return name;
|
||||
return name.Substring(0, num) + Environment.NewLine + name.Substring(num);
|
||||
}
|
||||
return ColorUtil.Blend(baseColor, Color.White, 1.4d / (tile.Elevation + 1));
|
||||
}
|
||||
}
|
||||
|
||||
private static Color GetRoadColor(TerrainUnitModel mdl)
|
||||
{
|
||||
if (mdl.IsRoadBrick)
|
||||
return Color.Firebrick;
|
||||
if (mdl.IsRoadDarkSoil)
|
||||
return Color.SaddleBrown;
|
||||
if (mdl.IsRoadSoil)
|
||||
return Color.Peru;
|
||||
if (mdl.IsRoadStone)
|
||||
return Color.DarkGray;
|
||||
if (mdl.IsRoadPattern)
|
||||
return Color.Ivory;
|
||||
if (mdl.IsRoadTile)
|
||||
return Color.SteelBlue;
|
||||
if (mdl.IsRoadSand)
|
||||
return Color.SandyBrown;
|
||||
return Color.BurlyWood;
|
||||
}
|
||||
|
||||
/// <summary>Notes about rivers the number is how many sides / diagonals are water.</summary>
|
||||
private static Color GetRiverColor(TerrainUnitModel mdl, LandAngles landAngle, int relativeX, int relativeY)
|
||||
{
|
||||
return mdl switch
|
||||
{
|
||||
// River0A single "hole" of water land all sides. Rotation does nothing
|
||||
River0A when (relativeX < 4 || relativeX >= 12 || relativeY < 4 || relativeY >= 12) =>
|
||||
Grass,
|
||||
// River1A narrow channel end opening on bottom, land on other sides
|
||||
River1A => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || relativeX >= 12 || relativeY < 4 => Grass,
|
||||
Rotate90ClockAnverse when relativeX < 4 || relativeY < 4 || relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when relativeX < 4 || relativeX >= 12 || relativeY >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || relativeY >= 12 || relativeX >= 12 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River2A narrow water channel opening on top and bottom, land left and right
|
||||
River2A => landAngle switch
|
||||
{
|
||||
Default when relativeX is < 4 or >= 12 => Grass,
|
||||
Rotate90ClockAnverse when relativeY is >= 12 or < 4 => Grass,
|
||||
Rotate180ClockAnverse when relativeX is < 4 or >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeY is < 4 or >= 12 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River2B narrow 45 channel angled land top left with nub bottom right
|
||||
River2B => landAngle switch
|
||||
{
|
||||
Default when IsPointInMultiTriangle(relativeX, relativeY, new(4, 15), new(0, 0), new(15, 4), new(0, 15), new(15, 0)) || IsNubOnBottomRight(relativeX, relativeY) || relativeX < 4 || relativeY < 4 => Grass,
|
||||
Rotate90ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(4, 0), new(0, 15), new(15, 12), new(0, 0), new(15, 15)) || IsNubOnTopRight(relativeX, relativeY) || relativeX < 4 || relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 12), new(15, 15), new(12, 0), new(0, 15), new(15, 0)) || IsNubOnTopLeft(relativeX, relativeY) || relativeX >= 12 || relativeY >= 12 => Grass,
|
||||
Rotate270ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 4), new(15, 0), new(12, 15), new(0, 0), new(15, 15)) || IsNubOnBottomLeft(relativeX, relativeY) || relativeX >= 12 || relativeY < 4 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River2C narrow 90 channel corner land top left with nub bottom right
|
||||
River2C => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeX < 4 || relativeY >= 12 || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeX >= 12 || relativeY < 4 || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River3A narrow 3 way land left side, nub top right and bottom right
|
||||
River3A => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || IsNubOnTopRight(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River3B river 45 corner angled land top left, no nub
|
||||
River3B => landAngle switch
|
||||
{
|
||||
Default when IsPointInMultiTriangle(relativeX, relativeY, new(4, 15), new(0, 0), new(15, 4), new(0, 15), new(15, 0)) => Grass,
|
||||
Rotate90ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(4, 0), new(0, 15), new(15, 12), new(0, 0), new(15, 15)) => Grass,
|
||||
Rotate180ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 12), new(15, 15), new(12, 0), new(0, 15), new(15, 0)) => Grass,
|
||||
Rotate270ClockAnverse when IsPointInMultiTriangle(relativeX, relativeY, new(0, 4), new(15, 0), new(12, 15), new(0, 0), new(15, 15)) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River3C river 90 corner corner land top left, no nub
|
||||
River3C => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || relativeY < 4 => Grass,
|
||||
Rotate90ClockAnverse when relativeX < 4 || relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || relativeY >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeX >= 12 || relativeY < 4 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River4A river side with nub top land left side with nub top right only
|
||||
River4A => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River4B river side with nub bottom land left side with nub bottom right only
|
||||
River4B => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River4C narrow 4 way nub on all 4 corners, 4 sides water. rotation does nothing
|
||||
River4C when (IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY)) => Grass,
|
||||
// River5A river corner to 2 narrow Nub on top left, top right, and bottom right. 2 narrows meet a river
|
||||
River5A => landAngle switch
|
||||
{
|
||||
Default when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River5B river side land on left side
|
||||
River5B => landAngle switch
|
||||
{
|
||||
Default when relativeX < 4 => Grass,
|
||||
Rotate90ClockAnverse when relativeY >= 12 => Grass,
|
||||
Rotate180ClockAnverse when relativeX >= 12 => Grass,
|
||||
Rotate270ClockAnverse when relativeY < 4 => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River6A river 2 opposing nubs nub on top left and bottom right
|
||||
River6A => landAngle switch
|
||||
{
|
||||
Default when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River6B river 2 nubs same side nub on bottom left and bottom right corner, where 1 narrow meets river bottom side
|
||||
River6B => landAngle switch
|
||||
{
|
||||
Default when IsNubOnBottomLeft(relativeX, relativeY) || IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomRight(relativeX, relativeY) || IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnTopRight(relativeX, relativeY) || IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) || IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River7A river 1 nub nub on bottom left corner, fills gaps of diagonal bank
|
||||
River7A => landAngle switch
|
||||
{
|
||||
Default when IsNubOnBottomLeft(relativeX, relativeY) => Grass,
|
||||
Rotate90ClockAnverse when IsNubOnBottomRight(relativeX, relativeY) => Grass,
|
||||
Rotate180ClockAnverse when IsNubOnTopRight(relativeX, relativeY) => Grass,
|
||||
Rotate270ClockAnverse when IsNubOnTopLeft(relativeX, relativeY) => Grass,
|
||||
_ => River
|
||||
},
|
||||
// River8A river is no land, just water. Rotation doesn't matter
|
||||
River8A => River,
|
||||
_ => River
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsNubOnTopLeft(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(0, 4), new(0, 0), new(4, 0));
|
||||
private static bool IsNubOnTopRight(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(12, 0), new(15, 0), new(15, 4));
|
||||
private static bool IsNubOnBottomLeft(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(0, 12), new(0, 15), new(4, 15));
|
||||
private static bool IsNubOnBottomRight(int relativeX, int relativeY) => IsPointInTriangle(relativeX, relativeY, new(12, 15), new(15, 15), new(15, 12));
|
||||
|
||||
private static bool IsPointInMultiTriangle(int px, int py, Coordinate a, Coordinate b, Coordinate c, Coordinate vortexA, Coordinate vortexB)
|
||||
{
|
||||
return IsPointInTriangle(px, py, a, vortexA, b)
|
||||
|| IsPointInTriangle(px, py, a, b, c)
|
||||
|| IsPointInTriangle(px, py, c, b, vortexB);
|
||||
}
|
||||
|
||||
private static bool IsPointInTriangle(int px, int py, Coordinate a, Coordinate b, Coordinate c)
|
||||
{
|
||||
Coordinate p = new(px, py);
|
||||
float areaTotal = GetTriangleArea(a, b, c);
|
||||
float area1 = GetTriangleArea(p, b, c);
|
||||
float area2 = GetTriangleArea(a, p, c);
|
||||
float area3 = GetTriangleArea(a, b, p);
|
||||
|
||||
return Math.Abs(areaTotal - (area1 + area2 + area3)) < 0.0001f;
|
||||
}
|
||||
|
||||
private static float GetTriangleArea(Coordinate a, Coordinate b, Coordinate c)
|
||||
{
|
||||
return Math.Abs(((a.X * (b.Y - c.Y)) +
|
||||
(b.X * (c.Y - a.Y)) +
|
||||
(c.X * (a.Y - b.Y))) / 2.0f);
|
||||
}
|
||||
|
||||
private readonly record struct Coordinate(int X, int Y);
|
||||
|
||||
private static readonly Color CliffBase = ColorUtil.Blend(Grass, Color.Black, 0.6d);
|
||||
|
||||
private static Color GetTileDefaultColor(TerrainUnitModel mdl, ushort landAngle, int relativeX, int relativeY)
|
||||
{
|
||||
var angle = (LandAngles)landAngle;
|
||||
if (mdl.IsRiver)
|
||||
return GetRiverColor(mdl, angle, relativeX, relativeY);
|
||||
if (mdl.IsFall)
|
||||
return Color.DeepSkyBlue;
|
||||
if (mdl.IsCliff)
|
||||
return CliffBase;
|
||||
return Grass;
|
||||
}
|
||||
|
||||
public static string GetTileName(TerrainTile tile)
|
||||
{
|
||||
var name = tile.UnitModel.ToString();
|
||||
var num = name.IndexOfAnyInRange('0', '9');
|
||||
if (num < 0)
|
||||
return name;
|
||||
return name[..num] + Environment.NewLine + name[num..];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
public abstract class BatchMutator<T> where T : class
|
||||
{
|
||||
protected const string CONST_RAND = "$rand";
|
||||
namespace NHSE.Core;
|
||||
|
||||
public abstract ModifyResult Modify(T item, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications);
|
||||
}
|
||||
}
|
||||
public abstract class BatchMutator<T> where T : class
|
||||
{
|
||||
protected const string CONST_RAND = "$rand";
|
||||
|
||||
public abstract ModifyResult Modify(T item, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications);
|
||||
}
|
||||
|
|
@ -2,86 +2,82 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Carries out a batch edit and contains information summarizing the results.
|
||||
/// </summary>
|
||||
public abstract class BatchProcessor<T>(BatchMutator<T> Mutator) where T : class
|
||||
{
|
||||
private int Modified { get; set; }
|
||||
private int Iterated { get; set; }
|
||||
private int Failed { get; set; }
|
||||
|
||||
protected abstract bool CanModify(T item);
|
||||
protected abstract bool Finalize(T item);
|
||||
|
||||
/// <summary>
|
||||
/// Carries out a batch edit and contains information summarizing the results.
|
||||
/// Tries to modify the <see cref="item"/>.
|
||||
/// </summary>
|
||||
public abstract class BatchProcessor<T> where T : class
|
||||
/// <param name="item">Object to modify.</param>
|
||||
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
|
||||
/// <param name="modifications">Modifications to perform on the item.</param>
|
||||
/// <returns>Result of the attempted modification.</returns>
|
||||
public bool Process(T item, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
{
|
||||
private int Modified { get; set; }
|
||||
private int Iterated { get; set; }
|
||||
private int Failed { get; set; }
|
||||
if (!CanModify(item))
|
||||
return false;
|
||||
|
||||
protected readonly BatchMutator<T> Mutator;
|
||||
protected BatchProcessor(BatchMutator<T> mut) => Mutator = mut;
|
||||
var result = Mutator.Modify(item, filters, modifications);
|
||||
if (result != ModifyResult.Invalid)
|
||||
Iterated++;
|
||||
if (result == ModifyResult.Error)
|
||||
Failed++;
|
||||
if (result != ModifyResult.Modified)
|
||||
return false;
|
||||
|
||||
protected abstract bool CanModify(T item);
|
||||
protected abstract bool Finalize(T item);
|
||||
Finalize(item);
|
||||
Modified++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to modify the <see cref="item"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">Object to modify.</param>
|
||||
/// <param name="filters">Filters which must be satisfied prior to any modifications being made.</param>
|
||||
/// <param name="modifications">Modifications to perform on the item.</param>
|
||||
/// <returns>Result of the attempted modification.</returns>
|
||||
public bool Process(T item, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
/// <summary>
|
||||
/// Gets a message indicating the overall result of all modifications performed across multiple Batch Edit jobs.
|
||||
/// </summary>
|
||||
/// <param name="sets">Collection of modifications.</param>
|
||||
/// <returns>Friendly (multi-line) string indicating the result of the batch edits.</returns>
|
||||
public string GetEditorResults(ICollection<StringInstructionSet> sets)
|
||||
{
|
||||
if (sets.Count == 0)
|
||||
return "No instructions present.";
|
||||
int ctr = Modified / sets.Count;
|
||||
int len = Iterated / sets.Count;
|
||||
string maybe = sets.Count == 1 ? string.Empty : "~";
|
||||
string result = $"Success: {maybe}{ctr}/{len}";
|
||||
if (Failed > 0)
|
||||
result += Environment.NewLine + maybe + $"Failed: {Failed} not processed.";
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Execute(ReadOnlySpan<string> lines, IEnumerable<T> data)
|
||||
{
|
||||
var sets = StringInstructionSet.GetBatchSets(lines).ToArray();
|
||||
foreach (var pk in data)
|
||||
{
|
||||
if (!CanModify(item))
|
||||
return false;
|
||||
|
||||
var result = Mutator.Modify(item, filters, modifications);
|
||||
if (result != ModifyResult.Invalid)
|
||||
Iterated++;
|
||||
if (result == ModifyResult.Error)
|
||||
Failed++;
|
||||
if (result != ModifyResult.Modified)
|
||||
return false;
|
||||
|
||||
Finalize(item);
|
||||
Modified++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a message indicating the overall result of all modifications performed across multiple Batch Edit jobs.
|
||||
/// </summary>
|
||||
/// <param name="sets">Collection of modifications.</param>
|
||||
/// <returns>Friendly (multi-line) string indicating the result of the batch edits.</returns>
|
||||
public string GetEditorResults(ICollection<StringInstructionSet> sets)
|
||||
{
|
||||
if (sets.Count == 0)
|
||||
return "No instructions present.";
|
||||
int ctr = Modified / sets.Count;
|
||||
int len = Iterated / sets.Count;
|
||||
string maybe = sets.Count == 1 ? string.Empty : "~";
|
||||
string result = $"Success: {maybe}{ctr}/{len}";
|
||||
if (Failed > 0)
|
||||
result += Environment.NewLine + maybe + $"Failed: {Failed} not processed.";
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Execute(IList<string> lines, IEnumerable<T> data)
|
||||
{
|
||||
var sets = StringInstructionSet.GetBatchSets(lines).ToArray();
|
||||
foreach (var pk in data)
|
||||
{
|
||||
foreach (var set in sets)
|
||||
Process(pk, set.Filters, set.Instructions);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void Initialize(StringInstructionSet[] sets);
|
||||
|
||||
public void Process(StringInstructionSet[] sets, IReadOnlyList<T> items)
|
||||
{
|
||||
Initialize(sets);
|
||||
foreach (var s in sets)
|
||||
{
|
||||
foreach (var i in items)
|
||||
Process(i, s.Filters, s.Instructions);
|
||||
}
|
||||
foreach (var set in sets)
|
||||
Process(pk, set.Filters, set.Instructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void Initialize(ReadOnlySpan<StringInstructionSet> sets);
|
||||
|
||||
public void Process(ReadOnlySpan<StringInstructionSet> sets, IReadOnlyList<T> items)
|
||||
{
|
||||
Initialize(sets);
|
||||
foreach (var s in sets)
|
||||
{
|
||||
foreach (var i in items)
|
||||
Process(i, s.Filters, s.Instructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,205 +5,214 @@
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public class ItemMutator : BatchMutator<Item>
|
||||
{
|
||||
public class ItemMutator : BatchMutator<Item>
|
||||
public readonly ItemReflection Reflect = ItemReflection.Default;
|
||||
private const char CONST_POINTER = '*';
|
||||
|
||||
public override ModifyResult Modify(Item item, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
{
|
||||
public readonly ItemReflection Reflect = ItemReflection.Default;
|
||||
|
||||
public override ModifyResult Modify(Item item, IEnumerable<StringInstruction> filters, IEnumerable<StringInstruction> modifications)
|
||||
var pi = Reflect.Props[Array.IndexOf(Reflect.Types, item.GetType())];
|
||||
foreach (var cmd in filters)
|
||||
{
|
||||
var pi = Reflect.Props[Array.IndexOf(Reflect.Types, item.GetType())];
|
||||
foreach (var cmd in filters)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsFilterMatch(cmd, item, pi))
|
||||
return ModifyResult.Filtered;
|
||||
}
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Failed to compare: {ex.Message} - {cmd.PropertyName} {cmd.PropertyValue}");
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
if (!IsFilterMatch(cmd, item, pi))
|
||||
return ModifyResult.Filtered;
|
||||
}
|
||||
|
||||
ModifyResult result = ModifyResult.Modified;
|
||||
foreach (var cmd in modifications)
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tmp = SetProperty(cmd, item, pi);
|
||||
if (tmp != ModifyResult.Modified)
|
||||
result = tmp;
|
||||
}
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Failed to modify: {ex.Message} - {cmd.PropertyName} {cmd.PropertyValue}");
|
||||
}
|
||||
Debug.WriteLine($"Failed to compare: {ex.Message} - {cmd.PropertyName} {cmd.PropertyValue}");
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the if the <see cref="Item"/> should be filtered due to the <see cref="StringInstruction"/> provided.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Filter</param>
|
||||
/// <param name="item">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache (optional)</param>
|
||||
/// <returns>True if filtered, else false.</returns>
|
||||
private static ModifyResult SetProperty(StringInstruction cmd, Item item, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
ModifyResult result = ModifyResult.Modified;
|
||||
foreach (var cmd in modifications)
|
||||
{
|
||||
if (SetComplexProperty(item, cmd))
|
||||
return ModifyResult.Modified;
|
||||
try
|
||||
{
|
||||
var tmp = SetProperty(cmd, item, pi);
|
||||
if (tmp != ModifyResult.Modified)
|
||||
result = tmp;
|
||||
}
|
||||
// Swallow any error because this can be malformed user input.
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Failed to modify: {ex.Message} - {cmd.PropertyName} {cmd.PropertyValue}");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return ModifyResult.Error;
|
||||
|
||||
if (!pi.CanWrite)
|
||||
return ModifyResult.Error;
|
||||
|
||||
object val = cmd.Random ? (object)cmd.RandomValue : cmd.PropertyValue;
|
||||
ReflectUtil.SetValue(pi, item, val);
|
||||
/// <summary>
|
||||
/// Sets if the <see cref="Item"/> should be filtered due to the <see cref="StringInstruction"/> provided.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Filter</param>
|
||||
/// <param name="item">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache (optional)</param>
|
||||
/// <returns>True if filtered, else false.</returns>
|
||||
private static ModifyResult SetProperty(StringInstruction cmd, Item item, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> props)
|
||||
{
|
||||
if (SetComplexProperty(item, cmd))
|
||||
return ModifyResult.Modified;
|
||||
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return ModifyResult.Error;
|
||||
|
||||
if (!pi.CanWrite)
|
||||
return ModifyResult.Error;
|
||||
|
||||
if (cmd.Random)
|
||||
ReflectUtil.SetValue(pi, item, cmd.RandomValue);
|
||||
else
|
||||
ReflectUtil.SetValue(pi, item, cmd.PropertyValue);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
|
||||
private static bool SetComplexProperty(Item item, StringInstruction cmd)
|
||||
{
|
||||
// Zeroed out item?
|
||||
if (cmd.PropertyName == nameof(Item.ItemId))
|
||||
{
|
||||
if (!int.TryParse(cmd.PropertyValue, out var val))
|
||||
return false;
|
||||
if (val is not (0 or 0xFFFE))
|
||||
return false;
|
||||
item.Delete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="Item"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="item">Pokémon to check</param>
|
||||
/// <param name="name">Property Name to check</param>
|
||||
/// <param name="pi">Property Info retrieved (if any).</param>
|
||||
/// <returns>True if has property, false if does not.</returns>
|
||||
public bool TryGetHasProperty(Item item, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
{
|
||||
var type = item.GetType();
|
||||
return TryGetHasProperty(type, name, out pi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="Item"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="type">Type to check</param>
|
||||
/// <param name="name">Property Name to check</param>
|
||||
/// <param name="pi">Property Info retrieved (if any).</param>
|
||||
/// <returns>True if has property, false if does not.</returns>
|
||||
public bool TryGetHasProperty(Type type, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
{
|
||||
var index = Array.IndexOf(Reflect.Types, type);
|
||||
if (index < 0)
|
||||
{
|
||||
pi = null;
|
||||
return false;
|
||||
}
|
||||
var props = Reflect.Props[index];
|
||||
return props.TryGetValue(name, out pi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the <see cref="Item"/> property using the saved cache of properties.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Property Name to fetch the type for</param>
|
||||
/// <param name="typeIndex">Type index. Leave empty (0) for a nonspecific format.</param>
|
||||
/// <returns>Short name of the property's type.</returns>
|
||||
public string? GetPropertyType(string propertyName, int typeIndex = 0)
|
||||
{
|
||||
if (typeIndex == 0) // Any
|
||||
{
|
||||
foreach (var p in Reflect.Props)
|
||||
{
|
||||
if (p.TryGetValue(propertyName, out var pi))
|
||||
return pi.PropertyType.Name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool SetComplexProperty(Item item, StringInstruction cmd)
|
||||
int index = typeIndex - 1 >= Reflect.Props.Length ? 0 : typeIndex - 1; // All vs Specific
|
||||
var pr = Reflect.Props[index];
|
||||
if (!pr.TryGetValue(propertyName, out var info))
|
||||
return null;
|
||||
return info.PropertyType.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the object is filtered by the provided <see cref="filters"/>.
|
||||
/// </summary>
|
||||
/// <param name="filters">Filters which must be satisfied.</param>
|
||||
/// <param name="item">Object to check.</param>
|
||||
/// <returns>True if <see cref="item"/> matches all filters.</returns>
|
||||
public bool IsFilterMatch(IEnumerable<StringInstruction> filters, Item item) => filters.All(z => IsFilterMatch(z, item, Reflect.Props[Array.IndexOf(Reflect.Types, item.GetType())]));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="Item"/> should be filtered due to the <see cref="StringInstruction"/> provided.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Filter</param>
|
||||
/// <param name="item">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache (optional)</param>
|
||||
/// <returns>True if filter matches, else false.</returns>
|
||||
private static bool IsFilterMatch(StringInstruction cmd, Item item, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> props)
|
||||
{
|
||||
return IsPropertyFiltered(cmd, item, props);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="Item"/> should be filtered due to the <see cref="StringInstruction"/> provided.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Filter</param>
|
||||
/// <param name="item">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache</param>
|
||||
/// <returns>True if filtered, else false.</returns>
|
||||
private static bool IsPropertyFiltered(StringInstruction cmd, Item item, Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>> props)
|
||||
{
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
if (!pi.CanRead)
|
||||
return false;
|
||||
|
||||
var val = cmd.PropertyValue;
|
||||
if (val.StartsWith(CONST_POINTER) && props.TryGetValue(val.AsSpan(1), out var opi))
|
||||
{
|
||||
// Zeroed out item?
|
||||
if (cmd.PropertyName == nameof(Item.ItemId))
|
||||
var result = opi.GetValue(item) ?? throw new NullReferenceException();
|
||||
return cmd.Comparer.IsCompareOperator(pi.CompareTo(item, result));
|
||||
}
|
||||
return cmd.Comparer.IsCompareOperator(pi.CompareTo(item, val));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the object is filtered by the provided <see cref="filters"/>.
|
||||
/// </summary>
|
||||
/// <param name="filters">Filters which must be satisfied.</param>
|
||||
/// <param name="obj">Object to check.</param>
|
||||
/// <returns>True if <see cref="obj"/> matches all filters.</returns>
|
||||
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, object obj)
|
||||
{
|
||||
foreach (var cmd in filters)
|
||||
{
|
||||
if (!ReflectUtil.HasProperty(obj, cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
if (!int.TryParse(cmd.PropertyValue, out var val))
|
||||
return false;
|
||||
if (val is not 0 or 0xFFFE)
|
||||
return false;
|
||||
item.Delete();
|
||||
return true;
|
||||
if (cmd.Comparer.IsCompareOperator(pi.CompareTo(obj, cmd.PropertyValue)))
|
||||
continue;
|
||||
}
|
||||
// User provided inputs can mismatch the type's required value format, and fail to be compared.
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}.");
|
||||
Debug.WriteLine(e.Message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="Item"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="item">Pokémon to check</param>
|
||||
/// <param name="name">Property Name to check</param>
|
||||
/// <param name="pi">Property Info retrieved (if any).</param>
|
||||
/// <returns>True if has property, false if does not.</returns>
|
||||
public bool TryGetHasProperty(Item item, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
{
|
||||
var type = item.GetType();
|
||||
return TryGetHasProperty(type, name, out pi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fetch the <see cref="Item"/> property from the cache of available properties.
|
||||
/// </summary>
|
||||
/// <param name="type">Type to check</param>
|
||||
/// <param name="name">Property Name to check</param>
|
||||
/// <param name="pi">Property Info retrieved (if any).</param>
|
||||
/// <returns>True if has property, false if does not.</returns>
|
||||
public bool TryGetHasProperty(Type type, string name, [NotNullWhen(true)] out PropertyInfo? pi)
|
||||
{
|
||||
var index = Array.IndexOf(Reflect.Types, type);
|
||||
if (index < 0)
|
||||
{
|
||||
pi = null;
|
||||
return false;
|
||||
}
|
||||
var props = Reflect.Props[index];
|
||||
return props.TryGetValue(name, out pi);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the <see cref="Item"/> property using the saved cache of properties.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Property Name to fetch the type for</param>
|
||||
/// <param name="typeIndex">Type index. Leave empty (0) for a nonspecific format.</param>
|
||||
/// <returns>Short name of the property's type.</returns>
|
||||
public string? GetPropertyType(string propertyName, int typeIndex = 0)
|
||||
{
|
||||
if (typeIndex == 0) // Any
|
||||
{
|
||||
foreach (var p in Reflect.Props)
|
||||
{
|
||||
if (p.TryGetValue(propertyName, out var pi))
|
||||
return pi.PropertyType.Name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = typeIndex - 1 >= Reflect.Props.Length ? 0 : typeIndex - 1; // All vs Specific
|
||||
var pr = Reflect.Props[index];
|
||||
if (!pr.TryGetValue(propertyName, out var info))
|
||||
return null;
|
||||
return info.PropertyType.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the object is filtered by the provided <see cref="filters"/>.
|
||||
/// </summary>
|
||||
/// <param name="filters">Filters which must be satisfied.</param>
|
||||
/// <param name="item">Object to check.</param>
|
||||
/// <returns>True if <see cref="item"/> matches all filters.</returns>
|
||||
public bool IsFilterMatch(IEnumerable<StringInstruction> filters, Item item) => filters.All(z => IsFilterMatch(z, item, Reflect.Props[Array.IndexOf(Reflect.Types, item.GetType())]));
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="Item"/> should be filtered due to the <see cref="StringInstruction"/> provided.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Filter</param>
|
||||
/// <param name="item">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache (optional)</param>
|
||||
/// <returns>True if filter matches, else false.</returns>
|
||||
private static bool IsFilterMatch(StringInstruction cmd, Item item, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
return IsPropertyFiltered(cmd, item, props);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="Item"/> should be filtered due to the <see cref="StringInstruction"/> provided.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command Filter</param>
|
||||
/// <param name="item">Pokémon to check.</param>
|
||||
/// <param name="props">PropertyInfo cache</param>
|
||||
/// <returns>True if filtered, else false.</returns>
|
||||
private static bool IsPropertyFiltered(StringInstruction cmd, Item item, IReadOnlyDictionary<string, PropertyInfo> props)
|
||||
{
|
||||
if (!props.TryGetValue(cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
if (!pi.CanRead)
|
||||
return false;
|
||||
return pi.IsValueEqual(item, cmd.PropertyValue) == cmd.Evaluator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the object is filtered by the provided <see cref="filters"/>.
|
||||
/// </summary>
|
||||
/// <param name="filters">Filters which must be satisfied.</param>
|
||||
/// <param name="obj">Object to check.</param>
|
||||
/// <returns>True if <see cref="obj"/> matches all filters.</returns>
|
||||
public static bool IsFilterMatch(IEnumerable<StringInstruction> filters, object obj)
|
||||
{
|
||||
foreach (var cmd in filters)
|
||||
{
|
||||
if (!ReflectUtil.HasProperty(obj, cmd.PropertyName, out var pi))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
if (pi.IsValueEqual(obj, cmd.PropertyValue) == cmd.Evaluator)
|
||||
continue;
|
||||
}
|
||||
// User provided inputs can mismatch the type's required value format, and fail to be compared.
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine($"Unable to compare {cmd.PropertyName} to {cmd.PropertyValue}.");
|
||||
Debug.WriteLine(e.Message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +1,48 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public class ItemProcessor(BatchMutator<Item> mut) : BatchProcessor<Item>(mut)
|
||||
{
|
||||
public class ItemProcessor : BatchProcessor<Item>
|
||||
protected override bool CanModify(Item item) => true;
|
||||
protected override bool Finalize(Item item) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="StringInstruction"/> list with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
|
||||
/// </summary>
|
||||
/// <param name="il">Instructions to initialize.</param>
|
||||
public static void ScreenStrings(IEnumerable<StringInstruction> il)
|
||||
{
|
||||
public ItemProcessor(BatchMutator<Item> mut) : base(mut)
|
||||
foreach (var i in il.Where(i => !i.PropertyValue.All(char.IsDigit)))
|
||||
{
|
||||
}
|
||||
string pv = i.PropertyValue;
|
||||
if (pv.StartsWith('$') && pv.Contains(','))
|
||||
i.SetRandomRange(pv);
|
||||
|
||||
protected override bool CanModify(Item item) => true;
|
||||
protected override bool Finalize(Item item) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="StringInstruction"/> list with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
|
||||
/// </summary>
|
||||
/// <param name="il">Instructions to initialize.</param>
|
||||
public void ScreenStrings(IEnumerable<StringInstruction> il)
|
||||
{
|
||||
foreach (var i in il.Where(i => !i.PropertyValue.All(char.IsDigit)))
|
||||
{
|
||||
string pv = i.PropertyValue;
|
||||
if (pv.StartsWith("$") && pv.Contains(","))
|
||||
i.SetRandRange(pv);
|
||||
|
||||
SetInstructionScreenedValue(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="StringInstruction"/> with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
|
||||
/// </summary>
|
||||
/// <param name="i">Instruction to initialize.</param>
|
||||
private static void SetInstructionScreenedValue(StringInstruction i)
|
||||
{
|
||||
switch (i.PropertyName)
|
||||
{
|
||||
case nameof(Item.ItemId) or nameof(Item.ExtensionItemId): i.SetScreenedValue(GameInfo.Strings.itemlistdisplay); return;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Initialize(StringInstructionSet[] sets)
|
||||
{
|
||||
foreach (var set in sets)
|
||||
{
|
||||
ScreenStrings(set.Filters);
|
||||
ScreenStrings(set.Instructions);
|
||||
}
|
||||
SetInstructionScreenedValue(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the <see cref="StringInstruction"/> with a context-sensitive value. If the provided value is a string, it will attempt to convert that string to its corresponding index.
|
||||
/// </summary>
|
||||
/// <param name="i">Instruction to initialize.</param>
|
||||
private static void SetInstructionScreenedValue(StringInstruction i)
|
||||
{
|
||||
switch (i.PropertyName)
|
||||
{
|
||||
case nameof(Item.ItemId) or nameof(Item.ExtensionItemId): i.SetScreenedValue(GameInfo.Strings.itemlistdisplay); return;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Initialize(ReadOnlySpan<StringInstructionSet> sets)
|
||||
{
|
||||
foreach (var set in sets)
|
||||
{
|
||||
ScreenStrings(set.Filters);
|
||||
ScreenStrings(set.Instructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,48 +3,82 @@
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public class ItemReflection
|
||||
{
|
||||
public class ItemReflection
|
||||
public static ItemReflection Default { get; } = new();
|
||||
|
||||
public readonly Type[] Types = [typeof(Item), typeof(VillagerItem)];
|
||||
public string[][] Properties => GetProperties.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Extra properties to show in the list of selectable properties (GUI)
|
||||
/// </summary>
|
||||
private static readonly string[] CustomProperties =
|
||||
[
|
||||
];
|
||||
|
||||
public readonly Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[] Props;
|
||||
private readonly Lazy<string[][]> GetProperties;
|
||||
|
||||
public ItemReflection()
|
||||
{
|
||||
public static ItemReflection Default { get; } = new();
|
||||
|
||||
public readonly Type[] Types = { typeof(Item), typeof(VillagerItem) };
|
||||
public readonly Dictionary<string, PropertyInfo>[] Props;
|
||||
public readonly string[][] Properties;
|
||||
|
||||
public ItemReflection()
|
||||
{
|
||||
Props = Types
|
||||
.Select(z => ReflectUtil.GetAllPropertyInfoPublic(z)
|
||||
.GroupBy(p => p.Name)
|
||||
.Select(g => g.First())
|
||||
.ToDictionary(p => p.Name))
|
||||
.ToArray();
|
||||
|
||||
Properties = GetPropArray();
|
||||
}
|
||||
|
||||
public string[][] GetPropArray()
|
||||
{
|
||||
var p = new string[Types.Length][];
|
||||
for (int i = 0; i < p.Length; i++)
|
||||
{
|
||||
var pz = ReflectUtil.GetPropertiesPublic(Types[i]);
|
||||
p[i] = pz.OrderBy(a => a).ToArray();
|
||||
}
|
||||
|
||||
// Properties for any
|
||||
var any = ReflectUtil.GetPropertiesPublic(typeof(Item)).Union(p.SelectMany(a => a)).OrderBy(a => a).ToArray();
|
||||
// Properties shared by all
|
||||
var all = p.Aggregate(new HashSet<string>(p[0]), (h, e) => { h.IntersectWith(e); return h; }).OrderBy(a => a).ToArray();
|
||||
|
||||
var p1 = new string[Types.Length + 2][];
|
||||
Array.Copy(p, 0, p1, 1, p.Length);
|
||||
p1[0] = any;
|
||||
p1[p1.Length - 1] = all;
|
||||
|
||||
return p1;
|
||||
}
|
||||
Props = GetPropertyDictionaries(Types);
|
||||
GetProperties = new Lazy<string[][]>(() => GetPropArray(Props, CustomProperties));
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[] GetPropertyDictionaries(ReadOnlySpan<Type> types)
|
||||
{
|
||||
var result = new Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[types.Length];
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
result[i] = GetPropertyDictionary(types[i], ReflectUtil.GetAllPropertyInfoPublic).GetAlternateLookup<ReadOnlySpan<char>>();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<string, PropertyInfo> GetPropertyDictionary(Type type, Func<Type, IEnumerable<PropertyInfo>> selector)
|
||||
{
|
||||
const int expectedMax = 8;
|
||||
var dict = new Dictionary<string, PropertyInfo>(expectedMax);
|
||||
var props = selector(type);
|
||||
foreach (var p in props)
|
||||
dict.TryAdd(p.Name, p);
|
||||
return dict;
|
||||
}
|
||||
|
||||
private static string[][] GetPropArray<T>(Dictionary<string, T>.AlternateLookup<ReadOnlySpan<char>>[] types, ReadOnlySpan<string> extra)
|
||||
{
|
||||
// Create a list for all types, [inAny, ..types, inAll]
|
||||
var result = new string[types.Length + 2][];
|
||||
var p = result.AsSpan(1, types.Length);
|
||||
|
||||
for (int i = 0; i < p.Length; i++)
|
||||
{
|
||||
var type = types[i].Dictionary;
|
||||
string[] combine = [.. type.Keys, .. extra];
|
||||
Array.Sort(combine);
|
||||
p[i] = combine;
|
||||
}
|
||||
|
||||
// Properties for any PKM
|
||||
// Properties shared by all PKM
|
||||
var first = p[0];
|
||||
var any = new HashSet<string>(first);
|
||||
var all = new HashSet<string>(first);
|
||||
foreach (var set in p[1..])
|
||||
{
|
||||
any.UnionWith(set);
|
||||
all.IntersectWith(set);
|
||||
}
|
||||
|
||||
var arrAny = any.ToArray();
|
||||
Array.Sort(arrAny);
|
||||
result[0] = arrAny;
|
||||
|
||||
var arrAll = all.ToArray();
|
||||
Array.Sort(arrAll);
|
||||
result[^1] = arrAll;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,27 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Batch Editor Modification result for an individual item.
|
||||
/// </summary>
|
||||
public enum ModifyResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Batch Editor Modification result for an individual item.
|
||||
/// The data has invalid data and is not a suitable candidate for modification.
|
||||
/// </summary>
|
||||
public enum ModifyResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The data has invalid data and is not a suitable candidate for modification.
|
||||
/// </summary>
|
||||
Invalid,
|
||||
Invalid,
|
||||
|
||||
/// <summary>
|
||||
/// An error was occurred while iterating modifications for this data.
|
||||
/// </summary>
|
||||
Error,
|
||||
/// <summary>
|
||||
/// An error was occurred while iterating modifications for this data.
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// The data was skipped due to a matching Filter.
|
||||
/// </summary>
|
||||
Filtered,
|
||||
/// <summary>
|
||||
/// The data was skipped due to a matching Filter.
|
||||
/// </summary>
|
||||
Filtered,
|
||||
|
||||
/// <summary>
|
||||
/// The data was modified.
|
||||
/// </summary>
|
||||
Modified,
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The data was modified.
|
||||
/// </summary>
|
||||
Modified,
|
||||
}
|
||||
|
|
@ -1,101 +1,375 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
using static NHSE.Core.InstructionComparer;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Batch Editing instruction
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be a filter (skip), or a modification instruction (modify)
|
||||
/// </remarks>
|
||||
/// <see cref="FilterNotEqual"/>
|
||||
/// <see cref="FilterEqual"/>
|
||||
/// <see cref="Apply"/>
|
||||
/// <param name="PropertyName">Property to modify.</param>
|
||||
/// <param name="PropertyValue">Value to set to the property.</param>
|
||||
/// <param name="Comparer">Filter Comparison Type</param>
|
||||
public sealed record StringInstruction(string PropertyName, string PropertyValue, InstructionComparer Comparer)
|
||||
{
|
||||
public string PropertyValue { get; private set; } = PropertyValue;
|
||||
|
||||
/// <summary>
|
||||
/// Batch Editing instruction
|
||||
/// Sets the <see cref="PropertyValue"/> to the index of the value in the input <see cref="arr"/>, if it exists.
|
||||
/// </summary>
|
||||
/// <param name="arr">List of values to search for the <see cref="PropertyValue"/>.</param>
|
||||
/// <returns>True if the value was found and set, false otherwise.</returns>
|
||||
public bool SetScreenedValue(ReadOnlySpan<string> arr)
|
||||
{
|
||||
int index = arr.IndexOf(PropertyValue);
|
||||
if ((uint)index >= arr.Length)
|
||||
return false;
|
||||
PropertyValue = index.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Valid prefixes that are recognized for <see cref="InstructionComparer"/> value comparison types.
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<char> Prefixes =>
|
||||
[
|
||||
Apply,
|
||||
FilterEqual, FilterNotEqual, FilterGreaterThan, FilterGreaterThanOrEqual, FilterLessThan, FilterLessThanOrEqual,
|
||||
];
|
||||
|
||||
private const char Apply = '.';
|
||||
private const char SplitRange = ',';
|
||||
|
||||
private const char FilterEqual = '=';
|
||||
private const char FilterNotEqual = '!';
|
||||
private const char FilterGreaterThan = '>';
|
||||
private const char FilterLessThan = '<';
|
||||
private const char FilterGreaterThanOrEqual = '≥';
|
||||
private const char FilterLessThanOrEqual = '≤';
|
||||
|
||||
/// <summary>
|
||||
/// Character which divides a property and a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be a filter (skip), or a modification instruction (modify)
|
||||
/// Example:
|
||||
/// =Species=1
|
||||
/// The second = is the split.
|
||||
/// </remarks>
|
||||
/// <see cref="Exclude"/>
|
||||
/// <see cref="Require"/>
|
||||
/// <see cref="Apply"/>
|
||||
public sealed class StringInstruction
|
||||
public const char SplitInstruction = '=';
|
||||
|
||||
// Extra Functionality
|
||||
private int RandomMinimum, RandomMaximum;
|
||||
|
||||
/// <summary>
|
||||
/// Apply a <see cref="RandomValue"/> instead of fixed value, based on the <see cref="RandomMinimum"/> and <see cref="RandomMaximum"/> values.
|
||||
/// </summary>
|
||||
public bool Random { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Random"/> value, based on the <see cref="RandomMinimum"/> and <see cref="RandomMaximum"/> values.
|
||||
/// </summary>
|
||||
public int RandomValue => RandUtil.Rand.Next(RandomMinimum, RandomMaximum + 1);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the input <see cref="str"/> is a valid "random range" specification.
|
||||
/// </summary>
|
||||
public static bool IsRandomRange(ReadOnlySpan<char> str)
|
||||
{
|
||||
public string PropertyName { get; }
|
||||
public string PropertyValue { get; private set; }
|
||||
public bool Evaluator { get; private init; }
|
||||
// Need at least one character on either side of the splitter char.
|
||||
int index = str.IndexOf(SplitRange);
|
||||
return index > 0 && index < str.Length - 1;
|
||||
}
|
||||
|
||||
public StringInstruction(string name, string value)
|
||||
/// <summary>
|
||||
/// Sets a "random range" specification to the instruction.
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">When the splitter is not present.</exception>
|
||||
public void SetRandomRange(ReadOnlySpan<char> str)
|
||||
{
|
||||
var index = str.IndexOf(SplitRange);
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(index);
|
||||
|
||||
var min = str[..index];
|
||||
var max = str[(index + 1)..];
|
||||
_ = int.TryParse(min, out RandomMinimum);
|
||||
_ = int.TryParse(max, out RandomMaximum);
|
||||
|
||||
if (RandomMinimum == RandomMaximum)
|
||||
{
|
||||
PropertyName = name;
|
||||
PropertyValue = value;
|
||||
PropertyValue = RandomMinimum.ToString();
|
||||
Debug.WriteLine($"{PropertyName} randomization range Min/Max same?");
|
||||
}
|
||||
|
||||
public void SetScreenedValue(string[] arr)
|
||||
else
|
||||
{
|
||||
int index = Array.IndexOf(arr, PropertyValue);
|
||||
PropertyValue = index > -1 ? index.ToString() : PropertyValue;
|
||||
}
|
||||
|
||||
public static readonly IReadOnlyList<char> Prefixes = new[] { Apply, Require, Exclude };
|
||||
private const char Exclude = '!';
|
||||
private const char Require = '=';
|
||||
private const char Apply = '.';
|
||||
private const char SplitRange = ',';
|
||||
|
||||
/// <summary>
|
||||
/// Character which divides a property and a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Example:
|
||||
/// =Species=1
|
||||
/// The second = is the split.
|
||||
/// </remarks>
|
||||
public const char SplitInstruction = '=';
|
||||
|
||||
// Extra Functionality
|
||||
private int RandomMinimum, RandomMaximum;
|
||||
public bool Random { get; private set; }
|
||||
public int RandomValue => RandUtil.Rand.Next(RandomMinimum, RandomMaximum + 1);
|
||||
|
||||
public void SetRandRange(string pv)
|
||||
{
|
||||
string str = pv.Substring(1);
|
||||
var split = str.Split(SplitRange);
|
||||
int.TryParse(split[0], out RandomMinimum);
|
||||
int.TryParse(split[1], out RandomMaximum);
|
||||
|
||||
if (RandomMinimum == RandomMaximum)
|
||||
{
|
||||
PropertyValue = RandomMinimum.ToString();
|
||||
Debug.WriteLine(PropertyName + " randomization range Min/Max same?");
|
||||
}
|
||||
else
|
||||
{
|
||||
Random = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<StringInstruction> GetFilters(IEnumerable<string> lines)
|
||||
{
|
||||
var raw = GetRelevantStrings(lines, Exclude, Require);
|
||||
return from line in raw
|
||||
let eval = line[0] == Require
|
||||
let split = line.Substring(1).Split(SplitInstruction)
|
||||
where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0])
|
||||
select new StringInstruction(split[0], split[1]) { Evaluator = eval };
|
||||
}
|
||||
|
||||
public static IEnumerable<StringInstruction> GetInstructions(IEnumerable<string> lines)
|
||||
{
|
||||
var raw = GetRelevantStrings(lines, Apply).Select(line => line.Substring(1));
|
||||
return from line in raw
|
||||
select line.Split(SplitInstruction) into split
|
||||
where split.Length == 2
|
||||
select new StringInstruction(split[0], split[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Weeds out invalid lines and only returns those with a valid first character.
|
||||
/// </summary>
|
||||
private static IEnumerable<string> GetRelevantStrings(IEnumerable<string> lines, params char[] pieces)
|
||||
{
|
||||
return lines.Where(line => !string.IsNullOrEmpty(line) && pieces.Any(z => z == line[0]));
|
||||
Random = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/>s from the input <see cref="text"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetFilters(ReadOnlySpan<char> text) => GetFilters(text.EnumerateLines());
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetFilters(ReadOnlySpan<string> lines)
|
||||
{
|
||||
var result = new List<StringInstruction>(lines.Length);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseFilter(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetFilters(SpanLineEnumerator lines)
|
||||
{
|
||||
var result = new List<StringInstruction>();
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseFilter(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetFilters(IReadOnlyList<string> lines)
|
||||
{
|
||||
var result = new List<StringInstruction>(lines.Count);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseFilter(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> filters from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetFilters(IEnumerable<string> lines)
|
||||
{
|
||||
var result = new List<StringInstruction>();
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseFilter(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="text"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetInstructions(ReadOnlySpan<char> text) => GetInstructions(text.EnumerateLines());
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetInstructions(ReadOnlySpan<string> lines)
|
||||
{
|
||||
var result = new List<StringInstruction>(lines.Length);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseInstruction(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetInstructions(SpanLineEnumerator lines)
|
||||
{
|
||||
var result = new List<StringInstruction>();
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseInstruction(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetInstructions(IReadOnlyList<string> lines)
|
||||
{
|
||||
var result = new List<StringInstruction>(lines.Count);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseInstruction(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstruction"/> instructions from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static List<StringInstruction> GetInstructions(IEnumerable<string> lines)
|
||||
{
|
||||
var result = new List<StringInstruction>();
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (TryParseInstruction(line, out var entry))
|
||||
result.Add(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse a <see cref="StringInstruction"/> filter from the input <see cref="line"/>.
|
||||
/// </summary>
|
||||
public static bool TryParseFilter(ReadOnlySpan<char> line, [NotNullWhen(true)] out StringInstruction? entry)
|
||||
{
|
||||
entry = null;
|
||||
if (line.Length is 0)
|
||||
return false;
|
||||
var comparer = GetComparer(line[0]);
|
||||
if (!comparer.IsSupported)
|
||||
return false;
|
||||
return TryParseSplitTuple(line[1..], ref entry, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse a <see cref="StringInstruction"/> instruction from the input <see cref="line"/>.
|
||||
/// </summary>
|
||||
public static bool TryParseInstruction(ReadOnlySpan<char> line, [NotNullWhen(true)] out StringInstruction? entry)
|
||||
{
|
||||
entry = null;
|
||||
if (!line.StartsWith(Apply))
|
||||
return false;
|
||||
return TryParseSplitTuple(line[1..], ref entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to split a <see cref="StringInstruction"/> tuple from the input <see cref="tuple"/>.
|
||||
/// </summary>
|
||||
public static bool TryParseSplitTuple(ReadOnlySpan<char> tuple, [NotNullWhen(true)] ref StringInstruction? entry, InstructionComparer eval = default)
|
||||
{
|
||||
if (!TryParseSplitTuple(tuple, out var name, out var value))
|
||||
return false;
|
||||
entry = new StringInstruction(name.ToString(), value.ToString(), eval);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to split a <see cref="StringInstruction"/> tuple from the input <see cref="tuple"/>.
|
||||
/// </summary>
|
||||
public static bool TryParseSplitTuple(ReadOnlySpan<char> tuple, out ReadOnlySpan<char> name, out ReadOnlySpan<char> value)
|
||||
{
|
||||
name = default;
|
||||
value = default;
|
||||
var splitIndex = tuple.IndexOf(SplitInstruction);
|
||||
if (splitIndex <= 0)
|
||||
return false;
|
||||
|
||||
name = tuple[..splitIndex];
|
||||
if (name.IsWhiteSpace())
|
||||
return false;
|
||||
|
||||
value = tuple[(splitIndex + 1)..];
|
||||
var noExtra = value.IndexOf(SplitInstruction);
|
||||
return noExtra == -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="InstructionComparer"/> from the input <see cref="opCode"/>.
|
||||
/// </summary>
|
||||
public static InstructionComparer GetComparer(char opCode) => opCode switch
|
||||
{
|
||||
FilterEqual => IsEqual,
|
||||
FilterNotEqual => IsNotEqual,
|
||||
FilterGreaterThan => IsGreaterThan,
|
||||
FilterLessThan => IsLessThan,
|
||||
FilterGreaterThanOrEqual => IsGreaterThanOrEqual,
|
||||
FilterLessThanOrEqual => IsLessThanOrEqual,
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Value comparison type
|
||||
/// </summary>
|
||||
public enum InstructionComparer : byte
|
||||
{
|
||||
None,
|
||||
IsEqual,
|
||||
IsNotEqual,
|
||||
IsGreaterThan,
|
||||
IsGreaterThanOrEqual,
|
||||
IsLessThan,
|
||||
IsLessThanOrEqual,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="InstructionComparer"/>
|
||||
/// </summary>
|
||||
public static class InstructionComparerExtensions
|
||||
{
|
||||
extension(InstructionComparer comparer)
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the <see cref="comparer"/> is supported by the logic.
|
||||
/// </summary>
|
||||
/// <returns>True if supported, false if unsupported.</returns>
|
||||
public bool IsSupported => comparer switch
|
||||
{
|
||||
IsEqual => true,
|
||||
IsNotEqual => true,
|
||||
IsGreaterThan => true,
|
||||
IsGreaterThanOrEqual => true,
|
||||
IsLessThan => true,
|
||||
IsLessThanOrEqual => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the compare operator is satisfied by a boolean comparison result.
|
||||
/// </summary>
|
||||
/// <param name="compareResult">Result from Equals comparison</param>
|
||||
/// <returns>True if satisfied</returns>
|
||||
/// <remarks>Only use this method if the comparison is boolean only. Use the <see cref="IsCompareOperator"/> otherwise.</remarks>
|
||||
public bool IsCompareEquivalence(bool compareResult) => comparer switch
|
||||
{
|
||||
IsEqual => compareResult,
|
||||
IsNotEqual => !compareResult,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the compare operator is satisfied by the <see cref="IComparable.CompareTo"/> result.
|
||||
/// </summary>
|
||||
/// <param name="compareResult">Result from CompareTo</param>
|
||||
/// <returns>True if satisfied</returns>
|
||||
public bool IsCompareOperator(int compareResult) => comparer switch
|
||||
{
|
||||
IsEqual => compareResult is 0,
|
||||
IsNotEqual => compareResult is not 0,
|
||||
IsGreaterThan => compareResult > 0,
|
||||
IsGreaterThanOrEqual => compareResult >= 0,
|
||||
IsLessThan => compareResult < 0,
|
||||
IsLessThanOrEqual => compareResult <= 0,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +1,143 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Processes input of strings into a list of valid Filters and Instructions.
|
||||
/// </summary>
|
||||
public sealed class StringInstructionSet
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes input of strings into a list of valid Filters and Instructions.
|
||||
/// Filters to check if the object should be modified.
|
||||
/// </summary>
|
||||
public sealed class StringInstructionSet
|
||||
public readonly IReadOnlyList<StringInstruction> Filters;
|
||||
|
||||
/// <summary>
|
||||
/// Instructions to modify the object.
|
||||
/// </summary>
|
||||
public readonly IReadOnlyList<StringInstruction> Instructions;
|
||||
|
||||
private const char SetSeparatorChar = ';';
|
||||
|
||||
public StringInstructionSet(IReadOnlyList<StringInstruction> filters, IReadOnlyList<StringInstruction> instructions)
|
||||
{
|
||||
public readonly IReadOnlyList<StringInstruction> Filters;
|
||||
public readonly IReadOnlyList<StringInstruction> Instructions;
|
||||
|
||||
private const string SetSeparator = ";";
|
||||
|
||||
public StringInstructionSet(IReadOnlyList<StringInstruction> filters, IReadOnlyList<StringInstruction> instructions)
|
||||
{
|
||||
Filters = filters;
|
||||
Instructions = instructions;
|
||||
}
|
||||
|
||||
public StringInstructionSet(ICollection<string> set)
|
||||
{
|
||||
Filters = StringInstruction.GetFilters(set).ToList();
|
||||
Instructions = StringInstruction.GetInstructions(set).ToList();
|
||||
}
|
||||
|
||||
public static IEnumerable<StringInstructionSet> GetBatchSets(IList<string> lines)
|
||||
{
|
||||
int start = 0;
|
||||
while (start < lines.Count)
|
||||
{
|
||||
var list = lines.Skip(start).TakeWhile(_ => !lines[start++].StartsWith(SetSeparator)).ToList();
|
||||
yield return new StringInstructionSet(list);
|
||||
}
|
||||
}
|
||||
Filters = filters;
|
||||
Instructions = instructions;
|
||||
}
|
||||
}
|
||||
|
||||
public StringInstructionSet(ReadOnlySpan<char> text)
|
||||
{
|
||||
var set = text.EnumerateLines();
|
||||
Filters = StringInstruction.GetFilters(set);
|
||||
Instructions = StringInstruction.GetInstructions(set);
|
||||
}
|
||||
|
||||
public StringInstructionSet(SpanLineEnumerator set)
|
||||
{
|
||||
Filters = StringInstruction.GetFilters(set);
|
||||
Instructions = StringInstruction.GetInstructions(set);
|
||||
}
|
||||
|
||||
public StringInstructionSet(ReadOnlySpan<string> set)
|
||||
{
|
||||
Filters = StringInstruction.GetFilters(set);
|
||||
Instructions = StringInstruction.GetInstructions(set);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstructionSet"/>s from the input <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static StringInstructionSet[] GetBatchSets(ReadOnlySpan<string> lines)
|
||||
{
|
||||
int ctr = 0;
|
||||
int start = 0;
|
||||
while (start < lines.Length)
|
||||
{
|
||||
var slice = lines[start..];
|
||||
var count = GetInstructionSetLength(slice);
|
||||
ctr++;
|
||||
start += count + 1;
|
||||
}
|
||||
|
||||
var result = new StringInstructionSet[ctr];
|
||||
ctr = 0;
|
||||
start = 0;
|
||||
while (start < lines.Length)
|
||||
{
|
||||
var slice = lines[start..];
|
||||
var count = GetInstructionSetLength(slice);
|
||||
var set = slice[..count];
|
||||
result[ctr++] = new StringInstructionSet(set);
|
||||
start += count + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="StringInstructionSet"/>s from the input <see cref="text"/>.
|
||||
/// </summary>
|
||||
public static StringInstructionSet[] GetBatchSets(ReadOnlySpan<char> text)
|
||||
{
|
||||
int ctr = 0;
|
||||
int start = 0;
|
||||
while (start < text.Length)
|
||||
{
|
||||
var slice = text[start..];
|
||||
var count = GetInstructionSetLength(slice);
|
||||
ctr++;
|
||||
start += count + 1;
|
||||
}
|
||||
|
||||
var result = new StringInstructionSet[ctr];
|
||||
ctr = 0;
|
||||
start = 0;
|
||||
while (start < text.Length)
|
||||
{
|
||||
var slice = text[start..];
|
||||
var count = GetInstructionSetLength(slice);
|
||||
var set = slice[..count];
|
||||
result[ctr++] = new StringInstructionSet(set);
|
||||
start += count + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scans through the <see cref="text"/> to count the amount of characters to consume.
|
||||
/// </summary>
|
||||
/// <param name="text">Multi line string</param>
|
||||
/// <returns>Amount of characters comprising a set of instructions</returns>
|
||||
public static int GetInstructionSetLength(ReadOnlySpan<char> text)
|
||||
{
|
||||
int start = 0;
|
||||
while (start < text.Length)
|
||||
{
|
||||
var line = text[start..];
|
||||
if (line.Length != 0 && line[0] == SetSeparatorChar)
|
||||
return start;
|
||||
var next = line.IndexOf('\n');
|
||||
if (next == -1)
|
||||
return text.Length;
|
||||
start += next + 1;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scans through the <see cref="lines"/> to count the amount of valid lines to consume.
|
||||
/// </summary>
|
||||
/// <returns>Amount of lines comprising a set of instructions.</returns>
|
||||
public static int GetInstructionSetLength(ReadOnlySpan<string> lines)
|
||||
{
|
||||
int start = 0;
|
||||
while (start < lines.Length)
|
||||
{
|
||||
var line = lines[start++];
|
||||
if (line.StartsWith(SetSeparatorChar))
|
||||
return start;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +1,23 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed class FieldItemColumn
|
||||
{
|
||||
public sealed class FieldItemColumn
|
||||
/// <summary> X Coordinate within the Field Item Layer </summary>
|
||||
public readonly int X;
|
||||
|
||||
/// <summary> Y Coordinate within the Field Item Layer </summary>
|
||||
public readonly int Y;
|
||||
|
||||
/// <summary> Offset relative to the start of the Field Item Layer </summary>
|
||||
public readonly int Offset;
|
||||
|
||||
public readonly byte[] Data;
|
||||
|
||||
public FieldItemColumn(int x, int y, int offset, byte[] data)
|
||||
{
|
||||
/// <summary> X Coordinate within the Field Item Layer </summary>
|
||||
public readonly int X;
|
||||
|
||||
/// <summary> Y Coordinate within the Field Item Layer </summary>
|
||||
public readonly int Y;
|
||||
|
||||
/// <summary> Offset relative to the start of the Field Item Layer </summary>
|
||||
public readonly int Offset;
|
||||
|
||||
public readonly byte[] Data;
|
||||
|
||||
public FieldItemColumn(int x, int y, int offset, byte[] data)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Offset = offset;
|
||||
Data = data;
|
||||
}
|
||||
X = x;
|
||||
Y = y;
|
||||
Offset = offset;
|
||||
Data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +1,126 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="Item"/> into columns of writable Item tiles.
|
||||
/// </summary>
|
||||
public static class FieldItemDropper
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts <see cref="Item"/> into columns of writable Item tiles.
|
||||
/// </summary>
|
||||
public static class FieldItemDropper
|
||||
private const int MapHeight = FieldItemLayer.FieldItemHeight;
|
||||
private const int MapWidth = FieldItemLayer.FieldItemWidth;
|
||||
|
||||
// Each dropped item is a 2x2 square, with the top left tile being the root node, and the other 3 being extensions pointing back to the root.
|
||||
|
||||
/// <inheritdoc cref="CanFitDropped(int,int,int,int,int,int,int,int)"/>
|
||||
/// <param name="x">Raw X coordinate for the top-left item root tile.</param>
|
||||
/// <param name="y">Raw Y coordinate for the top-left item root tile.</param>
|
||||
/// <param name="totalCount">Total count of items to be dropped (not tiles).</param>
|
||||
/// <param name="yCount">Count of items tall the overall spawn-rectangle is.</param>
|
||||
/// <param name="borderX">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="borderY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int borderX, int borderY)
|
||||
{
|
||||
private const int MapHeight = FieldItemLayer.FieldItemHeight;
|
||||
private const int MapWidth = FieldItemLayer.FieldItemWidth;
|
||||
|
||||
// Each dropped item is a 2x2 square, with the top left tile being the root node, and the other 3 being extensions pointing back to the root.
|
||||
|
||||
/// <inheritdoc cref="CanFitDropped(int,int,int,int,int,int,int,int)"/>
|
||||
/// <param name="x">Raw X coordinate for the top-left item root tile.</param>
|
||||
/// <param name="y">Raw Y coordinate for the top-left item root tile.</param>
|
||||
/// <param name="totalCount">Total count of items to be dropped (not tiles).</param>
|
||||
/// <param name="yCount">Count of items tall the overall spawn-rectangle is.</param>
|
||||
/// <param name="borderX">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="borderY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int borderX, int borderY)
|
||||
{
|
||||
return CanFitDropped(x, y, totalCount, yCount, borderX, borderX, borderY, borderY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the requested <see cref="totalCount"/> of items can be dropped on the field item layer. Does not check terrain or existing items.
|
||||
/// </summary>
|
||||
/// <remarks>Coordinates should be 32x32 style instead of 16x16.</remarks>
|
||||
/// <param name="x">Raw X coordinate for the top-left item root tile.</param>
|
||||
/// <param name="y">Raw Y coordinate for the top-left item root tile.</param>
|
||||
/// <param name="totalCount">Total count of items to be dropped (not tiles).</param>
|
||||
/// <param name="yCount">Count of items tall the overall spawn-rectangle is.</param>
|
||||
/// <param name="leftX">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="rightX">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="topY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="botY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <returns>True if can fit, false if not.</returns>
|
||||
public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int leftX, int rightX, int topY, int botY)
|
||||
{
|
||||
var xCount = totalCount / yCount;
|
||||
if (x < leftX || (x + (xCount * 2)) > MapWidth - rightX)
|
||||
return false;
|
||||
if (y < topY || (y + (yCount * 2)) > MapHeight - botY)
|
||||
return false;
|
||||
|
||||
return totalCount < (MapHeight * MapWidth / 32);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item)
|
||||
{
|
||||
int yStride = (item.Count > 16) ? 16 : item.Count;
|
||||
return InjectItemsAsDropped(mapX, mapY, item, yStride);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item, int yStride)
|
||||
{
|
||||
var xStride = item.Count / yStride;
|
||||
List<FieldItemColumn> result = new(yStride * xStride);
|
||||
for (int i = 0; i < xStride; i++)
|
||||
{
|
||||
var x = mapX + (i * 2);
|
||||
var y = mapY;
|
||||
var itemSlice = item.Skip(i * yStride).Take(yStride).ToArray();
|
||||
|
||||
// Root+ExtensionY
|
||||
var offset = GetTileOffset(x, y);
|
||||
var data = GetColumnRoot(itemSlice);
|
||||
var column = new FieldItemColumn(x, y, offset, data);
|
||||
result.Add(column);
|
||||
|
||||
// Ex X/XY
|
||||
++x;
|
||||
offset = GetTileOffset(x, y);
|
||||
data = GetColumnExtension(itemSlice);
|
||||
column = new FieldItemColumn(x, y, offset, data);
|
||||
result.Add(column);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static byte[] GetColumnRoot(Item[] items)
|
||||
{
|
||||
var col = new Item[items.Length * 2];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var idx = i * 2;
|
||||
col[idx] = GetDroppedItem(item);
|
||||
col[idx + 1] = GetExtension(item, 0, 1);
|
||||
}
|
||||
return Item.SetArray(col);
|
||||
}
|
||||
|
||||
private static byte[] GetColumnExtension(Item[] items)
|
||||
{
|
||||
var col = new Item[items.Length * 2];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var idx = i * 2;
|
||||
col[idx] = GetExtension(item, 1, 0);
|
||||
col[idx + 1] = GetExtension(item, 1, 1);
|
||||
}
|
||||
return Item.SetArray(col);
|
||||
}
|
||||
|
||||
private static int GetTileOffset(int x, int y)
|
||||
{
|
||||
return Item.SIZE * (y + (x * MapHeight));
|
||||
}
|
||||
|
||||
private static Item GetDroppedItem(Item item)
|
||||
{
|
||||
var copy = new Item();
|
||||
copy.CopyFrom(item);
|
||||
copy.ClearFlags();
|
||||
copy.IsDropped = true;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static Item GetExtension(Item item, byte x, byte y) => new(Item.EXTENSION) { ExtensionItemId = item.ItemId, ExtensionX = x, ExtensionY = y };
|
||||
return CanFitDropped(x, y, totalCount, yCount, borderX, borderX, borderY, borderY);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the requested <see cref="totalCount"/> of items can be dropped on the field item layer. Does not check terrain or existing items.
|
||||
/// </summary>
|
||||
/// <remarks>Coordinates should be 32x32 style instead of 16x16.</remarks>
|
||||
/// <param name="x">Raw X coordinate for the top-left item root tile.</param>
|
||||
/// <param name="y">Raw Y coordinate for the top-left item root tile.</param>
|
||||
/// <param name="totalCount">Total count of items to be dropped (not tiles).</param>
|
||||
/// <param name="yCount">Count of items tall the overall spawn-rectangle is.</param>
|
||||
/// <param name="leftX">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="rightX">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="topY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <param name="botY">Excluded outer tile count. Useful for enforcing that beach acre tiles are skipped.</param>
|
||||
/// <returns>True if can fit, false if not.</returns>
|
||||
public static bool CanFitDropped(int x, int y, int totalCount, int yCount, int leftX, int rightX, int topY, int botY)
|
||||
{
|
||||
var xCount = totalCount / yCount;
|
||||
if (x < leftX || (x + (xCount * 2)) > MapWidth - rightX)
|
||||
return false;
|
||||
if (y < topY || (y + (yCount * 2)) > MapHeight - botY)
|
||||
return false;
|
||||
|
||||
return totalCount < (MapHeight * MapWidth / 32);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item)
|
||||
{
|
||||
int yStride = (item.Count > 16) ? 16 : item.Count;
|
||||
return InjectItemsAsDropped(mapX, mapY, item, yStride);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<FieldItemColumn> InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList<Item> item, int yStride)
|
||||
{
|
||||
var xStride = item.Count / yStride;
|
||||
List<FieldItemColumn> result = new(yStride * xStride);
|
||||
for (int i = 0; i < xStride; i++)
|
||||
{
|
||||
var x = mapX + (i * 2);
|
||||
var y = mapY;
|
||||
var itemSlice = item.Skip(i * yStride).Take(yStride).ToArray();
|
||||
|
||||
// Root+ExtensionY
|
||||
var offset = GetTileOffset(x, y);
|
||||
var data = GetColumnRoot(itemSlice);
|
||||
var column = new FieldItemColumn(x, y, offset, data);
|
||||
result.Add(column);
|
||||
|
||||
// Ex X/XY
|
||||
++x;
|
||||
offset = GetTileOffset(x, y);
|
||||
data = GetColumnExtension(itemSlice);
|
||||
column = new FieldItemColumn(x, y, offset, data);
|
||||
result.Add(column);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static byte[] GetColumnRoot(ReadOnlySpan<Item> items)
|
||||
{
|
||||
var col = new Item[items.Length * 2];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var idx = i * 2;
|
||||
col[idx] = GetDroppedItem(item);
|
||||
col[idx + 1] = GetExtension(item, 0, 1);
|
||||
}
|
||||
return Item.SetArray(col);
|
||||
}
|
||||
|
||||
private static byte[] GetColumnExtension(ReadOnlySpan<Item> items)
|
||||
{
|
||||
var col = new Item[items.Length * 2];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var idx = i * 2;
|
||||
col[idx] = GetExtension(item, 1, 0);
|
||||
col[idx + 1] = GetExtension(item, 1, 1);
|
||||
}
|
||||
return Item.SetArray(col);
|
||||
}
|
||||
|
||||
private static int GetTileOffset(int x, int y)
|
||||
{
|
||||
return Item.SIZE * (y + (x * MapHeight));
|
||||
}
|
||||
|
||||
private static Item GetDroppedItem(Item item)
|
||||
{
|
||||
var copy = new Item();
|
||||
copy.CopyFrom(item);
|
||||
copy.ClearFlags();
|
||||
copy.IsDropped = true;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static Item GetExtension(Item item, byte x, byte y) => new(Item.EXTENSION) { ExtensionItemId = item.ItemId, ExtensionX = x, ExtensionY = y };
|
||||
}
|
||||
|
|
@ -1,135 +1,135 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Handles operations for parsing the player inventory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Player Inventory is comprised of multiple values, which we won't bother reimplementing as a convertible data structure.
|
||||
/// <br>Refer to GSavePlayerItemBaggage's ItemBag & ItemPocket in the dumped c-structure schema.</br>
|
||||
/// </remarks>
|
||||
public static class PlayerItemSet
|
||||
{
|
||||
private const int ItemSet_Quantity = 2; // Pouch (Bag) & Pocket.
|
||||
private const int ItemSet_ItemCount = 20; // 20 items per item set.
|
||||
private const int ItemSet_ItemSize = Item.SIZE * ItemSet_ItemCount;
|
||||
private const int ItemSet_MetaSize = 4 + ItemSet_ItemCount;
|
||||
private const int ItemSet_TotalSize = (ItemSet_ItemSize + ItemSet_MetaSize) * ItemSet_Quantity;
|
||||
private const int ShiftToTopOfStructure = -ItemSet_MetaSize - (Item.SIZE * ItemSet_ItemCount); // shifts slot1 offset => top of data structure
|
||||
|
||||
/// <summary>
|
||||
/// Handles operations for parsing the player inventory.
|
||||
/// Gets the Offset and Size to read from based on the Item 1 RAM offset.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Player Inventory is comprised of multiple values, which we won't bother reimplementing as a convertible data structure.
|
||||
/// <br>Refer to GSavePlayerItemBaggage's ItemBag & ItemPocket in the dumped c-structure schema.</br>
|
||||
/// </remarks>
|
||||
public static class PlayerItemSet
|
||||
/// <param name="slot1">Item Slot 1 offset in RAM</param>
|
||||
/// <param name="offset">Offset to read from</param>
|
||||
/// <param name="length">Length of data to read from <see cref="offset"/></param>
|
||||
public static void GetOffsetLength(uint slot1, out uint offset, out int length)
|
||||
{
|
||||
private const int ItemSet_Quantity = 2; // Pouch (Bag) & Pocket.
|
||||
private const int ItemSet_ItemCount = 20; // 20 items per item set.
|
||||
private const int ItemSet_ItemSize = Item.SIZE * ItemSet_ItemCount;
|
||||
private const int ItemSet_MetaSize = 4 + ItemSet_ItemCount;
|
||||
private const int ItemSet_TotalSize = (ItemSet_ItemSize + ItemSet_MetaSize) * ItemSet_Quantity;
|
||||
private const int ShiftToTopOfStructure = -ItemSet_MetaSize - (Item.SIZE * ItemSet_ItemCount); // shifts slot1 offset => top of data structure
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Offset and Size to read from based on the Item 1 RAM offset.
|
||||
/// </summary>
|
||||
/// <param name="slot1">Item Slot 1 offset in RAM</param>
|
||||
/// <param name="offset">Offset to read from</param>
|
||||
/// <param name="length">Length of data to read from <see cref="offset"/></param>
|
||||
public static void GetOffsetLength(uint slot1, out uint offset, out int length)
|
||||
{
|
||||
offset = (uint)((int)slot1 + ShiftToTopOfStructure);
|
||||
length = ItemSet_TotalSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the raw data to the expected data layout.
|
||||
/// </summary>
|
||||
/// <param name="data">Raw RAM from the game from the offset read (as per <see cref="GetOffsetLength"/>).</param>
|
||||
/// <returns>True if valid, false if not valid or corrupt.</returns>
|
||||
public static bool ValidateItemBinary(byte[] data)
|
||||
{
|
||||
// Check the unlocked slot count -- expect 0,10,20
|
||||
var bagCount = BitConverter.ToUInt32(data, ItemSet_ItemSize);
|
||||
if (bagCount > ItemSet_ItemCount || bagCount % 10 != 0) // pouch21-39 count
|
||||
return false;
|
||||
|
||||
var pocketCount = BitConverter.ToUInt32(data, ItemSet_ItemSize + ItemSet_MetaSize + ItemSet_ItemSize);
|
||||
if (pocketCount != ItemSet_ItemCount) // pouch0-19 count should be 20.
|
||||
return false;
|
||||
|
||||
// Check the item wheel binding -- expect -1 or [0,7]
|
||||
// Disallow duplicate binds!
|
||||
// Don't bother checking that bind[i] (when ! -1) is not NONE at items[i]. We don't need to check everything!
|
||||
var bound = new List<byte>();
|
||||
if (!ValidateBindList(data, ItemSet_ItemSize + 4, bound))
|
||||
return false;
|
||||
if (!ValidateBindList(data, ItemSet_ItemSize + 4 + (ItemSet_ItemSize + ItemSet_MetaSize), bound))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ValidateBindList(byte[] data, int bindStart, ICollection<byte> bound)
|
||||
{
|
||||
for (int i = 0; i < ItemSet_ItemCount; i++)
|
||||
{
|
||||
var bind = data[bindStart + i];
|
||||
if (bind == 0xFF) // Not bound
|
||||
continue;
|
||||
if (bind > 7) // Only [0,7] permitted as the wheel has 8 spots
|
||||
return false;
|
||||
if (bound.Contains(bind)) // Wheel index is already bound to another item slot
|
||||
return false;
|
||||
|
||||
bound.Add(bind);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the items present in the player inventory packet and returns the list of items.
|
||||
/// </summary>
|
||||
/// <param name="data">Player Inventory packet</param>
|
||||
public static Item[] ReadPlayerInventory(byte[] data)
|
||||
{
|
||||
var items = GetEmptyItemArray(40);
|
||||
ReadPlayerInventory(data, items);
|
||||
return items;
|
||||
}
|
||||
|
||||
private static Item[] GetEmptyItemArray(int count)
|
||||
{
|
||||
var items = new Item[count];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
items[i] = new Item();
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the items present in the player inventory packet into the <see cref="destination"/> list of items.
|
||||
/// </summary>
|
||||
/// <param name="data">Player Inventory packet</param>
|
||||
/// <param name="destination">40 Item array</param>
|
||||
public static void ReadPlayerInventory(byte[] data, IReadOnlyList<Item> destination)
|
||||
{
|
||||
var pocket2 = destination.Take(20).ToArray();
|
||||
var pocket1 = destination.Skip(20).ToArray();
|
||||
var p1 = Item.GetArray(data.Slice(0, ItemSet_ItemSize));
|
||||
var p2 = Item.GetArray(data.Slice(ItemSet_ItemSize + 0x18, ItemSet_ItemSize));
|
||||
|
||||
for (int i = 0; i < pocket1.Length; i++)
|
||||
pocket1[i].CopyFrom(p1[i]);
|
||||
|
||||
for (int i = 0; i < pocket2.Length; i++)
|
||||
pocket2[i].CopyFrom(p2[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the items in the <see cref="source"/> list of items to the player inventory packet.
|
||||
/// </summary>
|
||||
/// <param name="data">Player Inventory packet</param>
|
||||
/// <param name="source">40 Item array</param>
|
||||
public static void WritePlayerInventory(byte[] data, IReadOnlyList<Item> source)
|
||||
{
|
||||
var pocket2 = source.Take(20).ToArray();
|
||||
var pocket1 = source.Skip(20).ToArray();
|
||||
var p1 = Item.SetArray(pocket1);
|
||||
var p2 = Item.SetArray(pocket2);
|
||||
|
||||
p1.CopyTo(data, 0);
|
||||
p2.CopyTo(data, ItemSet_ItemSize + 0x18);
|
||||
}
|
||||
offset = (uint)((int)slot1 + ShiftToTopOfStructure);
|
||||
length = ItemSet_TotalSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the raw data to the expected data layout.
|
||||
/// </summary>
|
||||
/// <param name="data">Raw RAM from the game from the offset read (as per <see cref="GetOffsetLength"/>).</param>
|
||||
/// <returns>True if valid, false if not valid or corrupt.</returns>
|
||||
public static bool ValidateItemBinary(ReadOnlySpan<byte> data)
|
||||
{
|
||||
// Check the unlocked slot count -- expect 0,10,20
|
||||
var bagCount = ReadUInt32LittleEndian(data[ItemSet_ItemSize..]);
|
||||
if (bagCount > ItemSet_ItemCount || bagCount % 10 != 0) // pouch21-39 count
|
||||
return false;
|
||||
|
||||
var pocketCount = ReadUInt32LittleEndian(data[(ItemSet_ItemSize + ItemSet_MetaSize + ItemSet_ItemSize)..]);
|
||||
if (pocketCount != ItemSet_ItemCount) // pouch0-19 count should be 20.
|
||||
return false;
|
||||
|
||||
// Check the item wheel binding -- expect -1 or [0,7]
|
||||
// Disallow duplicate binds!
|
||||
// Don't bother checking that bind[i] (when ! -1) is not NONE at items[i]. We don't need to check everything!
|
||||
var bound = new List<byte>();
|
||||
if (!ValidateBindList(data, ItemSet_ItemSize + 4, bound))
|
||||
return false;
|
||||
if (!ValidateBindList(data, ItemSet_ItemSize + 4 + (ItemSet_ItemSize + ItemSet_MetaSize), bound))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ValidateBindList(ReadOnlySpan<byte> data, int bindStart, ICollection<byte> bound)
|
||||
{
|
||||
for (int i = 0; i < ItemSet_ItemCount; i++)
|
||||
{
|
||||
var bind = data[bindStart + i];
|
||||
if (bind == 0xFF) // Not bound
|
||||
continue;
|
||||
if (bind > 7) // Only [0,7] permitted as the wheel has 8 spots
|
||||
return false;
|
||||
if (bound.Contains(bind)) // Wheel index is already bound to another item slot
|
||||
return false;
|
||||
|
||||
bound.Add(bind);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the items present in the player inventory packet and returns the list of items.
|
||||
/// </summary>
|
||||
/// <param name="data">Player Inventory packet</param>
|
||||
public static Item[] ReadPlayerInventory(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var items = GetEmptyItemArray(40);
|
||||
ReadPlayerInventory(data, items);
|
||||
return items;
|
||||
}
|
||||
|
||||
private static Item[] GetEmptyItemArray(int count)
|
||||
{
|
||||
var items = new Item[count];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
items[i] = new Item();
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the items present in the player inventory packet into the <see cref="destination"/> list of items.
|
||||
/// </summary>
|
||||
/// <param name="data">Player Inventory packet</param>
|
||||
/// <param name="destination">40 Item array</param>
|
||||
public static void ReadPlayerInventory(ReadOnlySpan<byte> data, IReadOnlyList<Item> destination)
|
||||
{
|
||||
var pocket2 = destination.Take(20).ToArray();
|
||||
var pocket1 = destination.Skip(20).ToArray();
|
||||
var p1 = Item.GetArray(data[..ItemSet_ItemSize]);
|
||||
var p2 = Item.GetArray(data.Slice(ItemSet_ItemSize + 0x18, ItemSet_ItemSize));
|
||||
|
||||
for (int i = 0; i < pocket1.Length; i++)
|
||||
pocket1[i].CopyFrom(p1[i]);
|
||||
|
||||
for (int i = 0; i < pocket2.Length; i++)
|
||||
pocket2[i].CopyFrom(p2[i]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the items in the <see cref="source"/> list of items to the player inventory packet.
|
||||
/// </summary>
|
||||
/// <param name="data">Player Inventory packet</param>
|
||||
/// <param name="source">40 Item array</param>
|
||||
public static void WritePlayerInventory(Span<byte> data, IReadOnlyList<Item> source)
|
||||
{
|
||||
var pocket2 = source.Take(20).ToArray();
|
||||
var pocket1 = source.Skip(20).ToArray();
|
||||
var p1 = Item.SetArray(pocket1);
|
||||
var p2 = Item.SetArray(pocket2);
|
||||
|
||||
p1.CopyTo(data);
|
||||
p2.CopyTo(data[(ItemSet_ItemSize + 0x18)..]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Interface describing how items should be configured prior to being dropped by the player.
|
||||
/// </summary>
|
||||
public interface IConfigItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface describing how items should be configured prior to being dropped by the player.
|
||||
/// Checks if the item should have wrapping paper applied.
|
||||
/// </summary>
|
||||
public interface IConfigItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the item should have wrapping paper applied.
|
||||
/// </summary>
|
||||
bool WrapAllItems { get; }
|
||||
bool WrapAllItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Wrapping paper type applied if <see cref="WrapAllItems"/> is set.
|
||||
/// </summary>
|
||||
ItemWrappingPaper WrappingPaper { get; }
|
||||
/// <summary>
|
||||
/// Wrapping paper type applied if <see cref="WrapAllItems"/> is set.
|
||||
/// </summary>
|
||||
ItemWrappingPaper WrappingPaper { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Drop Compatibility check should be skipped.
|
||||
/// </summary>
|
||||
bool SkipDropCheck { get; }
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks if the Drop Compatibility check should be skipped.
|
||||
/// </summary>
|
||||
bool SkipDropCheck { get; }
|
||||
}
|
||||
|
|
@ -2,383 +2,385 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for retrieving <see cref="Item"/> details based off input strings.
|
||||
/// </summary>
|
||||
public static class ItemParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for retrieving <see cref="Item"/> details based off input strings.
|
||||
/// Invert the recipe dictionary so we can look up recipe IDs from an input item ID.
|
||||
/// </summary>
|
||||
public static class ItemParser
|
||||
public static readonly IReadOnlyDictionary<ushort, ushort> InvertedRecipeDictionary =
|
||||
RecipeList.Recipes.ToDictionary(z => z.Value, z => z.Key);
|
||||
|
||||
// Users can put spaces between item codes, or newlines. Recognize both!
|
||||
private static readonly string[] SplittersHex = [" ", "\n", "\r\n"];
|
||||
private static readonly string[] SplittersName = [",", "\n", "\r\n"];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested hex string(s).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the first input is a language code (2 characters), the logic will try to parse item names for that language instead of item IDs.
|
||||
/// </remarks>
|
||||
/// <param name="requestHex">8 byte hex item values (u64 format)</param>
|
||||
/// <param name="cfg">Options for packaging items</param>
|
||||
/// <param name="type">End destination of the item</param>
|
||||
public static IReadOnlyCollection<Item> GetItemsFromUserInput(string requestHex, IConfigItem cfg, ItemDestination type = ItemDestination.PlayerDropped)
|
||||
{
|
||||
/// <summary>
|
||||
/// Invert the recipe dictionary so we can look up recipe IDs from an input item ID.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyDictionary<ushort, ushort> InvertedRecipeDictionary =
|
||||
RecipeList.Recipes.ToDictionary(z => z.Value, z => z.Key);
|
||||
|
||||
// Users can put spaces between item codes, or newlines. Recognize both!
|
||||
private static readonly string[] SplittersHex = {" ", "\n", "\r\n"};
|
||||
private static readonly string[] SplittersName = {",", "\n", "\r\n"};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested hex string(s).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the first input is a language code (2 characters), the logic will try to parse item names for that language instead of item IDs.
|
||||
/// </remarks>
|
||||
/// <param name="requestHex">8 byte hex item values (u64 format)</param>
|
||||
/// <param name="cfg">Options for packaging items</param>
|
||||
/// <param name="type">End destination of the item</param>
|
||||
public static IReadOnlyCollection<Item> GetItemsFromUserInput(string requestHex, IConfigItem cfg, ItemDestination type = ItemDestination.PlayerDropped)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// having a language 2char code will cause an exception in parsing; this is fine and is handled by our catch statement.
|
||||
var split = requestHex.Split(SplittersHex, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetItemsHexCode(split, cfg, type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var split = requestHex.Split(SplittersName, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetItemsLanguage(split, cfg, type, GameLanguage.DefaultLanguage);
|
||||
}
|
||||
// having a language 2char code will cause an exception in parsing; this is fine and is handled by our catch statement.
|
||||
var split = requestHex.Split(SplittersHex, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetItemsHexCode(split, cfg, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of DIY item cards from the requested list of DIY IDs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the first input is a language code (2 characters), the logic will try to parse item names for that language instead of DIY IDs.
|
||||
/// </remarks>
|
||||
/// <param name="requestHex">8 byte hex item values (u64 format)</param>
|
||||
public static IReadOnlyCollection<Item> GetDIYsFromUserInput(string requestHex)
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
// having a language 2char code will cause an exception in parsing; this is fine and is handled by our catch statement.
|
||||
var split = requestHex.Split(SplittersHex, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetDIYItemsHexCode(split);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var split = requestHex.Split(SplittersName, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetDIYItemsLanguage(split);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested list of DIY hex code strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a hex code parse fails or a recipe ID does not exist, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of recipe IDs as u16 hex</param>
|
||||
public static IReadOnlyCollection<Item> GetDIYItemsHexCode(IReadOnlyList<string> split)
|
||||
{
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
bool parse = ulong.TryParse(text, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var value);
|
||||
if (!parse)
|
||||
throw new Exception($"Item value out of expected range ({text}).");
|
||||
|
||||
if (!RecipeList.Recipes.TryGetValue((ushort)value, out _))
|
||||
throw new Exception($"DIY recipe appears to be invalid ({text}).");
|
||||
|
||||
result[i] = new Item(Item.DIYRecipe) { Count = (ushort)value };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of DIY item cards from the requested list of item name strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a item name parse fails or a recipe ID does not exist, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of item names</param>
|
||||
/// <param name="lang">Language code to parse with. If the first entry in <see cref="split"/> is a language code, it will be used instead of <see cref="lang"/>.</param>
|
||||
public static IReadOnlyCollection<Item> GetDIYItemsLanguage(IReadOnlyList<string> split, string lang = GameLanguage.DefaultLanguage)
|
||||
{
|
||||
if (split.Count > 1 && split[0].Length < 3)
|
||||
{
|
||||
var langIndex = GameLanguage.GetLanguageIndex(split[0]);
|
||||
lang = GameLanguage.Language2Char(langIndex);
|
||||
split = split.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
var item = GetItem(text, lang);
|
||||
if (!InvertedRecipeDictionary.TryGetValue(item.ItemId, out var diy))
|
||||
throw new Exception($"DIY recipe appears to be invalid ({text}).");
|
||||
|
||||
result[i] = new Item(Item.DIYRecipe) { Count = diy };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested list of item name strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a item name parse fails or the item ID does not exist as a known item, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of item names</param>
|
||||
/// <param name="config">Item packaging options</param>
|
||||
/// <param name="type">Destination where the item will end up at</param>
|
||||
/// <param name="lang">Language code to parse with. If the first entry in <see cref="split"/> is a language code, it will be used instead of <see cref="lang"/>.</param>
|
||||
public static IReadOnlyCollection<Item> GetItemsLanguage(IReadOnlyList<string> split, IConfigItem config, ItemDestination type = ItemDestination.PlayerDropped, string lang = GameLanguage.DefaultLanguage)
|
||||
{
|
||||
if (split.Count > 1 && split[0].Length < 3)
|
||||
{
|
||||
var langIndex = GameLanguage.GetLanguageIndex(split[0]);
|
||||
lang = GameLanguage.Language2Char(langIndex);
|
||||
split = split.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
var strings = GameInfo.Strings.itemlistdisplay;
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
var item = CreateItem(text, i, config, type, lang);
|
||||
|
||||
if (item.ItemId >= strings.Length)
|
||||
throw new Exception($"Item requested is out of expected range ({item.ItemId:X4} > {strings.Length:X4}).");
|
||||
if (string.IsNullOrWhiteSpace(strings[item.ItemId]))
|
||||
throw new Exception($"Item requested does not have a valid name ({item.ItemId:X4}).");
|
||||
|
||||
result[i] = item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested list of item hex code strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a hex code parse fails or a recipe ID does not exist, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of recipe IDs as u16 hex</param>
|
||||
/// <param name="config">Item packaging options</param>
|
||||
/// <param name="type">Destination where the item will end up at</param>
|
||||
public static IReadOnlyCollection<Item> GetItemsHexCode(IReadOnlyList<string> split, IConfigItem config, ItemDestination type = ItemDestination.PlayerDropped)
|
||||
{
|
||||
var strings = GameInfo.Strings.itemlistdisplay;
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
var convert = GetBytesFromString(text);
|
||||
var item = CreateItem(convert, i, config, type);
|
||||
|
||||
if (item.ItemId >= strings.Length)
|
||||
throw new Exception($"Item requested is out of expected range ({item.ItemId:X4} > {strings.Length:X4}).");
|
||||
if (string.IsNullOrWhiteSpace(strings[item.ItemId]))
|
||||
throw new Exception($"Item requested does not have a valid name ({item.ItemId:X4}).");
|
||||
|
||||
result[i] = item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static byte[] GetBytesFromString(string text)
|
||||
{
|
||||
if (!ulong.TryParse(text, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out var value))
|
||||
return Item.NONE.ToBytes();
|
||||
return BitConverter.GetBytes(value);
|
||||
}
|
||||
|
||||
private static Item CreateItem(string name, int requestIndex, IConfigItem config, ItemDestination type, string lang = "en")
|
||||
{
|
||||
var item = GetItem(name, lang);
|
||||
if (item.IsNone)
|
||||
throw new Exception($"Failed to convert item (index {requestIndex}: {name}) for Language {lang}.");
|
||||
|
||||
return FinalizeItem(requestIndex, config, type, item);
|
||||
}
|
||||
|
||||
private static Item CreateItem(byte[] convert, int requestIndex, IConfigItem config, ItemDestination type)
|
||||
{
|
||||
Item item;
|
||||
try
|
||||
{
|
||||
if (convert.Length != Item.SIZE)
|
||||
throw new Exception();
|
||||
item = convert.ToClass<Item>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to convert item (index {requestIndex}: {ex.Message}).");
|
||||
}
|
||||
|
||||
return FinalizeItem(requestIndex, config, type, item);
|
||||
}
|
||||
|
||||
private static Item FinalizeItem(int requestIndex, IConfigItem config, ItemDestination type, Item item)
|
||||
{
|
||||
if (type == ItemDestination.PlayerDropped)
|
||||
{
|
||||
if (!ItemInfo.IsSaneItemForDrop(item) && !config.SkipDropCheck)
|
||||
throw new Exception($"Unsupported item: (index {requestIndex}).");
|
||||
if (config.WrapAllItems && item.ShouldWrapItem())
|
||||
item.SetWrapping(ItemWrapping.WrappingPaper, config.WrappingPaper, true);
|
||||
}
|
||||
|
||||
item.IsDropped = type == ItemDestination.FieldItemDropped;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static readonly CompareInfo Comparer = CultureInfo.InvariantCulture.CompareInfo;
|
||||
private const CompareOptions optIncludeSymbols = CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth;
|
||||
private const CompareOptions optIgnoreSymbols = CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a sensitive compare option, depending on the input string's qualities.
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Default options if no symbols,</returns>
|
||||
private static CompareOptions GetCompareOption(string str) => str.Any(ch => !char.IsLetterOrDigit(ch) && !char.IsWhiteSpace(ch)) ? optIgnoreSymbols & ~CompareOptions.IgnoreSymbols : optIgnoreSymbols;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item name-value that contains the <see cref="itemName"/> (case insensitive).
|
||||
/// </summary>
|
||||
/// <param name="itemName">Requested Item</param>
|
||||
/// <param name="lang">Game strings language to fetch with</param>
|
||||
/// <returns>Returns <see cref="Item.NO_ITEM"/> if no match found.</returns>
|
||||
public static Item GetItem(string itemName, string lang = "en")
|
||||
{
|
||||
var gStrings = GameInfo.GetStrings(lang);
|
||||
var strings = gStrings.ItemDataSource;
|
||||
var parsedItem = GetItem(itemName, strings);
|
||||
if (parsedItem != Item.NO_ITEM)
|
||||
return parsedItem;
|
||||
|
||||
if (gStrings.HasAssociatedItems(itemName, out var items))
|
||||
{
|
||||
if (items?.Count == 1)
|
||||
return new Item((ushort)items[0].Value);
|
||||
}
|
||||
|
||||
return Item.NO_ITEM;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item name-value that contains the <see cref="itemName"/> (case insensitive).
|
||||
/// </summary>
|
||||
/// <param name="itemName">Requested Item</param>
|
||||
/// <param name="strings">Game strings</param>
|
||||
/// <returns>Returns <see cref="Item.NO_ITEM"/> if no match found.</returns>
|
||||
public static Item GetItem(string itemName, IReadOnlyList<ComboItem> strings)
|
||||
{
|
||||
if (TryGetItem(itemName, strings, out var id))
|
||||
return new Item(id);
|
||||
return Item.NO_ITEM;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item name-value that contains the <see cref="itemName"/> (case insensitive).
|
||||
/// </summary>
|
||||
/// <param name="itemName">Requested Item</param>
|
||||
/// <param name="strings">List of item name-values</param>
|
||||
/// <param name="value">Item ID, if found. Otherwise, 0</param>
|
||||
/// <returns>True if found, false if none.</returns>
|
||||
public static bool TryGetItem(string itemName, IReadOnlyList<ComboItem> strings, out ushort value)
|
||||
{
|
||||
if (TryGetItem(itemName, strings, out value, optIncludeSymbols))
|
||||
return true;
|
||||
return TryGetItem(itemName, strings, out value, optIgnoreSymbols);
|
||||
}
|
||||
|
||||
private static bool TryGetItem(string itemName, IEnumerable<ComboItem> strings, out ushort value, CompareOptions opt)
|
||||
{
|
||||
foreach (var item in strings)
|
||||
{
|
||||
var result = Comparer.Compare(item.Text, 0, itemName, 0, opt);
|
||||
if (result != 0)
|
||||
continue;
|
||||
|
||||
value = (ushort)item.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = Item.NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable list of item key-value pairs that contain (case insensitive) the requested <see cref="itemName"/>.
|
||||
/// </summary>
|
||||
/// <param name="itemName">Item name</param>
|
||||
/// <param name="strings">Item names (and their Item ID values)</param>
|
||||
public static IEnumerable<ComboItem> GetItemsMatching(string itemName, IEnumerable<ComboItem> strings)
|
||||
{
|
||||
var opt = GetCompareOption(itemName);
|
||||
foreach (var item in strings)
|
||||
{
|
||||
var result = Comparer.IndexOf(item.Text, itemName, opt);
|
||||
if (result < 0)
|
||||
continue;
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable list of item key-value pairs that contain (case insensitive) the requested <see cref="itemName"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Orders the items based on the closest match (<see cref="LevenshteinDistance"/>).
|
||||
/// </remarks>
|
||||
/// <param name="itemName">Item name</param>
|
||||
/// <param name="strings">Item names (and their Item ID values)</param>
|
||||
public static IEnumerable<ComboItem> GetItemsMatchingOrdered(string itemName, IEnumerable<ComboItem> strings)
|
||||
{
|
||||
var matches = GetItemsMatching(itemName, strings);
|
||||
return GetItemsClosestOrdered(itemName, matches);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable list of item key-value pairs ordered by the closest <see cref="LevenshteinDistance"/> for the requested <see cref="itemName"/>.
|
||||
/// </summary>
|
||||
/// <param name="itemName">Item name</param>
|
||||
/// <param name="strings">Item names (and their Item ID values)</param>
|
||||
public static IEnumerable<ComboItem> GetItemsClosestOrdered(string itemName, IEnumerable<ComboItem> strings)
|
||||
{
|
||||
return strings.OrderBy(z => LevenshteinDistance.Compute(z.Text, itemName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Item Name and raw 8-byte value as a string.
|
||||
/// </summary>
|
||||
/// <param name="item">Item value</param>
|
||||
public static string GetItemText(Item item)
|
||||
{
|
||||
var value = BitConverter.ToUInt64(item.ToBytesClass(), 0);
|
||||
var name = GameInfo.Strings.GetItemName(item.ItemId);
|
||||
return $"{name}: {value:X16}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the u16 item ID from the input hex code.
|
||||
/// </summary>
|
||||
/// <param name="text">Hex code for the item (preferably 4 digits)</param>
|
||||
public static ushort GetID(string text)
|
||||
{
|
||||
if (!ulong.TryParse(text.Trim(), NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out var value))
|
||||
return Item.NONE;
|
||||
return (ushort)value;
|
||||
var split = requestHex.Split(SplittersName, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetItemsLanguage(split, cfg, type, GameLanguage.DefaultLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ItemDestination
|
||||
/// <summary>
|
||||
/// Gets a list of DIY item cards from the requested list of DIY IDs.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the first input is a language code (2 characters), the logic will try to parse item names for that language instead of DIY IDs.
|
||||
/// </remarks>
|
||||
/// <param name="requestHex">8 byte hex item values (u64 format)</param>
|
||||
public static IReadOnlyCollection<Item> GetDIYsFromUserInput(string requestHex)
|
||||
{
|
||||
PlayerDropped,
|
||||
FieldItemDropped,
|
||||
HeldItem,
|
||||
try
|
||||
{
|
||||
// having a language 2char code will cause an exception in parsing; this is fine and is handled by our catch statement.
|
||||
var split = requestHex.Split(SplittersHex, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetDIYItemsHexCode(split);
|
||||
}
|
||||
catch
|
||||
{
|
||||
var split = requestHex.Split(SplittersName, StringSplitOptions.RemoveEmptyEntries);
|
||||
return GetDIYItemsLanguage(split);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested list of DIY hex code strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a hex code parse fails or a recipe ID does not exist, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of recipe IDs as u16 hex</param>
|
||||
public static IReadOnlyCollection<Item> GetDIYItemsHexCode(IReadOnlyList<string> split)
|
||||
{
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
bool parse = ulong.TryParse(text, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var value);
|
||||
if (!parse)
|
||||
throw new Exception($"Item value out of expected range ({text}).");
|
||||
|
||||
if (!RecipeList.Recipes.TryGetValue((ushort)value, out _))
|
||||
throw new Exception($"DIY recipe appears to be invalid ({text}).");
|
||||
|
||||
result[i] = new Item(Item.DIYRecipe) { Count = (ushort)value };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of DIY item cards from the requested list of item name strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If an item name parse fails or a recipe ID does not exist, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of item names</param>
|
||||
/// <param name="lang">Language code to parse with. If the first entry in <see cref="split"/> is a language code, it will be used instead of <see cref="lang"/>.</param>
|
||||
public static IReadOnlyCollection<Item> GetDIYItemsLanguage(IReadOnlyList<string> split, string lang = GameLanguage.DefaultLanguage)
|
||||
{
|
||||
if (split.Count > 1 && split[0].Length < 3)
|
||||
{
|
||||
var langIndex = GameLanguage.GetLanguageIndex(split[0]);
|
||||
lang = GameLanguage.Language2Char(langIndex);
|
||||
split = split.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
var item = GetItem(text, lang);
|
||||
if (!InvertedRecipeDictionary.TryGetValue(item.ItemId, out var diy))
|
||||
throw new Exception($"DIY recipe appears to be invalid ({text}).");
|
||||
|
||||
result[i] = new Item(Item.DIYRecipe) { Count = diy };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested list of item name strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If an item name parse fails or the item ID does not exist as a known item, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of item names</param>
|
||||
/// <param name="config">Item packaging options</param>
|
||||
/// <param name="type">Destination where the item will end up at</param>
|
||||
/// <param name="lang">Language code to parse with. If the first entry in <see cref="split"/> is a language code, it will be used instead of <see cref="lang"/>.</param>
|
||||
public static IReadOnlyCollection<Item> GetItemsLanguage(IReadOnlyList<string> split, IConfigItem config, ItemDestination type = ItemDestination.PlayerDropped, string lang = GameLanguage.DefaultLanguage)
|
||||
{
|
||||
if (split.Count > 1 && split[0].Length < 3)
|
||||
{
|
||||
var langIndex = GameLanguage.GetLanguageIndex(split[0]);
|
||||
lang = GameLanguage.Language2Char(langIndex);
|
||||
split = split.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
var strings = GameInfo.Strings.itemlistdisplay;
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
var item = CreateItem(text, i, config, type, lang);
|
||||
|
||||
if (item.ItemId >= strings.Length)
|
||||
throw new Exception($"Item requested is out of expected range ({item.ItemId:X4} > {strings.Length:X4}).");
|
||||
if (string.IsNullOrWhiteSpace(strings[item.ItemId]))
|
||||
throw new Exception($"Item requested does not have a valid name ({item.ItemId:X4}).");
|
||||
|
||||
result[i] = item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of items from the requested list of item hex code strings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If a hex code parse fails or a recipe ID does not exist, exceptions will be thrown.
|
||||
/// </remarks>
|
||||
/// <param name="split">List of recipe IDs as u16 hex</param>
|
||||
/// <param name="config">Item packaging options</param>
|
||||
/// <param name="type">Destination where the item will end up at</param>
|
||||
public static IReadOnlyCollection<Item> GetItemsHexCode(IReadOnlyList<string> split, IConfigItem config, ItemDestination type = ItemDestination.PlayerDropped)
|
||||
{
|
||||
var strings = GameInfo.Strings.itemlistdisplay;
|
||||
var result = new Item[split.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var text = split[i].Trim();
|
||||
var convert = GetBytesFromString(text);
|
||||
var item = CreateItem(convert, i, config, type);
|
||||
|
||||
if (item.ItemId >= strings.Length)
|
||||
throw new Exception($"Item requested is out of expected range ({item.ItemId:X4} > {strings.Length:X4}).");
|
||||
if (string.IsNullOrWhiteSpace(strings[item.ItemId]))
|
||||
throw new Exception($"Item requested does not have a valid name ({item.ItemId:X4}).");
|
||||
|
||||
result[i] = item;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static byte[] GetBytesFromString(string text)
|
||||
{
|
||||
if (!ulong.TryParse(text, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out var value))
|
||||
return Item.NONE.ToBytes();
|
||||
var bytes = new byte[sizeof(ulong)];
|
||||
WriteUInt64LittleEndian(bytes, value);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static Item CreateItem(string name, int requestIndex, IConfigItem config, ItemDestination type, string lang = "en")
|
||||
{
|
||||
var item = GetItem(name, lang);
|
||||
if (item.IsNone)
|
||||
throw new Exception($"Failed to convert item (index {requestIndex}: {name}) for Language {lang}.");
|
||||
|
||||
return FinalizeItem(requestIndex, config, type, item);
|
||||
}
|
||||
|
||||
private static Item CreateItem(byte[] convert, int requestIndex, IConfigItem config, ItemDestination type)
|
||||
{
|
||||
Item item;
|
||||
try
|
||||
{
|
||||
if (convert.Length != Item.SIZE)
|
||||
throw new Exception();
|
||||
item = convert.ToClass<Item>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to convert item (index {requestIndex}: {ex.Message}).");
|
||||
}
|
||||
|
||||
return FinalizeItem(requestIndex, config, type, item);
|
||||
}
|
||||
|
||||
private static Item FinalizeItem(int requestIndex, IConfigItem config, ItemDestination type, Item item)
|
||||
{
|
||||
if (type == ItemDestination.PlayerDropped)
|
||||
{
|
||||
if (!ItemInfo.IsSaneItemForDrop(item) && !config.SkipDropCheck)
|
||||
throw new Exception($"Unsupported item: (index {requestIndex}).");
|
||||
if (config.WrapAllItems && item.ShouldWrapItem())
|
||||
item.SetWrapping(ItemWrapping.WrappingPaper, config.WrappingPaper, true);
|
||||
}
|
||||
|
||||
item.IsDropped = type == ItemDestination.FieldItemDropped;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static readonly CompareInfo Comparer = CultureInfo.InvariantCulture.CompareInfo;
|
||||
private const CompareOptions optIncludeSymbols = CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth;
|
||||
private const CompareOptions optIgnoreSymbols = CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a sensitive compare option, depending on the input string's qualities.
|
||||
/// </summary>
|
||||
/// <param name="str">Input string</param>
|
||||
/// <returns>Default options if no symbols,</returns>
|
||||
private static CompareOptions GetCompareOption(string str) => str.Any(ch => !char.IsLetterOrDigit(ch) && !char.IsWhiteSpace(ch)) ? optIgnoreSymbols & ~CompareOptions.IgnoreSymbols : optIgnoreSymbols;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item name-value that contains the <see cref="itemName"/> (case-insensitive).
|
||||
/// </summary>
|
||||
/// <param name="itemName">Requested Item</param>
|
||||
/// <param name="lang">Game strings language to fetch with</param>
|
||||
/// <returns>Returns <see cref="Item.NO_ITEM"/> if no match found.</returns>
|
||||
public static Item GetItem(string itemName, string lang = "en")
|
||||
{
|
||||
var gStrings = GameInfo.GetStrings(lang);
|
||||
var strings = gStrings.ItemDataSource;
|
||||
var parsedItem = GetItem(itemName, strings);
|
||||
if (parsedItem != Item.NO_ITEM)
|
||||
return parsedItem;
|
||||
|
||||
if (gStrings.HasAssociatedItems(itemName, out var items))
|
||||
{
|
||||
if (items?.Count == 1)
|
||||
return new Item((ushort)items[0].Value);
|
||||
}
|
||||
|
||||
return Item.NO_ITEM;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item name-value that contains the <see cref="itemName"/> (case-insensitive).
|
||||
/// </summary>
|
||||
/// <param name="itemName">Requested Item</param>
|
||||
/// <param name="strings">Game strings</param>
|
||||
/// <returns>Returns <see cref="Item.NO_ITEM"/> if no match found.</returns>
|
||||
public static Item GetItem(string itemName, IReadOnlyList<ComboItem> strings)
|
||||
{
|
||||
if (TryGetItem(itemName, strings, out var id))
|
||||
return new Item(id);
|
||||
return Item.NO_ITEM;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first item name-value that contains the <see cref="itemName"/> (case-insensitive).
|
||||
/// </summary>
|
||||
/// <param name="itemName">Requested Item</param>
|
||||
/// <param name="strings">List of item name-values</param>
|
||||
/// <param name="value">Item ID, if found. Otherwise, 0</param>
|
||||
/// <returns>True if found, false if none.</returns>
|
||||
public static bool TryGetItem(string itemName, IReadOnlyList<ComboItem> strings, out ushort value)
|
||||
{
|
||||
if (TryGetItem(itemName, strings, out value, optIncludeSymbols))
|
||||
return true;
|
||||
return TryGetItem(itemName, strings, out value, optIgnoreSymbols);
|
||||
}
|
||||
|
||||
private static bool TryGetItem(string itemName, IEnumerable<ComboItem> strings, out ushort value, CompareOptions opt)
|
||||
{
|
||||
foreach (var item in strings)
|
||||
{
|
||||
var result = Comparer.Compare(item.Text, 0, itemName, 0, opt);
|
||||
if (result != 0)
|
||||
continue;
|
||||
|
||||
value = (ushort)item.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = Item.NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable list of item key-value pairs that contain (case-insensitive) the requested <see cref="itemName"/>.
|
||||
/// </summary>
|
||||
/// <param name="itemName">Item name</param>
|
||||
/// <param name="strings">Item names (and their Item ID values)</param>
|
||||
public static IEnumerable<ComboItem> GetItemsMatching(string itemName, IEnumerable<ComboItem> strings)
|
||||
{
|
||||
var opt = GetCompareOption(itemName);
|
||||
foreach (var item in strings)
|
||||
{
|
||||
var result = Comparer.IndexOf(item.Text, itemName, opt);
|
||||
if (result < 0)
|
||||
continue;
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable list of item key-value pairs that contain (case-insensitive) the requested <see cref="itemName"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Orders the items based on the closest match (<see cref="LevenshteinDistance"/>).
|
||||
/// </remarks>
|
||||
/// <param name="itemName">Item name</param>
|
||||
/// <param name="strings">Item names (and their Item ID values)</param>
|
||||
public static IEnumerable<ComboItem> GetItemsMatchingOrdered(string itemName, IEnumerable<ComboItem> strings)
|
||||
{
|
||||
var matches = GetItemsMatching(itemName, strings);
|
||||
return GetItemsClosestOrdered(itemName, matches);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerable list of item key-value pairs ordered by the closest <see cref="LevenshteinDistance"/> for the requested <see cref="itemName"/>.
|
||||
/// </summary>
|
||||
/// <param name="itemName">Item name</param>
|
||||
/// <param name="strings">Item names (and their Item ID values)</param>
|
||||
public static IEnumerable<ComboItem> GetItemsClosestOrdered(string itemName, IEnumerable<ComboItem> strings)
|
||||
{
|
||||
return strings.OrderBy(z => LevenshteinDistance.Compute(z.Text, itemName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Item Name and raw 8-byte value as a string.
|
||||
/// </summary>
|
||||
/// <param name="item">Item value</param>
|
||||
public static string GetItemText(Item item)
|
||||
{
|
||||
var value = ReadUInt64LittleEndian(item.ToBytesClass());
|
||||
var name = GameInfo.Strings.GetItemName(item.ItemId);
|
||||
return $"{name}: {value:X16}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the u16 item ID from the input hex code.
|
||||
/// </summary>
|
||||
/// <param name="text">Hex code for the item (preferably 4 digits)</param>
|
||||
public static ushort GetID(string text)
|
||||
{
|
||||
if (!ulong.TryParse(text.Trim(), NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out var value))
|
||||
return Item.NONE;
|
||||
return (ushort)value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ItemDestination
|
||||
{
|
||||
PlayerDropped,
|
||||
FieldItemDropped,
|
||||
HeldItem,
|
||||
}
|
||||
|
|
@ -1,50 +1,49 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class LevenshteinDistance
|
||||
{
|
||||
public static class LevenshteinDistance
|
||||
/// <summary>
|
||||
/// Compute the distance between two strings.
|
||||
/// http://www.dotnetperls.com/levenshtein
|
||||
/// https://stackoverflow.com/a/13793600
|
||||
/// </summary>
|
||||
public static int Compute(string s, string t)
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute the distance between two strings.
|
||||
/// http://www.dotnetperls.com/levenshtein
|
||||
/// https://stackoverflow.com/a/13793600
|
||||
/// </summary>
|
||||
public static int Compute(string s, string t)
|
||||
int n = s.Length;
|
||||
int m = t.Length;
|
||||
int[,] d = new int[n + 1, m + 1];
|
||||
|
||||
// Step 1
|
||||
if (n == 0)
|
||||
return m;
|
||||
|
||||
if (m == 0)
|
||||
return n;
|
||||
|
||||
// Step 2
|
||||
for (int i = 0; i <= n; d[i, 0] = i++) { }
|
||||
for (int j = 0; j <= m; d[0, j] = j++) { }
|
||||
|
||||
// Step 3
|
||||
for (int i = 1; i <= n; i++)
|
||||
{
|
||||
int n = s.Length;
|
||||
int m = t.Length;
|
||||
int[,] d = new int[n + 1, m + 1];
|
||||
|
||||
// Step 1
|
||||
if (n == 0)
|
||||
return m;
|
||||
|
||||
if (m == 0)
|
||||
return n;
|
||||
|
||||
// Step 2
|
||||
for (int i = 0; i <= n; d[i, 0] = i++) { }
|
||||
for (int j = 0; j <= m; d[0, j] = j++) { }
|
||||
|
||||
// Step 3
|
||||
for (int i = 1; i <= n; i++)
|
||||
//Step 4
|
||||
for (int j = 1; j <= m; j++)
|
||||
{
|
||||
//Step 4
|
||||
for (int j = 1; j <= m; j++)
|
||||
{
|
||||
// Step 5
|
||||
int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
|
||||
// Step 5
|
||||
int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
|
||||
|
||||
// Step 6
|
||||
var x1 = d[i - 1, j] + 1;
|
||||
var x2 = d[i, j - 1] + 1;
|
||||
var x3 = d[i - 1, j - 1] + cost;
|
||||
d[i, j] = Math.Min(Math.Min(x1, x2), x3);
|
||||
}
|
||||
// Step 6
|
||||
var x1 = d[i - 1, j] + 1;
|
||||
var x2 = d[i, j - 1] + 1;
|
||||
var x3 = d[i - 1, j - 1] + cost;
|
||||
d[i, j] = Math.Min(Math.Min(x1, x2), x3);
|
||||
}
|
||||
|
||||
// Step 7
|
||||
return d[n, m];
|
||||
}
|
||||
|
||||
// Step 7
|
||||
return d[n, m];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,122 +2,128 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
// The MIT License (MIT)
|
||||
|
||||
// Copyright (c) 2014 Hans Wolff
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
public sealed class Aes128CounterMode : SymmetricAlgorithm
|
||||
{
|
||||
// The MIT License (MIT)
|
||||
private readonly byte[] _counter;
|
||||
private readonly Aes _aes = GetAes();
|
||||
|
||||
// Copyright (c) 2014 Hans Wolff
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
public sealed class Aes128CounterMode : SymmetricAlgorithm
|
||||
private static Aes GetAes()
|
||||
{
|
||||
private readonly byte[] _counter;
|
||||
private readonly AesManaged _aes = new() {Mode = CipherMode.ECB, Padding = PaddingMode.None};
|
||||
|
||||
public Aes128CounterMode(byte[] counter)
|
||||
{
|
||||
const int expect = 0x10;
|
||||
if (counter.Length != expect)
|
||||
throw new ArgumentException($"Counter size must be same as block size (actual: {counter.Length}, expected: {expect})");
|
||||
_counter = counter;
|
||||
}
|
||||
|
||||
public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] ignoredParameter) => new CounterModeCryptoTransform(_aes, rgbKey, _counter);
|
||||
public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] ignoredParameter) => new CounterModeCryptoTransform(_aes, rgbKey, _counter);
|
||||
|
||||
public override void GenerateKey() => _aes.GenerateKey();
|
||||
public override void GenerateIV() { /* IV not needed in Counter Mode */ }
|
||||
protected override void Dispose(bool disposing) => _aes.Dispose();
|
||||
var result = Aes.Create();
|
||||
result.Mode = CipherMode.ECB;
|
||||
result.Padding = PaddingMode.None;
|
||||
return result;
|
||||
}
|
||||
|
||||
public sealed class CounterModeCryptoTransform : ICryptoTransform
|
||||
public Aes128CounterMode(byte[] counter)
|
||||
{
|
||||
private readonly byte[] _counter;
|
||||
private readonly ICryptoTransform _counterEncryptor;
|
||||
private readonly Queue<byte> _xorMask = new();
|
||||
private readonly SymmetricAlgorithm _symmetricAlgorithm;
|
||||
|
||||
public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] counter)
|
||||
{
|
||||
if (counter.Length != symmetricAlgorithm.BlockSize / 8)
|
||||
throw new ArgumentException($"Counter size must be same as block size (actual: {counter.Length}, expected: {symmetricAlgorithm.BlockSize / 8})");
|
||||
|
||||
_symmetricAlgorithm = symmetricAlgorithm;
|
||||
_encryptOutput = new byte[counter.Length];
|
||||
_counter = counter;
|
||||
|
||||
var zeroIv = new byte[counter.Length];
|
||||
_counterEncryptor = symmetricAlgorithm.CreateEncryptor(key, zeroIv);
|
||||
}
|
||||
|
||||
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
|
||||
{
|
||||
var output = new byte[inputCount];
|
||||
TransformBlock(inputBuffer, inputOffset, inputCount, output, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
|
||||
{
|
||||
var xm = _xorMask;
|
||||
for (var i = 0; i < inputCount; i++)
|
||||
{
|
||||
if (xm.Count == 0)
|
||||
EncryptCounterThenIncrement();
|
||||
|
||||
var mask = xm.Dequeue();
|
||||
outputBuffer[outputOffset + i] = (byte)(inputBuffer[inputOffset + i] ^ mask);
|
||||
}
|
||||
|
||||
return inputCount;
|
||||
}
|
||||
|
||||
private readonly byte[] _encryptOutput;
|
||||
|
||||
private void EncryptCounterThenIncrement()
|
||||
{
|
||||
var counterModeBlock = _encryptOutput;
|
||||
|
||||
_counterEncryptor.TransformBlock(_counter, 0, _counter.Length, counterModeBlock, 0);
|
||||
IncrementCounter();
|
||||
|
||||
var xm = _xorMask;
|
||||
foreach (var b in counterModeBlock)
|
||||
xm.Enqueue(b);
|
||||
}
|
||||
|
||||
private void IncrementCounter()
|
||||
{
|
||||
var ctr = _counter;
|
||||
for (var i = ctr.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (++ctr[i] != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int InputBlockSize => _symmetricAlgorithm.BlockSize / 8;
|
||||
public int OutputBlockSize => _symmetricAlgorithm.BlockSize / 8;
|
||||
public bool CanTransformMultipleBlocks => true;
|
||||
public bool CanReuseTransform => false;
|
||||
|
||||
public void Dispose() => _counterEncryptor.Dispose();
|
||||
const int expect = 0x10;
|
||||
if (counter.Length != expect)
|
||||
throw new ArgumentException($"Counter size must be same as block size (actual: {counter.Length}, expected: {expect})");
|
||||
_counter = counter;
|
||||
}
|
||||
|
||||
public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? ignoredParameter) => new CounterModeCryptoTransform(_aes, rgbKey, _counter);
|
||||
public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? ignoredParameter) => new CounterModeCryptoTransform(_aes, rgbKey, _counter);
|
||||
|
||||
public override void GenerateKey() => _aes.GenerateKey();
|
||||
public override void GenerateIV() { /* IV not needed in Counter Mode */ }
|
||||
protected override void Dispose(bool disposing) => _aes.Dispose();
|
||||
}
|
||||
|
||||
public sealed class CounterModeCryptoTransform : ICryptoTransform
|
||||
{
|
||||
private readonly byte[] _counter;
|
||||
private readonly ICryptoTransform _counterEncryptor;
|
||||
private readonly Queue<byte> _xorMask = new();
|
||||
private readonly SymmetricAlgorithm _symmetricAlgorithm;
|
||||
|
||||
public CounterModeCryptoTransform(SymmetricAlgorithm symmetricAlgorithm, byte[] key, byte[] counter)
|
||||
{
|
||||
if (counter.Length != symmetricAlgorithm.BlockSize / 8)
|
||||
throw new ArgumentException($"Counter size must be same as block size (actual: {counter.Length}, expected: {symmetricAlgorithm.BlockSize / 8})");
|
||||
|
||||
_symmetricAlgorithm = symmetricAlgorithm;
|
||||
_encryptOutput = new byte[counter.Length];
|
||||
_counter = counter;
|
||||
|
||||
var zeroIv = new byte[counter.Length];
|
||||
_counterEncryptor = symmetricAlgorithm.CreateEncryptor(key, zeroIv);
|
||||
}
|
||||
|
||||
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
|
||||
{
|
||||
var output = new byte[inputCount];
|
||||
TransformBlock(inputBuffer, inputOffset, inputCount, output, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
|
||||
{
|
||||
var xm = _xorMask;
|
||||
for (var i = 0; i < inputCount; i++)
|
||||
{
|
||||
if (xm.Count == 0)
|
||||
EncryptCounterThenIncrement();
|
||||
|
||||
var mask = xm.Dequeue();
|
||||
outputBuffer[outputOffset + i] = (byte)(inputBuffer[inputOffset + i] ^ mask);
|
||||
}
|
||||
|
||||
return inputCount;
|
||||
}
|
||||
|
||||
private readonly byte[] _encryptOutput;
|
||||
|
||||
private void EncryptCounterThenIncrement()
|
||||
{
|
||||
var counterModeBlock = _encryptOutput;
|
||||
|
||||
_counterEncryptor.TransformBlock(_counter, 0, _counter.Length, counterModeBlock, 0);
|
||||
IncrementCounter();
|
||||
|
||||
var xm = _xorMask;
|
||||
foreach (var b in counterModeBlock)
|
||||
xm.Enqueue(b);
|
||||
}
|
||||
|
||||
private void IncrementCounter()
|
||||
{
|
||||
var ctr = _counter;
|
||||
for (var i = ctr.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (++ctr[i] != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int InputBlockSize => _symmetricAlgorithm.BlockSize / 8;
|
||||
public int OutputBlockSize => _symmetricAlgorithm.BlockSize / 8;
|
||||
public bool CanTransformMultipleBlocks => true;
|
||||
public bool CanReuseTransform => false;
|
||||
|
||||
public void Dispose() => _counterEncryptor.Dispose();
|
||||
}
|
||||
|
|
@ -1,16 +1,3 @@
|
|||
namespace NHSE.Core
|
||||
{
|
||||
internal readonly ref struct CryptoFile
|
||||
{
|
||||
public readonly byte[] Data;
|
||||
public readonly byte[] Key;
|
||||
public readonly byte[] Ctr;
|
||||
namespace NHSE.Core;
|
||||
|
||||
public CryptoFile(byte[] data, byte[] key, byte[] ctr)
|
||||
{
|
||||
Data = data;
|
||||
Key = key;
|
||||
Ctr = ctr;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal readonly record struct CryptoFile(byte[] Data, byte[] Key, byte[] Ctr);
|
||||
|
|
@ -1,81 +1,81 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed class EncryptedInt32
|
||||
{
|
||||
public sealed class EncryptedInt32
|
||||
// Encryption constant used to encrypt the int.
|
||||
private const uint ENCRYPTION_CONSTANT = 0x80E32B11;
|
||||
// Base shift count used in the encryption.
|
||||
private const byte SHIFT_BASE = 3;
|
||||
|
||||
public readonly uint OriginalEncrypted;
|
||||
public readonly ushort Adjust;
|
||||
public readonly byte Shift;
|
||||
public readonly byte Checksum;
|
||||
|
||||
public uint Value;
|
||||
|
||||
public override string ToString() => Value.ToString();
|
||||
|
||||
public EncryptedInt32(uint encryptedValue, ushort adjust = 0, byte shift = 0, byte checksum = 0)
|
||||
{
|
||||
// Encryption constant used to encrypt the int.
|
||||
private const uint ENCRYPTION_CONSTANT = 0x80E32B11;
|
||||
// Base shift count used in the encryption.
|
||||
private const byte SHIFT_BASE = 3;
|
||||
|
||||
public readonly uint OriginalEncrypted;
|
||||
public readonly ushort Adjust;
|
||||
public readonly byte Shift;
|
||||
public readonly byte Checksum;
|
||||
|
||||
public uint Value;
|
||||
|
||||
public override string ToString() => Value.ToString();
|
||||
|
||||
public EncryptedInt32(uint encryptedValue, ushort adjust = 0, byte shift = 0, byte checksum = 0)
|
||||
{
|
||||
OriginalEncrypted = encryptedValue;
|
||||
Adjust = adjust;
|
||||
Shift = shift;
|
||||
Checksum = checksum;
|
||||
Value = Decrypt(encryptedValue, shift, adjust);
|
||||
}
|
||||
|
||||
public void Write(byte[] data, int offset) => Write(this, data, offset);
|
||||
|
||||
// Calculates a checksum for a given encrypted value
|
||||
// Checksum calculation is every byte of the encrypted in added together minus 0x2D.
|
||||
public static byte CalculateChecksum(uint value)
|
||||
{
|
||||
var byteSum = value + (value >> 16) + (value >> 24) + (value >> 8);
|
||||
return (byte)(byteSum - 0x2D);
|
||||
}
|
||||
|
||||
public static uint Decrypt(uint encrypted, byte shift, ushort adjust)
|
||||
{
|
||||
// Decrypt the encrypted int using the given params.
|
||||
ulong val = ((ulong) encrypted) << ((32 - SHIFT_BASE - shift) & 0x3F);
|
||||
val += val >> 32;
|
||||
return ENCRYPTION_CONSTANT - adjust + (uint)val;
|
||||
}
|
||||
|
||||
public static uint Encrypt(uint value, byte shift, ushort adjust)
|
||||
{
|
||||
ulong val = (ulong) (value + (adjust - ENCRYPTION_CONSTANT)) << (shift + SHIFT_BASE);
|
||||
return (uint) ((val >> 32) + val);
|
||||
}
|
||||
|
||||
public static EncryptedInt32 ReadVerify(byte[] data, int offset)
|
||||
{
|
||||
var val = Read(data, offset);
|
||||
if (val.Checksum != CalculateChecksum(val.OriginalEncrypted))
|
||||
throw new ArgumentException($"Failed to verify the {nameof(EncryptedInt32)} at {nameof(offset)}");
|
||||
return val;
|
||||
}
|
||||
|
||||
public static EncryptedInt32 Read(byte[] data, int offset)
|
||||
{
|
||||
var enc = BitConverter.ToUInt32(data, offset + 0);
|
||||
var adjust = BitConverter.ToUInt16(data, offset + 4);
|
||||
var shift = data[offset + 6];
|
||||
var chk = data[offset + 7];
|
||||
return new EncryptedInt32(enc, adjust, shift, chk);
|
||||
}
|
||||
|
||||
public static void Write(EncryptedInt32 value, byte[] data, int offset)
|
||||
{
|
||||
uint enc = Encrypt(value.Value, value.Shift, value.Adjust);
|
||||
byte chk = CalculateChecksum(enc);
|
||||
BitConverter.GetBytes(enc).CopyTo(data, offset + 0);
|
||||
BitConverter.GetBytes(value.Adjust).CopyTo(data, offset + 4);
|
||||
data[offset + 6] = value.Shift;
|
||||
data[offset + 7] = chk;
|
||||
}
|
||||
OriginalEncrypted = encryptedValue;
|
||||
Adjust = adjust;
|
||||
Shift = shift;
|
||||
Checksum = checksum;
|
||||
Value = Decrypt(encryptedValue, shift, adjust);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(Span<byte> data) => Write(this, data);
|
||||
public void Write(byte[] data, int offset) => Write(data.AsSpan(offset));
|
||||
|
||||
// Calculates a checksum for a given encrypted value
|
||||
// Checksum calculation is every byte of the encrypted in added together minus 0x2D.
|
||||
public static byte CalculateChecksum(uint value)
|
||||
{
|
||||
var byteSum = value + (value >> 16) + (value >> 24) + (value >> 8);
|
||||
return (byte)(byteSum - 0x2D);
|
||||
}
|
||||
|
||||
public static uint Decrypt(uint encrypted, byte shift, ushort adjust)
|
||||
{
|
||||
// Decrypt the encrypted int using the given params.
|
||||
ulong val = ((ulong) encrypted) << ((32 - SHIFT_BASE - shift) & 0x3F);
|
||||
val += val >> 32;
|
||||
return ENCRYPTION_CONSTANT - adjust + (uint)val;
|
||||
}
|
||||
|
||||
public static uint Encrypt(uint value, byte shift, ushort adjust)
|
||||
{
|
||||
ulong val = (ulong) (value + unchecked(adjust - ENCRYPTION_CONSTANT)) << (shift + SHIFT_BASE);
|
||||
return (uint) ((val >> 32) + val);
|
||||
}
|
||||
|
||||
public static EncryptedInt32 ReadVerify(ReadOnlySpan<byte> data, int offset)
|
||||
{
|
||||
var val = Read(data[offset..]);
|
||||
if (val.Checksum != CalculateChecksum(val.OriginalEncrypted))
|
||||
throw new ArgumentException($"Failed to verify the {nameof(EncryptedInt32)} at {nameof(offset)}");
|
||||
return val;
|
||||
}
|
||||
|
||||
public static EncryptedInt32 Read(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var encrypted = ReadUInt32LittleEndian(data);
|
||||
var adjust = ReadUInt16LittleEndian(data[4..]);
|
||||
var shift = data[6];
|
||||
var chk = data[7];
|
||||
return new EncryptedInt32(encrypted, adjust, shift, chk);
|
||||
}
|
||||
|
||||
public static void Write(EncryptedInt32 value, Span<byte> data)
|
||||
{
|
||||
var encrypted = Encrypt(value.Value, value.Shift, value.Adjust);
|
||||
WriteUInt32LittleEndian(data, encrypted);
|
||||
WriteUInt16LittleEndian(data[4..], value.Adjust);
|
||||
data[6] = value.Shift;
|
||||
data[7] = CalculateChecksum(encrypted);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public readonly ref struct EncryptedSaveFile
|
||||
{
|
||||
public readonly ref struct EncryptedSaveFile
|
||||
public readonly byte[] Data;
|
||||
public readonly byte[] Header;
|
||||
|
||||
public EncryptedSaveFile(byte[] data, byte[] header)
|
||||
{
|
||||
public readonly byte[] Data;
|
||||
public readonly byte[] Header;
|
||||
|
||||
public EncryptedSaveFile(byte[] data, byte[] header)
|
||||
{
|
||||
Data = data;
|
||||
Header = header;
|
||||
}
|
||||
|
||||
#region Equality Comparison
|
||||
public override bool Equals(object obj) => false;
|
||||
public override int GetHashCode() => Data.GetHashCode();
|
||||
public static bool operator !=(EncryptedSaveFile left, EncryptedSaveFile right) => !(left == right);
|
||||
public static bool operator ==(EncryptedSaveFile left, EncryptedSaveFile right) => left.Data == right.Data && left.Header == right.Header;
|
||||
#endregion
|
||||
Data = data;
|
||||
Header = header;
|
||||
}
|
||||
}
|
||||
|
||||
#region Equality Comparison
|
||||
public override bool Equals(object? obj) => false;
|
||||
public override int GetHashCode() => Data.GetHashCode();
|
||||
public static bool operator !=(EncryptedSaveFile left, EncryptedSaveFile right) => !(left == right);
|
||||
public static bool operator ==(EncryptedSaveFile left, EncryptedSaveFile right) => left.Data == right.Data && left.Header == right.Header;
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,82 +1,81 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class Encryption
|
||||
{
|
||||
public static class Encryption
|
||||
private static byte[] GetParam(ReadOnlySpan<uint> data, in int index)
|
||||
{
|
||||
private static byte[] GetParam(uint[] data, in int index)
|
||||
{
|
||||
var rand = new XorShift128(data[data[index] & 0x7F]);
|
||||
var prms = data[data[index + 1] & 0x7F] & 0x7F;
|
||||
var rand = new XorShift128(data[(int)data[index] & 0x7F]);
|
||||
var prms = data[(int)(data[index + 1] & 0x7F)] & 0x7F;
|
||||
|
||||
var rndRollCount = (prms & 0xF) + 1;
|
||||
for (var i = 0; i < rndRollCount; i++)
|
||||
rand.GetU64();
|
||||
var rndRollCount = (prms & 0xF) + 1;
|
||||
for (var i = 0; i < rndRollCount; i++)
|
||||
rand.GetU64();
|
||||
|
||||
var result = new byte[0x10];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
result[i] = (byte)(rand.GetU32() >> 24);
|
||||
var result = new byte[0x10];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
result[i] = (byte)(rand.GetU32() >> 24);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the <see cref="encData"/> using the <see cref="headerData"/> in place.
|
||||
/// </summary>
|
||||
/// <param name="headerData">Header Data</param>
|
||||
/// <param name="encData">Encrypted SaveData</param>
|
||||
public static void Decrypt(byte[] headerData, byte[] encData)
|
||||
{
|
||||
// First 256 bytes go unused
|
||||
var importantData = new uint[0x80];
|
||||
Buffer.BlockCopy(headerData, 0x100, importantData, 0, 0x200);
|
||||
|
||||
// Set up Key
|
||||
var key = GetParam(importantData, 0);
|
||||
|
||||
// Set up counter
|
||||
var counter = GetParam(importantData, 2);
|
||||
|
||||
// Do the AES
|
||||
using var aesCtr = new Aes128CounterMode(counter);
|
||||
var transform = aesCtr.CreateDecryptor(key, counter);
|
||||
|
||||
transform.TransformBlock(encData, 0, encData.Length, encData, 0);
|
||||
}
|
||||
|
||||
private static CryptoFile GenerateHeaderFile(uint seed, byte[] versionData)
|
||||
{
|
||||
// Generate 128 Random uints which will be used for params
|
||||
var random = new XorShift128(seed);
|
||||
var encryptData = new uint[128];
|
||||
for (var i = 0; i < encryptData.Length; i++)
|
||||
encryptData[i] = random.GetU32();
|
||||
|
||||
var headerData = new byte[0x300];
|
||||
Buffer.BlockCopy(versionData, 0, headerData, 0, 0x100);
|
||||
Buffer.BlockCopy(encryptData, 0, headerData, 0x100, 0x200);
|
||||
return new CryptoFile(headerData, GetParam(encryptData, 0), GetParam(encryptData, 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts the <see cref="data"/> (savedata) using the provided <see cref="seed"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">SaveData to encrypt</param>
|
||||
/// <param name="seed">Seed to encrypt with</param>
|
||||
/// <param name="versionData">Version data to encrypt with</param>
|
||||
/// <returns>Encrypted SaveData, and associated headerData</returns>
|
||||
public static EncryptedSaveFile Encrypt(byte[] data, uint seed, byte[] versionData)
|
||||
{
|
||||
// Generate header file and get key and counter
|
||||
var header = GenerateHeaderFile(seed, versionData);
|
||||
|
||||
// Encrypt file
|
||||
using var aesCtr = new Aes128CounterMode(header.Ctr);
|
||||
var transform = aesCtr.CreateEncryptor(header.Key, header.Ctr);
|
||||
var encData = new byte[data.Length];
|
||||
transform.TransformBlock(data, 0, data.Length, encData, 0);
|
||||
|
||||
return new EncryptedSaveFile(encData, header.Data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the <see cref="encData"/> using the <see cref="headerData"/> in place.
|
||||
/// </summary>
|
||||
/// <param name="headerData">Header Data</param>
|
||||
/// <param name="encData">Encrypted SaveData</param>
|
||||
public static void Decrypt(byte[] headerData, byte[] encData)
|
||||
{
|
||||
// First 256 bytes go unused
|
||||
var importantData = new uint[0x80];
|
||||
Buffer.BlockCopy(headerData, 0x100, importantData, 0, 0x200);
|
||||
|
||||
// Set up Key
|
||||
var key = GetParam(importantData, 0);
|
||||
|
||||
// Set up counter
|
||||
var counter = GetParam(importantData, 2);
|
||||
|
||||
// Do the AES
|
||||
using var aesCtr = new Aes128CounterMode(counter);
|
||||
var transform = aesCtr.CreateDecryptor(key, counter);
|
||||
|
||||
transform.TransformBlock(encData, 0, encData.Length, encData, 0);
|
||||
}
|
||||
|
||||
private static CryptoFile GenerateHeaderFile(uint seed, byte[] versionData)
|
||||
{
|
||||
// Generate 128 Random uints which will be used for params
|
||||
var random = new XorShift128(seed);
|
||||
var encryptData = new uint[128];
|
||||
for (var i = 0; i < encryptData.Length; i++)
|
||||
encryptData[i] = random.GetU32();
|
||||
|
||||
var headerData = new byte[0x300];
|
||||
Buffer.BlockCopy(versionData, 0, headerData, 0, 0x100);
|
||||
Buffer.BlockCopy(encryptData, 0, headerData, 0x100, 0x200);
|
||||
return new CryptoFile(headerData, GetParam(encryptData, 0), GetParam(encryptData, 2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts the <see cref="data"/> (savedata) using the provided <see cref="seed"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">SaveData to encrypt</param>
|
||||
/// <param name="seed">Seed to encrypt with</param>
|
||||
/// <param name="versionData">Version data to encrypt with</param>
|
||||
/// <returns>Encrypted SaveData, and associated headerData</returns>
|
||||
public static EncryptedSaveFile Encrypt(byte[] data, uint seed, byte[] versionData)
|
||||
{
|
||||
// Generate header file and get key and counter
|
||||
var header = GenerateHeaderFile(seed, versionData);
|
||||
|
||||
// Encrypt file
|
||||
using var aesCtr = new Aes128CounterMode(header.Ctr);
|
||||
var transform = aesCtr.CreateEncryptor(header.Key, header.Ctr);
|
||||
var encData = new byte[data.Length];
|
||||
transform.TransformBlock(data, 0, data.Length, encData, 0);
|
||||
|
||||
return new EncryptedSaveFile(encData, header.Data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the <see cref="HashRegions"/> for a <see cref="FileName"/>.
|
||||
/// </summary>
|
||||
public sealed class FileHashDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the <see cref="HashRegions"/> for a <see cref="FileName"/>.
|
||||
/// Name of the File that these <see cref="HashRegions"/> apply to.
|
||||
/// </summary>
|
||||
public sealed class FileHashDetails
|
||||
public readonly string FileName;
|
||||
|
||||
/// <summary>
|
||||
/// Expected file size of the <see cref="FileName"/>.
|
||||
/// </summary>
|
||||
public readonly uint FileSize;
|
||||
|
||||
/// <summary>
|
||||
/// Hash specs that are done in this file.
|
||||
/// </summary>
|
||||
public readonly IReadOnlyList<FileHashRegion> HashRegions;
|
||||
|
||||
public FileHashDetails(string fileName, uint fileSize, IReadOnlyList<FileHashRegion> regions)
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the File that these <see cref="HashRegions"/> apply to.
|
||||
/// </summary>
|
||||
public readonly string FileName;
|
||||
|
||||
/// <summary>
|
||||
/// Expected file size of the <see cref="FileName"/>.
|
||||
/// </summary>
|
||||
public readonly uint FileSize;
|
||||
|
||||
/// <summary>
|
||||
/// Hash specs that are done in this file.
|
||||
/// </summary>
|
||||
public readonly IReadOnlyList<FileHashRegion> HashRegions;
|
||||
|
||||
public FileHashDetails(string fileName, uint fileSize, IReadOnlyList<FileHashRegion> regions)
|
||||
{
|
||||
FileName = fileName;
|
||||
FileSize = fileSize;
|
||||
HashRegions = regions;
|
||||
}
|
||||
FileName = fileName;
|
||||
FileSize = fileSize;
|
||||
HashRegions = regions;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public sealed class FileHashInfo
|
||||
{
|
||||
public sealed class FileHashInfo
|
||||
private readonly IReadOnlyDictionary<uint, FileHashDetails> List;
|
||||
|
||||
public FileHashInfo(FileHashInfo dupe) : this(dupe.List.Values) { }
|
||||
|
||||
public FileHashInfo(IEnumerable<FileHashDetails> hashSets)
|
||||
{
|
||||
private readonly IReadOnlyDictionary<uint, FileHashDetails> List;
|
||||
|
||||
public FileHashInfo(FileHashInfo dupe) : this(dupe.List.Values) { }
|
||||
|
||||
public FileHashInfo(IEnumerable<FileHashDetails> hashSets)
|
||||
{
|
||||
var list = new Dictionary<uint, FileHashDetails>();
|
||||
foreach (var hashSet in hashSets)
|
||||
list[hashSet.FileSize] = hashSet;
|
||||
List = list;
|
||||
}
|
||||
|
||||
public FileHashDetails? GetFile(string nameData)
|
||||
{
|
||||
return List.Values.FirstOrDefault(z => z.FileName == nameData);
|
||||
}
|
||||
var list = new Dictionary<uint, FileHashDetails>();
|
||||
foreach (var hashSet in hashSets)
|
||||
list[hashSet.FileSize] = hashSet;
|
||||
List = list;
|
||||
}
|
||||
}
|
||||
|
||||
public FileHashDetails? GetFile(string nameData)
|
||||
{
|
||||
return List.Values.FirstOrDefault(z => z.FileName == nameData);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +1,21 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the region that a validation hash is calculated over.
|
||||
/// </summary>
|
||||
/// <param name="HashOffset">Offset of the calculated hash.</param>
|
||||
/// <param name="Size">Length of the hashed data.</param>
|
||||
public readonly record struct FileHashRegion(int HashOffset, int Size)
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the region that a validation hash is calculated over.
|
||||
/// Offset where the data to be hashed starts at (calculated).
|
||||
/// </summary>
|
||||
public readonly struct FileHashRegion
|
||||
{
|
||||
/// <summary>
|
||||
/// Offset of the calculated hash.
|
||||
/// </summary>
|
||||
public readonly int HashOffset;
|
||||
public int BeginOffset => HashOffset + 4;
|
||||
|
||||
/// <summary>
|
||||
/// Length of the hashed data.
|
||||
/// </summary>
|
||||
public readonly int Size;
|
||||
/// <summary>
|
||||
/// Offset where the data to be hashed ends at (calculated).
|
||||
/// </summary>
|
||||
public int EndOffset => BeginOffset + Size;
|
||||
|
||||
/// <summary>
|
||||
/// Offset where the data to be hashed starts at (calculated).
|
||||
/// </summary>
|
||||
public int BeginOffset => HashOffset + 4;
|
||||
|
||||
/// <summary>
|
||||
/// Offset where the data to be hashed ends at (calculated).
|
||||
/// </summary>
|
||||
public int EndOffset => BeginOffset + Size;
|
||||
|
||||
public override string ToString() => $"0x{HashOffset:X}: (0x{BeginOffset:X}-0x{EndOffset:X})";
|
||||
|
||||
public FileHashRegion(int hashOfs, int size)
|
||||
{
|
||||
HashOffset = hashOfs;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
#region Equality Comparison
|
||||
public override bool Equals(object obj) => obj is FileHashRegion r && r == this;
|
||||
// ReSharper disable once PossiblyImpureMethodCallOnReadonlyVariable
|
||||
public override int GetHashCode() => BeginOffset.GetHashCode();
|
||||
|
||||
public static bool operator !=(FileHashRegion left, FileHashRegion right) => !(left == right);
|
||||
|
||||
public static bool operator ==(FileHashRegion left, FileHashRegion right)
|
||||
{
|
||||
if (left.HashOffset != right.HashOffset)
|
||||
return false;
|
||||
if (left.BeginOffset != right.BeginOffset)
|
||||
return false;
|
||||
if (left.Size != right.Size)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
public override string ToString() => $"0x{HashOffset:X}: (0x{BeginOffset:X}-0x{EndOffset:X})";
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,85 +1,77 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// MurmurHash implementation used by Animal Crossing New Horizons
|
||||
/// </summary>
|
||||
public static class Murmur3
|
||||
{
|
||||
/// <summary>
|
||||
/// MurmurHash implementation used by Animal Crossing New Horizons
|
||||
/// </summary>
|
||||
public static class Murmur3
|
||||
private static uint Murmur32_Scramble(uint k)
|
||||
{
|
||||
private static uint Murmur32_Scramble(uint k)
|
||||
{
|
||||
k = (k * 0x16A88000) | ((k * 0xCC9E2D51) >> 17);
|
||||
k *= 0x1B873593;
|
||||
return k;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hash at the specified offset, using the input parameters.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to hash</param>
|
||||
/// <param name="offset">Where the data-to-be-hashed starts</param>
|
||||
/// <param name="size">Amount of data to hash</param>
|
||||
/// <param name="seed">Initial Murmur seed (optional)</param>
|
||||
/// <returns>Calculated hash.</returns>
|
||||
public static uint GetMurmur3Hash(byte[] data, int offset, uint size, uint seed = 0)
|
||||
{
|
||||
uint checksum = seed;
|
||||
if (size > 3)
|
||||
{
|
||||
for (var i = 0; i < (size / sizeof(uint)); i++)
|
||||
{
|
||||
var val = BitConverter.ToUInt32(data, offset);
|
||||
checksum ^= Murmur32_Scramble(val);
|
||||
checksum = (checksum >> 19) | (checksum << 13);
|
||||
checksum = (checksum * 5) + 0xE6546B64;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
var remainder = size % sizeof(uint);
|
||||
if (remainder != 0)
|
||||
{
|
||||
uint val = BitConverter.ToUInt32(data, (int)((offset + size) - remainder));
|
||||
for (var i = 0; i < (sizeof(uint) - remainder); i++)
|
||||
val >>= 8;
|
||||
checksum ^= Murmur32_Scramble(val);
|
||||
}
|
||||
|
||||
checksum ^= size;
|
||||
checksum ^= checksum >> 16;
|
||||
checksum *= 0x85EBCA6B;
|
||||
checksum ^= checksum >> 13;
|
||||
checksum *= 0xC2B2AE35;
|
||||
checksum ^= checksum >> 16;
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hash at the specified offset, using the input parameters.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to hash</param>
|
||||
/// <param name="hashOffset">Offset to write the hash</param>
|
||||
/// <param name="readOffset">Where the data-to-be-hashed starts</param>
|
||||
/// <param name="readSize">Amount of data to hash</param>
|
||||
/// <returns>Calculated hash that was written back to the data.</returns>
|
||||
public static uint UpdateMurmur32(byte[] data, int hashOffset, int readOffset, uint readSize)
|
||||
{
|
||||
var newHash = GetMurmur3Hash(data, readOffset, readSize);
|
||||
var hashBytes = BitConverter.GetBytes(newHash);
|
||||
hashBytes.CopyTo(data, hashOffset);
|
||||
return newHash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the hash at the specified offset to see if the stored value matches the calculated value.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to hash</param>
|
||||
/// <param name="hashOffset">Offset to write the hash</param>
|
||||
/// <param name="readOffset">Where the data-to-be-hashed starts</param>
|
||||
/// <param name="readSize">Amount of data to hash</param>
|
||||
/// <returns>Calculated hash matches the currently stored hash.</returns>
|
||||
public static bool VerifyMurmur32(byte[] data, int hashOffset, int readOffset, uint readSize)
|
||||
=> BitConverter.ToUInt32(data, hashOffset) == GetMurmur3Hash(data, readOffset, readSize);
|
||||
k = (k * 0x16A88000) | ((k * 0xCC9E2D51) >> 17);
|
||||
k *= 0x1B873593;
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hash at the specified offset, using the input parameters.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to hash</param>
|
||||
/// <param name="seed">Initial Murmur seed (optional)</param>
|
||||
/// <returns>Calculated hash.</returns>
|
||||
public static uint GetMurmur3Hash(ReadOnlySpan<byte> data, uint seed = 0)
|
||||
{
|
||||
var checksum = seed;
|
||||
var remaining = data;
|
||||
while (remaining.Length >= sizeof(uint))
|
||||
{
|
||||
var val = ReadUInt32LittleEndian(remaining);
|
||||
checksum ^= Murmur32_Scramble(val);
|
||||
checksum = (checksum >> 19) | (checksum << 13);
|
||||
checksum = (checksum * 5) + 0xE6546B64;
|
||||
remaining = remaining[sizeof(uint)..];
|
||||
}
|
||||
|
||||
if (!remaining.IsEmpty)
|
||||
{
|
||||
uint val = 0;
|
||||
switch (remaining.Length)
|
||||
{
|
||||
case 3:
|
||||
val |= (uint)remaining[2] << 16;
|
||||
goto case 2;
|
||||
case 2:
|
||||
val |= (uint)remaining[1] << 8;
|
||||
goto case 1;
|
||||
case 1:
|
||||
val |= remaining[0];
|
||||
break;
|
||||
}
|
||||
|
||||
checksum ^= Murmur32_Scramble(val);
|
||||
}
|
||||
|
||||
checksum ^= (uint)data.Length;
|
||||
checksum ^= checksum >> 16;
|
||||
checksum *= 0x85EBCA6B;
|
||||
checksum ^= checksum >> 13;
|
||||
checksum *= 0xC2B2AE35;
|
||||
checksum ^= checksum >> 16;
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hash at the specified offset, using the input parameters.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to hash</param>
|
||||
/// <param name="hashDestination">Location two write the hash</param>
|
||||
/// <returns>Calculated hash that was written back to the data.</returns>
|
||||
public static uint UpdateMurmur32(ReadOnlySpan<byte> data, Span<byte> hashDestination)
|
||||
{
|
||||
var newHash = GetMurmur3Hash(data);
|
||||
WriteUInt32LittleEndian(hashDestination, newHash);
|
||||
return newHash;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\byte\item_kind.bin" />
|
||||
<None Remove="Resources\byte\item_menuicon.bin" />
|
||||
|
|
|
|||
|
|
@ -1,266 +1,262 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// main.dat
|
||||
/// </summary>
|
||||
public sealed class MainSave : EncryptedFilePair
|
||||
{
|
||||
/// <summary>
|
||||
/// main.dat
|
||||
/// </summary>
|
||||
public sealed class MainSave : EncryptedFilePair
|
||||
public readonly MainSaveOffsets Offsets;
|
||||
public MainSave(string folder) : base(folder, "main") => Offsets = MainSaveOffsets.GetOffsets(Info);
|
||||
|
||||
public Hemisphere Hemisphere { get => (Hemisphere)Data[Offsets.WeatherArea]; set => Data[Offsets.WeatherArea] = (byte)value; }
|
||||
public AirportColor AirportThemeColor { get => (AirportColor)Data[Offsets.AirportThemeColor]; set => Data[Offsets.AirportThemeColor] = (byte)value; }
|
||||
|
||||
public uint WeatherSeed
|
||||
{
|
||||
public readonly MainSaveOffsets Offsets;
|
||||
public MainSave(string folder) : base(folder, "main") => Offsets = MainSaveOffsets.GetOffsets(Info);
|
||||
|
||||
public Hemisphere Hemisphere { get => (Hemisphere)Data[Offsets.WeatherArea]; set => Data[Offsets.WeatherArea] = (byte)value; }
|
||||
public AirportColor AirportThemeColor { get => (AirportColor)Data[Offsets.AirportThemeColor]; set => Data[Offsets.AirportThemeColor] = (byte)value; }
|
||||
public uint WeatherSeed { get => BitConverter.ToUInt32(Data, Offsets.WeatherRandSeed); set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.WeatherRandSeed); }
|
||||
|
||||
public IVillager GetVillager(int index) => Offsets.ReadVillager(Data, index);
|
||||
public void SetVillager(IVillager value, int index) => Offsets.WriteVillager(value, Data, index);
|
||||
|
||||
public IVillagerHouse GetVillagerHouse(int index) => Offsets.ReadVillagerHouse(Data, index);
|
||||
public void SetVillagerHouse(IVillagerHouse value, int index) => Offsets.WriteVillagerHouse(value, Data, index);
|
||||
|
||||
public IVillager[] GetVillagers()
|
||||
{
|
||||
var villagers = new IVillager[MainSaveOffsets.VillagerCount];
|
||||
for (int i = 0; i < villagers.Length; i++)
|
||||
villagers[i] = GetVillager(i);
|
||||
return villagers;
|
||||
}
|
||||
|
||||
public void SetVillagers(IReadOnlyList<IVillager> villagers)
|
||||
{
|
||||
for (int i = 0; i < villagers.Count; i++)
|
||||
SetVillager(villagers[i], i);
|
||||
}
|
||||
|
||||
public IVillagerHouse[] GetVillagerHouses()
|
||||
{
|
||||
var villagers = new IVillagerHouse[MainSaveOffsets.VillagerCount];
|
||||
for (int i = 0; i < villagers.Length; i++)
|
||||
villagers[i] = GetVillagerHouse(i);
|
||||
return villagers;
|
||||
}
|
||||
|
||||
public void SetVillagerHouses(IReadOnlyList<IVillagerHouse> villagers)
|
||||
{
|
||||
for (int i = 0; i < villagers.Count; i++)
|
||||
SetVillagerHouse(villagers[i], i);
|
||||
}
|
||||
|
||||
public DesignPattern GetDesign(int index) => Offsets.ReadPattern(Data, index);
|
||||
public void SetDesign(DesignPattern value, int index, byte[] playerID, byte[] townID) => Offsets.WritePattern(value, Data, index, playerID, townID);
|
||||
public DesignPatternPRO GetDesignPRO(int index) => Offsets.ReadPatternPRO(Data, index);
|
||||
public void SetDesignPRO(DesignPatternPRO value, int index, byte[] playerID, byte[] townID) => Offsets.WritePatternPRO(value, Data, index, playerID, townID);
|
||||
|
||||
public IReadOnlyList<Item> RecycleBin
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.LostItemBox, MainSaveOffsets.RecycleBinCount * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data, Offsets.LostItemBox);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Building> Buildings
|
||||
{
|
||||
get => Building.GetArray(Data.Slice(Offsets.MainFieldStructure, MainSaveOffsets.BuildingCount * Building.SIZE));
|
||||
set => Building.SetArray(value).CopyTo(Data, Offsets.MainFieldStructure);
|
||||
}
|
||||
|
||||
public IPlayerHouse GetPlayerHouse(int index) => Offsets.ReadPlayerHouse(Data, index);
|
||||
public void SetPlayerHouse(IPlayerHouse value, int index) => Offsets.WritePlayerHouse(value, Data, index);
|
||||
|
||||
public IPlayerHouse[] GetPlayerHouses()
|
||||
{
|
||||
var players = new IPlayerHouse[MainSaveOffsets.PlayerCount];
|
||||
for (int i = 0; i < players.Length; i++)
|
||||
players[i] = GetPlayerHouse(i);
|
||||
return players;
|
||||
}
|
||||
|
||||
public void SetPlayerHouses(IReadOnlyList<IPlayerHouse> houses)
|
||||
{
|
||||
for (int i = 0; i < houses.Count; i++)
|
||||
SetPlayerHouse(houses[i], i);
|
||||
}
|
||||
|
||||
public DesignPattern[] GetDesigns()
|
||||
{
|
||||
var result = new DesignPattern[Offsets.PatternCount];
|
||||
for (int i = 0; i <result.Length; i++)
|
||||
result[i] = GetDesign(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetDesigns(IReadOnlyList<DesignPattern> value, byte[] playerID, byte[] townID)
|
||||
{
|
||||
var count = Math.Min(Offsets.PatternCount, value.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
SetDesign(value[i], i, playerID, townID);
|
||||
}
|
||||
|
||||
public DesignPatternPRO[] GetDesignsPRO()
|
||||
{
|
||||
var result = new DesignPatternPRO[Offsets.PatternCount];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = GetDesignPRO(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetDesignsPRO(IReadOnlyList<DesignPatternPRO> value, byte[] playerID, byte[] townID)
|
||||
{
|
||||
var count = Math.Min(Offsets.PatternCount, value.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
SetDesignPRO(value[i], i, playerID, townID);
|
||||
}
|
||||
|
||||
public DesignPattern FlagMyDesign
|
||||
{
|
||||
get => MainSaveOffsets.ReadPatternAtOffset(Data, Offsets.PatternFlag);
|
||||
set => value.Data.CopyTo(Data, Offsets.PatternFlag);
|
||||
}
|
||||
|
||||
public DesignPatternPRO[] GetDesignsTailor()
|
||||
{
|
||||
var result = new DesignPatternPRO[MainSaveOffsets.PatternTailorCount];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = MainSaveOffsets.ReadPatternPROAtOffset(Data, Offsets.PatternTailor + (i * DesignPatternPRO.SIZE));
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetDesignsTailor(IReadOnlyList<DesignPatternPRO> value)
|
||||
{
|
||||
var count = Math.Min(Offsets.PatternCount, value.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
value[i].Data.CopyTo(Data, Offsets.PatternTailor + (i * DesignPatternPRO.SIZE));
|
||||
}
|
||||
|
||||
private const int EventFlagsSaveCount = 0x400;
|
||||
|
||||
public short[] GetEventFlagLand()
|
||||
{
|
||||
var value = new short[EventFlagsSaveCount];
|
||||
Buffer.BlockCopy(Data, Offsets.EventFlagLand, value, 0, sizeof(short) * value.Length);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetEventFlagLand(short[] value)
|
||||
{
|
||||
Buffer.BlockCopy(value, 0, Data, Offsets.EventFlagLand, sizeof(short) * value.Length);
|
||||
}
|
||||
|
||||
public TurnipStonk Turnips
|
||||
{
|
||||
get => Data.Slice(Offsets.ShopKabu, TurnipStonk.SIZE).ToClass<TurnipStonk>();
|
||||
set => value.ToBytesClass().CopyTo(Data, Offsets.ShopKabu);
|
||||
}
|
||||
|
||||
public Museum Museum
|
||||
{
|
||||
get => new(Data.Slice(Offsets.Museum, Museum.SIZE));
|
||||
set => value.Data.CopyTo(Data, Offsets.Museum);
|
||||
}
|
||||
|
||||
public const int AcreWidth = 7 + (2 * 1); // 1 on each side cannot be traversed
|
||||
private const int AcreHeight = 6 + (2 * 1); // 1 on each side cannot be traversed
|
||||
private const int AcreMax = AcreWidth * AcreHeight;
|
||||
private const int AcreSizeAll = AcreMax * 2;
|
||||
|
||||
public ushort GetAcre(int index)
|
||||
{
|
||||
if ((uint)index > AcreMax)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return BitConverter.ToUInt16(Data, Offsets.OutsideField + (index * 2));
|
||||
}
|
||||
|
||||
public void SetAcre(int index, ushort value)
|
||||
{
|
||||
if ((uint)index > AcreMax)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
BitConverter.GetBytes(value).CopyTo(Data, Offsets.OutsideField + (index * 2));
|
||||
}
|
||||
|
||||
public byte[] GetAcreBytes() => Data.Slice(Offsets.OutsideField, AcreSizeAll);
|
||||
|
||||
public void SetAcreBytes(byte[] data)
|
||||
{
|
||||
if (data.Length != AcreSizeAll)
|
||||
throw new ArgumentOutOfRangeException(nameof(data.Length));
|
||||
data.CopyTo(Data, Offsets.OutsideField);
|
||||
}
|
||||
|
||||
public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, MapGrid.MapTileCount16x16 * TerrainTile.SIZE));
|
||||
public void SetTerrainTiles(IReadOnlyList<TerrainTile> array) => TerrainTile.SetArray(array).CopyTo(Data, Offsets.LandMakingMap);
|
||||
|
||||
public const int MapDesignNone = 0xF800;
|
||||
|
||||
public ushort[] GetMapDesignTiles()
|
||||
{
|
||||
var value = new ushort[112*96];
|
||||
Buffer.BlockCopy(Data, Offsets.MyDesignMap, value, 0, sizeof(ushort) * value.Length);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetMapDesignTiles(ushort[] value)
|
||||
{
|
||||
Buffer.BlockCopy(value, 0, Data, Offsets.MyDesignMap, sizeof(ushort) * value.Length);
|
||||
}
|
||||
|
||||
private const int FieldItemLayerSize = MapGrid.MapTileCount32x32 * Item.SIZE;
|
||||
private const int FieldItemFlagSize = MapGrid.MapTileCount32x32 / 8; // bitflags
|
||||
|
||||
private int FieldItemLayer1 => Offsets.FieldItem;
|
||||
private int FieldItemLayer2 => Offsets.FieldItem + FieldItemLayerSize;
|
||||
public int FieldItemFlag1 => Offsets.FieldItem + (FieldItemLayerSize * 2);
|
||||
public int FieldItemFlag2 => Offsets.FieldItem + (FieldItemLayerSize * 2) + FieldItemFlagSize;
|
||||
|
||||
public Item[] GetFieldItemLayer1() => Item.GetArray(Data.Slice(FieldItemLayer1, FieldItemLayerSize));
|
||||
public void SetFieldItemLayer1(IReadOnlyList<Item> array) => Item.SetArray(array).CopyTo(Data, FieldItemLayer1);
|
||||
|
||||
public Item[] GetFieldItemLayer2() => Item.GetArray(Data.Slice(FieldItemLayer2, FieldItemLayerSize));
|
||||
public void SetFieldItemLayer2(IReadOnlyList<Item> array) => Item.SetArray(array).CopyTo(Data, FieldItemLayer2);
|
||||
|
||||
public ushort OutsideFieldTemplateUniqueId
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, Offsets.OutsideField + AcreSizeAll);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.OutsideField + AcreSizeAll);
|
||||
}
|
||||
|
||||
public ushort MainFieldParamUniqueID
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, Offsets.OutsideField + AcreSizeAll + 2);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.OutsideField + AcreSizeAll + 2);
|
||||
}
|
||||
|
||||
public uint EventPlazaLeftUpX
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, Offsets.OutsideField + AcreSizeAll + 4);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.OutsideField + AcreSizeAll + 4);
|
||||
}
|
||||
|
||||
public uint EventPlazaLeftUpZ
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, Offsets.OutsideField + AcreSizeAll + 8);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.OutsideField + AcreSizeAll + 8);
|
||||
}
|
||||
|
||||
public GSaveVisitorNpc Visitor
|
||||
{
|
||||
get => Data.ToClass<GSaveVisitorNpc>(Offsets.Visitor, GSaveVisitorNpc.SIZE);
|
||||
set => value.ToBytesClass().CopyTo(Data, Offsets.Visitor);
|
||||
}
|
||||
|
||||
public GSaveFg SaveFg
|
||||
{
|
||||
get => Data.ToClass<GSaveFg>(Offsets.SaveFg, GSaveFg.SIZE);
|
||||
set => value.ToBytesClass().CopyTo(Data, Offsets.SaveFg);
|
||||
}
|
||||
|
||||
public GSaveTime LastSaved => Data.Slice(Offsets.LastSavedTime, GSaveTime.SIZE).ToStructure<GSaveTime>();
|
||||
|
||||
public GSaveBulletinBoard Bulletin
|
||||
{
|
||||
get => Data.Slice(Offsets.BulletinBoard, GSaveBulletinBoard.SIZE).ToStructure<GSaveBulletinBoard>();
|
||||
set => value.ToBytes().CopyTo(Data, Offsets.BulletinBoard);
|
||||
}
|
||||
get => ReadUInt32LittleEndian(Data[Offsets.WeatherRandSeed..]);
|
||||
set => WriteUInt32LittleEndian(Data[Offsets.WeatherRandSeed..], value);
|
||||
}
|
||||
}
|
||||
|
||||
public IVillager GetVillager(int index) => Offsets.ReadVillager(Data, index);
|
||||
public void SetVillager(IVillager value, int index) => Offsets.WriteVillager(value, Data, index);
|
||||
|
||||
public IVillagerHouse GetVillagerHouse(int index) => Offsets.ReadVillagerHouse(Data, index);
|
||||
public void SetVillagerHouse(IVillagerHouse value, int index) => Offsets.WriteVillagerHouse(value, Data, index);
|
||||
|
||||
public IVillager[] GetVillagers()
|
||||
{
|
||||
var villagers = new IVillager[MainSaveOffsets.VillagerCount];
|
||||
for (int i = 0; i < villagers.Length; i++)
|
||||
villagers[i] = GetVillager(i);
|
||||
return villagers;
|
||||
}
|
||||
|
||||
public void SetVillagers(IReadOnlyList<IVillager> villagers)
|
||||
{
|
||||
for (int i = 0; i < villagers.Count; i++)
|
||||
SetVillager(villagers[i], i);
|
||||
}
|
||||
|
||||
public IVillagerHouse[] GetVillagerHouses()
|
||||
{
|
||||
var villagers = new IVillagerHouse[MainSaveOffsets.VillagerCount];
|
||||
for (int i = 0; i < villagers.Length; i++)
|
||||
villagers[i] = GetVillagerHouse(i);
|
||||
return villagers;
|
||||
}
|
||||
|
||||
public void SetVillagerHouses(IReadOnlyList<IVillagerHouse> villagers)
|
||||
{
|
||||
for (int i = 0; i < villagers.Count; i++)
|
||||
SetVillagerHouse(villagers[i], i);
|
||||
}
|
||||
|
||||
public DesignPattern GetDesign(int index) => Offsets.ReadPattern(Data, index);
|
||||
public void SetDesign(DesignPattern value, int index, Span<byte> playerID, Span<byte> townID) => Offsets.WritePattern(value, Data, index, playerID, townID);
|
||||
public DesignPatternPRO GetDesignPRO(int index) => Offsets.ReadPatternPRO(Data, index);
|
||||
public void SetDesignPRO(DesignPatternPRO value, int index, Span<byte> playerID, Span<byte> townID) => Offsets.WritePatternPRO(value, Data, index, playerID, townID);
|
||||
|
||||
public IReadOnlyList<Item> RecycleBin
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.LostItemBox, MainSaveOffsets.RecycleBinCount * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data[Offsets.LostItemBox..]);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Building> Buildings
|
||||
{
|
||||
get => Building.GetArray(Data.Slice(Offsets.MainFieldStructure, MainSaveOffsets.BuildingCount * Building.SIZE));
|
||||
set => Building.SetArray(value).CopyTo(Data[Offsets.MainFieldStructure..]);
|
||||
}
|
||||
|
||||
public IPlayerHouse GetPlayerHouse(int index) => Offsets.ReadPlayerHouse(Data, index);
|
||||
public void SetPlayerHouse(IPlayerHouse value, int index) => Offsets.WritePlayerHouse(value, Data, index);
|
||||
|
||||
public IPlayerHouse[] GetPlayerHouses()
|
||||
{
|
||||
var players = new IPlayerHouse[MainSaveOffsets.PlayerCount];
|
||||
for (int i = 0; i < players.Length; i++)
|
||||
players[i] = GetPlayerHouse(i);
|
||||
return players;
|
||||
}
|
||||
|
||||
public void SetPlayerHouses(IReadOnlyList<IPlayerHouse> houses)
|
||||
{
|
||||
for (int i = 0; i < houses.Count; i++)
|
||||
SetPlayerHouse(houses[i], i);
|
||||
}
|
||||
|
||||
public DesignPattern[] GetDesigns()
|
||||
{
|
||||
var result = new DesignPattern[Offsets.PatternCount];
|
||||
for (int i = 0; i <result.Length; i++)
|
||||
result[i] = GetDesign(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetDesigns(IReadOnlyList<DesignPattern> value, Span<byte> playerID, Span<byte> townID)
|
||||
{
|
||||
var count = Math.Min(Offsets.PatternCount, value.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
SetDesign(value[i], i, playerID, townID);
|
||||
}
|
||||
|
||||
public DesignPatternPRO[] GetDesignsPRO()
|
||||
{
|
||||
var result = new DesignPatternPRO[Offsets.PatternCount];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = GetDesignPRO(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetDesignsPRO(IReadOnlyList<DesignPatternPRO> value, Span<byte> playerID, Span<byte> townID)
|
||||
{
|
||||
var count = Math.Min(Offsets.PatternCount, value.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
SetDesignPRO(value[i], i, playerID, townID);
|
||||
}
|
||||
|
||||
public DesignPattern FlagMyDesign
|
||||
{
|
||||
get => MainSaveOffsets.ReadPatternAtOffset(Data, Offsets.PatternFlag);
|
||||
set => value.Data.CopyTo(Data[Offsets.PatternFlag..]);
|
||||
}
|
||||
|
||||
public DesignPatternPRO[] GetDesignsTailor()
|
||||
{
|
||||
var result = new DesignPatternPRO[MainSaveOffsets.PatternTailorCount];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = MainSaveOffsets.ReadPatternPROAtOffset(Data, Offsets.PatternTailor + (i * DesignPatternPRO.SIZE));
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetDesignsTailor(IReadOnlyList<DesignPatternPRO> value)
|
||||
{
|
||||
var count = Math.Min(Offsets.PatternCount, value.Count);
|
||||
for (int i = 0; i < count; i++)
|
||||
value[i].Data.CopyTo(Data.Slice(Offsets.PatternTailor + (i * DesignPatternPRO.SIZE)));
|
||||
}
|
||||
|
||||
private const int EventFlagsSaveCount = 0x400;
|
||||
|
||||
public Span<short> EventFlagLand => MemoryMarshal.Cast<byte, short>(Data.Slice(Offsets.EventFlagLand, sizeof(short) * EventFlagsSaveCount));
|
||||
|
||||
public TurnipStonk Turnips
|
||||
{
|
||||
get => Data.Slice(Offsets.ShopKabu, TurnipStonk.SIZE).ToArray().ToClass<TurnipStonk>();
|
||||
set => value.ToBytesClass().CopyTo(Data[Offsets.ShopKabu..]);
|
||||
}
|
||||
|
||||
public Museum Museum
|
||||
{
|
||||
get => new(Data.Slice(Offsets.Museum, Museum.SIZE).ToArray());
|
||||
set => value.Data.CopyTo(Data[Offsets.Museum..]);
|
||||
}
|
||||
|
||||
public const int AcreWidth = 7 + (2 * 1); // 1 on each side cannot be traversed
|
||||
private const int AcreHeight = 6 + (2 * 1); // 1 on each side cannot be traversed
|
||||
private const int AcreMax = AcreWidth * AcreHeight;
|
||||
private const int AcreSizeAll = AcreMax * 2;
|
||||
|
||||
public ushort GetAcre(int index)
|
||||
{
|
||||
if ((uint)index > AcreMax)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return ReadUInt16LittleEndian(Data.Slice(Offsets.OutsideField + (index * 2)));
|
||||
}
|
||||
|
||||
public void SetAcre(int index, ushort value)
|
||||
{
|
||||
if ((uint)index > AcreMax)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
WriteUInt16LittleEndian(Data.Slice(Offsets.OutsideField + (index * 2)), value);
|
||||
}
|
||||
|
||||
public byte[] GetAcreBytes() => Data.Slice(Offsets.OutsideField, AcreSizeAll).ToArray();
|
||||
|
||||
public void SetAcreBytes(ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (data.Length != AcreSizeAll)
|
||||
throw new ArgumentOutOfRangeException(nameof(data.Length));
|
||||
data.CopyTo(Data.Slice(Offsets.OutsideField));
|
||||
}
|
||||
|
||||
public TerrainTile[] GetTerrainTiles() => TerrainTile.GetArray(Data.Slice(Offsets.LandMakingMap, MapGrid.MapTileCount16x16 * TerrainTile.SIZE));
|
||||
public void SetTerrainTiles(IReadOnlyList<TerrainTile> array) => TerrainTile.SetArray(array).CopyTo(Data[Offsets.LandMakingMap..]);
|
||||
|
||||
public const int MapDesignNone = 0xF800;
|
||||
|
||||
public ushort[] GetMapDesignTiles()
|
||||
{
|
||||
var slice = Data.Slice(Offsets.MyDesignMap, 112 * 96 * sizeof(ushort));
|
||||
return MemoryMarshal.Cast<byte, ushort>(slice).ToArray();
|
||||
}
|
||||
|
||||
public void SetMapDesignTiles(ReadOnlySpan<ushort> value)
|
||||
{
|
||||
var cast = MemoryMarshal.Cast<ushort, byte>(value);
|
||||
cast.CopyTo(Data[Offsets.MyDesignMap..]);
|
||||
}
|
||||
|
||||
private const int FieldItemLayerSize = MapGrid.MapTileCount32x32 * Item.SIZE;
|
||||
private const int FieldItemFlagSize = MapGrid.MapTileCount32x32 / 8; // bitflags
|
||||
|
||||
private int FieldItemLayer1 => Offsets.FieldItem;
|
||||
private int FieldItemLayer2 => Offsets.FieldItem + FieldItemLayerSize;
|
||||
public int FieldItemFlag1 => Offsets.FieldItem + (FieldItemLayerSize * 2);
|
||||
public int FieldItemFlag2 => Offsets.FieldItem + (FieldItemLayerSize * 2) + FieldItemFlagSize;
|
||||
|
||||
public Item[] GetFieldItemLayer1() => Item.GetArray(Data.Slice(FieldItemLayer1, FieldItemLayerSize));
|
||||
public void SetFieldItemLayer1(IReadOnlyList<Item> array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer1..]);
|
||||
|
||||
public Item[] GetFieldItemLayer2() => Item.GetArray(Data.Slice(FieldItemLayer2, FieldItemLayerSize));
|
||||
public void SetFieldItemLayer2(IReadOnlyList<Item> array) => Item.SetArray(array).CopyTo(Data[FieldItemLayer2..]);
|
||||
|
||||
public ushort OutsideFieldTemplateUniqueId
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll)..]);
|
||||
set => WriteUInt16LittleEndian(Data[(Offsets.OutsideField + AcreSizeAll)..], value);
|
||||
}
|
||||
|
||||
public ushort MainFieldParamUniqueID
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 2));
|
||||
set => WriteUInt16LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 2), value);
|
||||
}
|
||||
|
||||
public uint EventPlazaLeftUpX
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 4));
|
||||
set => WriteUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 4), value);
|
||||
}
|
||||
|
||||
public uint EventPlazaLeftUpZ
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 8));
|
||||
set => WriteUInt32LittleEndian(Data.Slice(Offsets.OutsideField + AcreSizeAll + 8), value);
|
||||
}
|
||||
|
||||
public GSaveVisitorNpc Visitor
|
||||
{
|
||||
get => Data.Slice(Offsets.Visitor, GSaveVisitorNpc.SIZE).ToArray().ToClass<GSaveVisitorNpc>();
|
||||
set => value.ToBytesClass().CopyTo(Data[Offsets.Visitor..]);
|
||||
}
|
||||
|
||||
public GSaveFg SaveFg
|
||||
{
|
||||
get => Data.Slice(Offsets.SaveFg, GSaveFg.SIZE).ToArray().ToClass<GSaveFg>();
|
||||
set => value.ToBytesClass().CopyTo(Data[Offsets.SaveFg..]);
|
||||
}
|
||||
|
||||
public GSaveTime LastSaved => Data.Slice(Offsets.LastSavedTime, GSaveTime.SIZE).ToStructure<GSaveTime>();
|
||||
|
||||
public GSaveBulletinBoard Bulletin
|
||||
{
|
||||
get => Data.Slice(Offsets.BulletinBoard, GSaveBulletinBoard.SIZE).ToStructure<GSaveBulletinBoard>();
|
||||
set => value.ToBytes().CopyTo(Data[Offsets.BulletinBoard..]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,172 +1,177 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// personal.dat
|
||||
/// </summary>
|
||||
public sealed class Personal : EncryptedFilePair, IVillagerOrigin
|
||||
{
|
||||
/// <summary>
|
||||
/// personal.dat
|
||||
/// </summary>
|
||||
public sealed class Personal : EncryptedFilePair, IVillagerOrigin
|
||||
public readonly PersonalOffsets Offsets;
|
||||
public Personal(string folder) : base(folder, "personal") => Offsets = PersonalOffsets.GetOffsets(Info);
|
||||
public override string ToString() => PlayerName;
|
||||
|
||||
public uint TownID
|
||||
{
|
||||
public readonly PersonalOffsets Offsets;
|
||||
public Personal(string folder) : base(folder, "personal") => Offsets = PersonalOffsets.GetOffsets(Info);
|
||||
public override string ToString() => PlayerName;
|
||||
|
||||
public uint TownID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, Offsets.PersonalId + 0x00);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.PersonalId + 0x00);
|
||||
}
|
||||
|
||||
public string TownName
|
||||
{
|
||||
get => GetString(Offsets.PersonalId + 0x04, 10);
|
||||
set => GetBytes(value, 10).CopyTo(Data, Offsets.PersonalId + 0x04);
|
||||
}
|
||||
|
||||
public byte[] GetTownIdentity() => Data.Slice(Offsets.PersonalId + 0x00, 4 + 20);
|
||||
|
||||
public uint PlayerID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, Offsets.PersonalId + 0x1C);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.PersonalId + 0x1C);
|
||||
}
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => GetString(Offsets.PersonalId + 0x20, 10);
|
||||
set => GetBytes(value, 10).CopyTo(Data, Offsets.PersonalId + 0x20);
|
||||
}
|
||||
|
||||
public byte[] GetPlayerIdentity() => Data.Slice(Offsets.PersonalId + 0x1C, 4 + 20);
|
||||
|
||||
public EncryptedInt32 Wallet
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.Wallet);
|
||||
set => value.Write(Data, Offsets.Wallet);
|
||||
}
|
||||
|
||||
public EncryptedInt32 Bank
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.Bank);
|
||||
set => value.Write(Data, Offsets.Bank);
|
||||
}
|
||||
|
||||
public EncryptedInt32 NookMiles
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.NowPoint);
|
||||
set => value.Write(Data, Offsets.NowPoint);
|
||||
}
|
||||
|
||||
public EncryptedInt32 TotalNookMiles
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.TotalPoint);
|
||||
set => value.Write(Data, Offsets.TotalPoint);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Item> Bag // Slots 21-40
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.Pockets1, Offsets.Pockets1Count * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data, Offsets.Pockets1);
|
||||
}
|
||||
|
||||
public uint BagCount // Count of the Bag Slots that are available for use
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, Offsets.Pockets1 + (Offsets.Pockets1Count * Item.SIZE));
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.Pockets1 + (Offsets.Pockets1Count * Item.SIZE));
|
||||
}
|
||||
|
||||
public IReadOnlyList<Item> Pocket // Slots 1-20
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.Pockets2, Offsets.Pockets2Count * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data, Offsets.Pockets2);
|
||||
}
|
||||
|
||||
public uint PocketCount // Count of the Pocket Slots that are available for use
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, Offsets.Pockets2 + (Offsets.Pockets2Count * Item.SIZE));
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.Pockets2 + (Offsets.Pockets2Count * Item.SIZE));
|
||||
}
|
||||
|
||||
public IReadOnlyList<Item> ItemChest
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.ItemChest, Offsets.ItemChestCount * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data, Offsets.ItemChest);
|
||||
}
|
||||
|
||||
public uint ItemChestCount // Count of the Item Chest Slots that are available for use
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, Offsets.ItemChest + (Offsets.ItemChestCount * Item.SIZE));
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.ItemChest + (Offsets.ItemChestCount * Item.SIZE));
|
||||
}
|
||||
|
||||
public IReactionStore Reactions
|
||||
{
|
||||
get => Offsets.ReadReactions(Data);
|
||||
set => Offsets.SetReactions(Data, value);
|
||||
}
|
||||
|
||||
public AchievementList Achievements
|
||||
{
|
||||
get => Data.Slice(Offsets.CountAchievement, AchievementList.SIZE).ToStructure<AchievementList>();
|
||||
set => value.ToBytes().CopyTo(Data, Offsets.CountAchievement);
|
||||
}
|
||||
|
||||
public RecipeBook GetRecipeBook() => new(Data.Slice(Offsets.Recipes, RecipeBook.SIZE));
|
||||
public void SetRecipeBook(RecipeBook book) => book.Save(Data, Offsets.Recipes);
|
||||
|
||||
public short[] GetEventFlagsPlayer()
|
||||
{
|
||||
var result = new short[0xE00/2];
|
||||
Buffer.BlockCopy(Data, Offsets.EventFlagsPlayer, result, 0, result.Length * sizeof(short));
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetEventFlagsPlayer(short[] value) => Buffer.BlockCopy(value, 0, Data, Offsets.EventFlagsPlayer, value.Length * sizeof(short));
|
||||
|
||||
public GSaveDateMD Birthday
|
||||
{
|
||||
get => Data.ToStructure<GSaveDateMD>(Offsets.Birthday, GSaveDateMD.SIZE);
|
||||
set => value.ToBytes().CopyTo(Data, Offsets.Birthday);
|
||||
}
|
||||
|
||||
#region Profile
|
||||
|
||||
public byte[] GetPhotoData()
|
||||
{
|
||||
var offset = Offsets.ProfilePhoto;
|
||||
|
||||
// Expect jpeg marker
|
||||
if (BitConverter.ToUInt16(Data, offset) != 0xD8FF)
|
||||
return Array.Empty<byte>();
|
||||
var len = BitConverter.ToInt32(Data, offset - 4);
|
||||
return Data.Slice(offset, len);
|
||||
}
|
||||
|
||||
public GSaveDateMD ProfileBirthday
|
||||
{
|
||||
get => Data.ToStructure<GSaveDateMD>(Offsets.ProfileBirthday, GSaveDateMD.SIZE);
|
||||
set => value.ToBytes().CopyTo(Data, Offsets.ProfileBirthday);
|
||||
}
|
||||
|
||||
public ushort ProfileFruit
|
||||
{
|
||||
get => BitConverter.ToUInt16(Data, Offsets.ProfileFruit);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.ProfileFruit);
|
||||
}
|
||||
|
||||
public GSaveDate ProfileTimestamp
|
||||
{
|
||||
get => Data.ToStructure<GSaveDate>(Offsets.ProfileTimestamp, GSaveDate.SIZE);
|
||||
set => value.ToBytes().CopyTo(Data, Offsets.ProfileTimestamp);
|
||||
}
|
||||
|
||||
public bool ProfileIsMakeVillage
|
||||
{
|
||||
get => Data[Offsets.ProfileIsMakeVillage] != 0;
|
||||
set => Data[Offsets.ProfileIsMakeVillage] = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
get => ReadUInt32LittleEndian(Data[(Offsets.PersonalId + 0x00)..]);
|
||||
set => WriteUInt32LittleEndian(Data[(Offsets.PersonalId + 0x00)..], value);
|
||||
}
|
||||
|
||||
public string TownName
|
||||
{
|
||||
get => GetString(Offsets.PersonalId + 0x04, 10);
|
||||
set => GetBytes(value, 10).CopyTo(Data[(Offsets.PersonalId + 0x04)..]);
|
||||
}
|
||||
|
||||
public Span<byte> GetTownIdentity() => Data.Slice(Offsets.PersonalId + 0x00, 4 + 20);
|
||||
|
||||
public uint PlayerID
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[(Offsets.PersonalId + 0x1C)..]);
|
||||
set => WriteUInt32LittleEndian(Data[(Offsets.PersonalId + 0x1C)..], value);
|
||||
}
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => GetString(Offsets.PersonalId + 0x20, 10);
|
||||
set => GetBytes(value, 10).CopyTo(Data[(Offsets.PersonalId + 0x20)..]);
|
||||
}
|
||||
|
||||
public Span<byte> GetPlayerIdentity() => Data.Slice(Offsets.PersonalId + 0x1C, 4 + 20);
|
||||
|
||||
public EncryptedInt32 Wallet
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.Wallet);
|
||||
set => value.Write(Data[Offsets.Wallet..]);
|
||||
}
|
||||
|
||||
public EncryptedInt32 Bank
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.Bank);
|
||||
set => value.Write(Data[Offsets.Bank..]);
|
||||
}
|
||||
|
||||
public EncryptedInt32 NookMiles
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.NowPoint);
|
||||
set => value.Write(Data[Offsets.NowPoint..]);
|
||||
}
|
||||
|
||||
public EncryptedInt32 TotalNookMiles
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.TotalPoint);
|
||||
set => value.Write(Data[Offsets.TotalPoint..]);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Item> Bag // Slots 21-40
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.Pockets1, Offsets.Pockets1Count * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data[Offsets.Pockets1..]);
|
||||
}
|
||||
|
||||
public uint BagCount // Count of the Bag Slots that are available for use
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[(Offsets.Pockets1 + (Offsets.Pockets1Count * Item.SIZE))..]);
|
||||
set => WriteUInt32LittleEndian(Data[(Offsets.Pockets1 + (Offsets.Pockets1Count * Item.SIZE))..], value);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Item> Pocket // Slots 1-20
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.Pockets2, Offsets.Pockets2Count * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data[Offsets.Pockets2..]);
|
||||
}
|
||||
|
||||
public uint PocketCount // Count of the Pocket Slots that are available for use
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[(Offsets.Pockets2 + (Offsets.Pockets2Count * Item.SIZE))..]);
|
||||
set => WriteUInt32LittleEndian(Data[(Offsets.Pockets2 + (Offsets.Pockets2Count * Item.SIZE))..], value);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Item> ItemChest
|
||||
{
|
||||
get => Item.GetArray(Data.Slice(Offsets.ItemChest, Offsets.ItemChestCount * Item.SIZE));
|
||||
set => Item.SetArray(value).CopyTo(Data[Offsets.ItemChest..]);
|
||||
}
|
||||
|
||||
public uint ItemChestCount // Count of the Item Chest Slots that are available for use
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[(Offsets.ItemChest + (Offsets.ItemChestCount * Item.SIZE))..]);
|
||||
set => WriteUInt32LittleEndian(Data[(Offsets.ItemChest + (Offsets.ItemChestCount * Item.SIZE))..], value);
|
||||
}
|
||||
|
||||
public IReactionStore Reactions
|
||||
{
|
||||
get => Offsets.ReadReactions(Data);
|
||||
set => Offsets.SetReactions(Data, value);
|
||||
}
|
||||
|
||||
public AchievementList Achievements
|
||||
{
|
||||
get => Data.Slice(Offsets.CountAchievement, AchievementList.SIZE).ToStructure<AchievementList>();
|
||||
set => value.ToBytes().CopyTo(Data[Offsets.CountAchievement..]);
|
||||
}
|
||||
|
||||
public RecipeBook GetRecipeBook() => new(Data.Slice(Offsets.Recipes, RecipeBook.SIZE).ToArray());
|
||||
public void SetRecipeBook(RecipeBook book) => book.Save(Data[Offsets.Recipes..]);
|
||||
|
||||
public short[] GetEventFlagsPlayer()
|
||||
{
|
||||
var slice = Data.Slice(Offsets.EventFlagsPlayer, 0xE00);
|
||||
return MemoryMarshal.Cast<byte, short>(slice).ToArray();
|
||||
}
|
||||
|
||||
public void SetEventFlagsPlayer(Span<short> value)
|
||||
{
|
||||
var slice = Data.Slice(Offsets.EventFlagsPlayer, 0xE00);
|
||||
var cast = MemoryMarshal.Cast<byte, short>(slice);
|
||||
value.CopyTo(cast);
|
||||
}
|
||||
|
||||
public GSaveDateMD Birthday
|
||||
{
|
||||
get => Data.ToStructure<GSaveDateMD>(Offsets.Birthday, GSaveDateMD.SIZE);
|
||||
set => value.ToBytes().CopyTo(Data[Offsets.Birthday..]);
|
||||
}
|
||||
|
||||
#region Profile
|
||||
|
||||
public byte[] GetPhotoData()
|
||||
{
|
||||
var offset = Offsets.ProfilePhoto;
|
||||
|
||||
// Expect jpeg marker
|
||||
if (ReadUInt16LittleEndian(Data[offset..]) != 0xD8FF)
|
||||
return [];
|
||||
var len = ReadInt32LittleEndian(Data[(offset - 4)..]);
|
||||
return Data.Slice(offset, len).ToArray();
|
||||
}
|
||||
|
||||
public GSaveDateMD ProfileBirthday
|
||||
{
|
||||
get => Data.ToStructure<GSaveDateMD>(Offsets.ProfileBirthday, GSaveDateMD.SIZE);
|
||||
set => value.ToBytes().CopyTo(Data[Offsets.ProfileBirthday..]);
|
||||
}
|
||||
|
||||
public ushort ProfileFruit
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data[Offsets.ProfileFruit..]);
|
||||
set => WriteUInt16LittleEndian(Data[Offsets.ProfileFruit..], value);
|
||||
}
|
||||
|
||||
public GSaveDate ProfileTimestamp
|
||||
{
|
||||
get => Data.ToStructure<GSaveDate>(Offsets.ProfileTimestamp, GSaveDate.SIZE);
|
||||
set => value.ToBytes().CopyTo(Data[Offsets.ProfileTimestamp..]);
|
||||
}
|
||||
|
||||
public bool ProfileIsMakeVillage
|
||||
{
|
||||
get => Data[Offsets.ProfileIsMakeVillage] != 0;
|
||||
set => Data[Offsets.ProfileIsMakeVillage] = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// photo_studio_island.dat
|
||||
/// </summary>
|
||||
public sealed class PhotoStudioIsland : EncryptedFilePair
|
||||
{
|
||||
/// <summary>
|
||||
/// photo_studio_island.dat
|
||||
/// </summary>
|
||||
public sealed class PhotoStudioIsland : EncryptedFilePair
|
||||
{
|
||||
public PhotoStudioIsland(string folder) : base(folder, "photo_studio_island") { }
|
||||
}
|
||||
public PhotoStudioIsland(string folder) : base(folder, "photo_studio_island") { }
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// postbox.dat
|
||||
/// </summary>
|
||||
public sealed class PostBox : EncryptedFilePair
|
||||
{
|
||||
/// <summary>
|
||||
/// postbox.dat
|
||||
/// </summary>
|
||||
public sealed class PostBox : EncryptedFilePair
|
||||
{
|
||||
public PostBox(string folder) : base(folder, "postbox") { }
|
||||
}
|
||||
public PostBox(string folder) : base(folder, "postbox") { }
|
||||
}
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
namespace NHSE.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// profile.dat
|
||||
/// </summary>
|
||||
public sealed class Profile : EncryptedFilePair
|
||||
{
|
||||
public Profile(string folder) : base(folder, "profile") { }
|
||||
namespace NHSE.Core;
|
||||
|
||||
// pretty much just a jpeg -- which is also stored in Personal.
|
||||
}
|
||||
/// <summary>
|
||||
/// profile.dat
|
||||
/// </summary>
|
||||
public sealed class Profile : EncryptedFilePair
|
||||
{
|
||||
public Profile(string folder) : base(folder, "profile") { }
|
||||
|
||||
// pretty much just a jpeg -- which is also stored in Personal.
|
||||
}
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public sealed class WhereAreN : EncryptedFilePair
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
public sealed class WhereAreN : EncryptedFilePair
|
||||
public const string FileName = "wherearen";
|
||||
|
||||
public readonly WhereAreNOffsets Offsets;
|
||||
public WhereAreN(string folder) : base(folder, FileName) => Offsets = WhereAreNOffsets.GetOffsets(Info);
|
||||
|
||||
public EncryptedInt32 Poki
|
||||
{
|
||||
public const string FileName = "wherearen";
|
||||
|
||||
public readonly WhereAreNOffsets Offsets;
|
||||
public WhereAreN(string folder) : base(folder, FileName) => Offsets = WhereAreNOffsets.GetOffsets(Info);
|
||||
|
||||
public EncryptedInt32 Poki
|
||||
{
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.Poki);
|
||||
set => value.Write(Data, Offsets.Poki);
|
||||
}
|
||||
get => EncryptedInt32.ReadVerify(Data, Offsets.Poki);
|
||||
set => value.Write(Data[Offsets.Poki..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents two files -- <see cref="DataPath"/> and <see cref="HeaderPath"/> and their decrypted data.
|
||||
/// </summary>
|
||||
public abstract class EncryptedFilePair
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents two files -- <see cref="DataPath"/> and <see cref="HeaderPath"/> and their decrypted data.
|
||||
/// </summary>
|
||||
public abstract class EncryptedFilePair
|
||||
private readonly byte[] RawData;
|
||||
private readonly byte[] RawHeader;
|
||||
|
||||
public Span<byte> Data => RawData;
|
||||
public Span<byte> Header => RawHeader;
|
||||
|
||||
public readonly FileHeaderInfo Info;
|
||||
|
||||
public readonly string DataPath;
|
||||
public readonly string HeaderPath;
|
||||
public readonly string NameData;
|
||||
public readonly string NameHeader;
|
||||
|
||||
public static bool Exists(string folder, string name)
|
||||
{
|
||||
public readonly byte[] Data;
|
||||
public readonly byte[] Header;
|
||||
|
||||
public readonly FileHeaderInfo Info;
|
||||
|
||||
public readonly string DataPath;
|
||||
public readonly string HeaderPath;
|
||||
public readonly string NameData;
|
||||
public readonly string NameHeader;
|
||||
|
||||
public static bool Exists(string folder, string name)
|
||||
{
|
||||
var NameData = $"{name}.dat";
|
||||
var NameHeader = $"{name}Header.dat";
|
||||
var hdr = Path.Combine(folder, NameHeader);
|
||||
var dat = Path.Combine(folder, NameData);
|
||||
return File.Exists(hdr) && File.Exists(dat);
|
||||
}
|
||||
|
||||
protected EncryptedFilePair(string folder, string name)
|
||||
{
|
||||
NameData = $"{name}.dat";
|
||||
NameHeader = $"{name}Header.dat";
|
||||
var hdr = Path.Combine(folder, NameHeader);
|
||||
var dat = Path.Combine(folder, NameData);
|
||||
|
||||
var hd = File.ReadAllBytes(hdr);
|
||||
var md = File.ReadAllBytes(dat);
|
||||
|
||||
Encryption.Decrypt(hd, md);
|
||||
|
||||
Header = hd;
|
||||
Data = md;
|
||||
DataPath = dat;
|
||||
HeaderPath = hdr;
|
||||
|
||||
Info = Header.Slice(0, FileHeaderInfo.SIZE).ToClass<FileHeaderInfo>();
|
||||
}
|
||||
|
||||
public void Save(uint seed)
|
||||
{
|
||||
var encrypt = Encryption.Encrypt(Data, seed, Header);
|
||||
File.WriteAllBytes(DataPath, encrypt.Data);
|
||||
File.WriteAllBytes(HeaderPath, encrypt.Header);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates all hashes of <see cref="Data"/>.
|
||||
/// </summary>
|
||||
public void Hash()
|
||||
{
|
||||
var ver = Info.GetKnownRevisionIndex();
|
||||
var hash = RevisionChecker.HashInfo[ver];
|
||||
var details = hash.GetFile(NameData);
|
||||
if (details == null)
|
||||
throw new ArgumentNullException(nameof(NameData));
|
||||
foreach (var h in details.HashRegions)
|
||||
Murmur3.UpdateMurmur32(Data, h.HashOffset, h.BeginOffset, (uint)h.Size);
|
||||
}
|
||||
|
||||
public IEnumerable<FileHashRegion> InvalidHashes()
|
||||
{
|
||||
var ver = Info.GetKnownRevisionIndex();
|
||||
var hash = RevisionChecker.HashInfo[ver];
|
||||
var details = hash.GetFile(NameData);
|
||||
if (details == null)
|
||||
throw new ArgumentNullException(nameof(NameData));
|
||||
foreach (var h in details.HashRegions)
|
||||
{
|
||||
var current = Murmur3.GetMurmur3Hash(Data, h.BeginOffset, (uint)h.Size);
|
||||
var saved = BitConverter.ToUInt32(Data, h.HashOffset);
|
||||
if (current != saved)
|
||||
yield return h;
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetString(int offset, int maxLength) => StringUtil.GetString(Data, offset, maxLength);
|
||||
protected static byte[] GetBytes(string value, int maxLength) => StringUtil.GetBytes(value, maxLength);
|
||||
var NameData = $"{name}.dat";
|
||||
var NameHeader = $"{name}Header.dat";
|
||||
var hdr = Path.Combine(folder, NameHeader);
|
||||
var dat = Path.Combine(folder, NameData);
|
||||
return File.Exists(hdr) && File.Exists(dat);
|
||||
}
|
||||
|
||||
protected EncryptedFilePair(string folder, string name)
|
||||
{
|
||||
NameData = $"{name}.dat";
|
||||
NameHeader = $"{name}Header.dat";
|
||||
var hdr = Path.Combine(folder, NameHeader);
|
||||
var dat = Path.Combine(folder, NameData);
|
||||
|
||||
var hd = File.ReadAllBytes(hdr);
|
||||
var md = File.ReadAllBytes(dat);
|
||||
|
||||
Encryption.Decrypt(hd, md);
|
||||
|
||||
RawHeader = hd;
|
||||
RawData = md;
|
||||
DataPath = dat;
|
||||
HeaderPath = hdr;
|
||||
|
||||
Info = RawHeader[..FileHeaderInfo.SIZE].ToClass<FileHeaderInfo>();
|
||||
}
|
||||
|
||||
public void Save(uint seed)
|
||||
{
|
||||
var encrypt = Encryption.Encrypt(RawData, seed, RawHeader);
|
||||
File.WriteAllBytes(DataPath, encrypt.Data);
|
||||
File.WriteAllBytes(HeaderPath, encrypt.Header);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates all hashes of <see cref="Data"/>.
|
||||
/// </summary>
|
||||
public void Hash()
|
||||
{
|
||||
var ver = Info.GetKnownRevisionIndex();
|
||||
var hash = RevisionChecker.HashInfo[ver];
|
||||
var details = hash.GetFile(NameData);
|
||||
if (details == null)
|
||||
throw new ArgumentNullException(nameof(NameData));
|
||||
foreach (var h in details.HashRegions)
|
||||
Murmur3.UpdateMurmur32(Data.Slice(h.BeginOffset, h.Size), Data[h.HashOffset..]);
|
||||
}
|
||||
|
||||
public IEnumerable<FileHashRegion> InvalidHashes()
|
||||
{
|
||||
var ver = Info.GetKnownRevisionIndex();
|
||||
var hash = RevisionChecker.HashInfo[ver];
|
||||
var details = hash.GetFile(NameData);
|
||||
if (details == null)
|
||||
throw new ArgumentNullException(nameof(NameData));
|
||||
foreach (var h in details.HashRegions)
|
||||
{
|
||||
var current = Murmur3.GetMurmur3Hash(Data.Slice(h.BeginOffset, h.Size));
|
||||
var saved = ReadUInt32LittleEndian(Data[h.HashOffset..]);
|
||||
if (current != saved)
|
||||
yield return h;
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetString(int offset, int maxLength) => StringUtil.GetString(Data, offset, maxLength);
|
||||
protected static byte[] GetBytes(string value, int maxLength) => StringUtil.GetBytes(value, maxLength);
|
||||
}
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
using System.Runtime.InteropServices;
|
||||
// ReSharper disable NonReadonlyMemberInGetHashCode
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata stored in a file's Header, indicating the revision information.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public sealed record FileHeaderInfo
|
||||
{
|
||||
public const int SIZE = 0x40;
|
||||
namespace NHSE.Core;
|
||||
|
||||
[field: FieldOffset(0x00)] public uint Major { get; init; }
|
||||
[field: FieldOffset(0x04)] public uint Minor { get; init; }
|
||||
[field: FieldOffset(0x08)] public ushort Unk1 { get; init; }
|
||||
[field: FieldOffset(0x0A)] public ushort HeaderRevision { get; init; }
|
||||
[field: FieldOffset(0x0C)] public ushort Unk2 { get; init; }
|
||||
[field: FieldOffset(0x0E)] public ushort SaveRevision { get; init; }
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Metadata stored in a file's Header, indicating the revision information.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public sealed record FileHeaderInfo
|
||||
{
|
||||
public const int SIZE = 0x40;
|
||||
|
||||
[field: FieldOffset(0x00)] public uint Major { get; init; }
|
||||
[field: FieldOffset(0x04)] public uint Minor { get; init; }
|
||||
[field: FieldOffset(0x08)] public ushort Unk1 { get; init; }
|
||||
[field: FieldOffset(0x0A)] public ushort HeaderRevision { get; init; }
|
||||
[field: FieldOffset(0x0C)] public ushort Unk2 { get; init; }
|
||||
[field: FieldOffset(0x0E)] public ushort SaveRevision { get; init; }
|
||||
}
|
||||
|
|
@ -1,102 +1,102 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents all saved data that is stored on the device for the New Horizon's game.
|
||||
/// </summary>
|
||||
public class HorizonSave
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents all saved data that is stored on the device for the New Horizon's game.
|
||||
/// </summary>
|
||||
public class HorizonSave
|
||||
public readonly MainSave Main;
|
||||
public readonly Player[] Players;
|
||||
public override string ToString() => $"{Players[0].Personal.TownName} - {Players[0]}";
|
||||
|
||||
public HorizonSave(string folder)
|
||||
{
|
||||
public readonly MainSave Main;
|
||||
public readonly Player[] Players;
|
||||
public override string ToString() => $"{Players[0].Personal.TownName} - {Players[0]}";
|
||||
Main = new MainSave(folder);
|
||||
Players = Player.ReadMany(folder);
|
||||
}
|
||||
|
||||
public HorizonSave(string folder)
|
||||
/// <summary>
|
||||
/// Saves the data using the provided crypto <see cref="seed"/>.
|
||||
/// </summary>
|
||||
/// <param name="seed">Seed to initialize the RNG with when encrypting the files.</param>
|
||||
public void Save(uint seed)
|
||||
{
|
||||
Main.Hash();
|
||||
Main.Save(seed);
|
||||
foreach (var player in Players)
|
||||
{
|
||||
Main = new MainSave(folder);
|
||||
Players = Player.ReadMany(folder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the data using the provided crypto <see cref="seed"/>.
|
||||
/// </summary>
|
||||
/// <param name="seed">Seed to initialize the RNG with when encrypting the files.</param>
|
||||
public void Save(uint seed)
|
||||
{
|
||||
Main.Hash();
|
||||
Main.Save(seed);
|
||||
foreach (var player in Players)
|
||||
foreach (var pair in player)
|
||||
{
|
||||
foreach (var pair in player)
|
||||
{
|
||||
pair.Hash();
|
||||
pair.Save(seed);
|
||||
}
|
||||
pair.Hash();
|
||||
pair.Save(seed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets every <see cref="FileHashRegion"/> that is deemed invalid.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Doesn't return any metadata about which file the hashes were bad for.
|
||||
/// Just check what's returned with what's implemented; the offsets are unique enough.
|
||||
/// </remarks>
|
||||
public IEnumerable<FileHashRegion> GetInvalidHashes()
|
||||
{
|
||||
foreach (var hash in Main.InvalidHashes())
|
||||
yield return hash;
|
||||
foreach (var hash in Players.SelectMany(z => z).SelectMany(z => z.InvalidHashes()))
|
||||
yield return hash;
|
||||
}
|
||||
|
||||
public void ChangeIdentity(byte[] original, byte[] updated)
|
||||
{
|
||||
Main.Data.ReplaceOccurrences(original, updated);
|
||||
foreach (var pair in Players.SelectMany(z => z))
|
||||
pair.Data.ReplaceOccurrences(original, updated);
|
||||
}
|
||||
|
||||
public bool ValidateSizes()
|
||||
{
|
||||
var info = Main.Info.GetKnownRevisionIndex();
|
||||
if (info < 0)
|
||||
return false;
|
||||
var sizes = RevisionChecker.SizeInfo[info];
|
||||
if (Main.Data.Length != sizes.Main)
|
||||
return false;
|
||||
|
||||
// Each player present in the savedata must have been migrated to this revision.
|
||||
foreach (var p in Players)
|
||||
{
|
||||
if (p.Personal.Data.Length != sizes.Personal)
|
||||
return false;
|
||||
if (p.Photo.Data.Length != sizes.PhotoStudioIsland)
|
||||
return false;
|
||||
if (p.PostBox.Data.Length != sizes.PostBox)
|
||||
return false;
|
||||
if (p.Profile.Data.Length != sizes.Profile)
|
||||
return false;
|
||||
if (p.WhereAreN is { } x && x.Data.Length != sizes.WhereAreN)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetSaveTitle(string prefix)
|
||||
{
|
||||
var townName = Players[0].Personal.TownName;
|
||||
var timestamp = Main.LastSaved.TimeStamp;
|
||||
|
||||
return $"{prefix} - {townName} @ {timestamp}";
|
||||
}
|
||||
|
||||
public string GetBackupFolderTitle()
|
||||
{
|
||||
var townName = Players[0].Personal.TownName;
|
||||
var timestamp = Main.LastSaved.TimeStamp.Replace(':', '.');
|
||||
return StringUtil.CleanFileName($"{townName} - {timestamp}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets every <see cref="FileHashRegion"/> that is deemed invalid.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Doesn't return any metadata about which file the hashes were bad for.
|
||||
/// Just check what's returned with what's implemented; the offsets are unique enough.
|
||||
/// </remarks>
|
||||
public IEnumerable<FileHashRegion> GetInvalidHashes()
|
||||
{
|
||||
foreach (var hash in Main.InvalidHashes())
|
||||
yield return hash;
|
||||
foreach (var hash in Players.SelectMany(z => z).SelectMany(z => z.InvalidHashes()))
|
||||
yield return hash;
|
||||
}
|
||||
|
||||
public void ChangeIdentity(ReadOnlySpan<byte> original, ReadOnlySpan<byte> updated)
|
||||
{
|
||||
Main.Data.ReplaceOccurrences(original, updated);
|
||||
foreach (var pair in Players.SelectMany(z => z))
|
||||
pair.Data.ReplaceOccurrences(original, updated);
|
||||
}
|
||||
|
||||
public bool ValidateSizes()
|
||||
{
|
||||
var info = Main.Info.GetKnownRevisionIndex();
|
||||
if (info < 0)
|
||||
return false;
|
||||
var sizes = RevisionChecker.SizeInfo[info];
|
||||
if (Main.Data.Length != sizes.Main)
|
||||
return false;
|
||||
|
||||
// Each player present in the savedata must have been migrated to this revision.
|
||||
foreach (var p in Players)
|
||||
{
|
||||
if (p.Personal.Data.Length != sizes.Personal)
|
||||
return false;
|
||||
if (p.Photo.Data.Length != sizes.PhotoStudioIsland)
|
||||
return false;
|
||||
if (p.PostBox.Data.Length != sizes.PostBox)
|
||||
return false;
|
||||
if (p.Profile.Data.Length != sizes.Profile)
|
||||
return false;
|
||||
if (p.WhereAreN is { } x && x.Data.Length != sizes.WhereAreN)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetSaveTitle(string prefix)
|
||||
{
|
||||
var townName = Players[0].Personal.TownName;
|
||||
var timestamp = Main.LastSaved.TimeStamp;
|
||||
|
||||
return $"{prefix} - {townName} @ {timestamp}";
|
||||
}
|
||||
|
||||
public string GetBackupFolderTitle()
|
||||
{
|
||||
var townName = Players[0].Personal.TownName;
|
||||
var timestamp = Main.LastSaved.TimeStamp.Replace(':', '.');
|
||||
return StringUtil.CleanFileName($"{townName} - {timestamp}");
|
||||
}
|
||||
}
|
||||
|
|
@ -3,63 +3,62 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores references for all files in the Villager (<see cref="DirectoryName"/>) folder.
|
||||
/// </summary>
|
||||
public sealed class Player : IEnumerable<EncryptedFilePair>
|
||||
{
|
||||
public readonly Personal Personal;
|
||||
public readonly PhotoStudioIsland Photo;
|
||||
public readonly PostBox PostBox;
|
||||
public readonly Profile Profile;
|
||||
public readonly WhereAreN? WhereAreN;
|
||||
|
||||
/// <summary>
|
||||
/// Stores references for all files in the Villager (<see cref="DirectoryName"/>) folder.
|
||||
/// Directory Name where the player data was loaded from. Not the full path.
|
||||
/// </summary>
|
||||
public sealed class Player : IEnumerable<EncryptedFilePair>
|
||||
public readonly string DirectoryName;
|
||||
|
||||
#region Override Implementations
|
||||
public IEnumerator<EncryptedFilePair> GetEnumerator()
|
||||
{
|
||||
public readonly Personal Personal;
|
||||
public readonly PhotoStudioIsland Photo;
|
||||
public readonly PostBox PostBox;
|
||||
public readonly Profile Profile;
|
||||
public readonly WhereAreN? WhereAreN;
|
||||
|
||||
/// <summary>
|
||||
/// Directory Name where the player data was loaded from. Not the full path.
|
||||
/// </summary>
|
||||
public readonly string DirectoryName;
|
||||
|
||||
#region Override Implementations
|
||||
public IEnumerator<EncryptedFilePair> GetEnumerator()
|
||||
{
|
||||
IEnumerable<EncryptedFilePair> baseFiles = new EncryptedFilePair[] { Personal, Photo, PostBox, Profile };
|
||||
if (WhereAreN is not null)
|
||||
baseFiles = baseFiles.Concat(new EncryptedFilePair[] { WhereAreN });
|
||||
return baseFiles.AsEnumerable().GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public override string ToString() => Personal.PlayerName;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Imports Player data from the requested <see cref="folder"/>.
|
||||
/// </summary>
|
||||
/// <param name="folder">Folder that contains the Player Villager sub-folders.</param>
|
||||
/// <returns>Player object array loaded from the <see cref="folder"/>.</returns>
|
||||
public static Player[] ReadMany(string folder)
|
||||
{
|
||||
var dirs = Directory.GetDirectories(folder, "Villager*", SearchOption.TopDirectoryOnly);
|
||||
var result = new Player[dirs.Length];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = new Player(dirs[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Player(string folder)
|
||||
{
|
||||
DirectoryName = new DirectoryInfo(folder).Name;
|
||||
|
||||
Personal = new Personal(folder);
|
||||
Photo = new PhotoStudioIsland(folder);
|
||||
PostBox = new PostBox(folder);
|
||||
Profile = new Profile(folder);
|
||||
|
||||
if (EncryptedFilePair.Exists(folder, WhereAreN.FileName))
|
||||
WhereAreN = new WhereAreN(folder);
|
||||
}
|
||||
IEnumerable<EncryptedFilePair> baseFiles = [Personal, Photo, PostBox, Profile];
|
||||
if (WhereAreN is not null)
|
||||
baseFiles = baseFiles.Concat([WhereAreN]);
|
||||
return baseFiles.AsEnumerable().GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public override string ToString() => Personal.PlayerName;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Imports Player data from the requested <see cref="folder"/>.
|
||||
/// </summary>
|
||||
/// <param name="folder">Folder that contains the Player Villager sub-folders.</param>
|
||||
/// <returns>Player object array loaded from the <see cref="folder"/>.</returns>
|
||||
public static Player[] ReadMany(string folder)
|
||||
{
|
||||
var dirs = Directory.GetDirectories(folder, "Villager*", SearchOption.TopDirectoryOnly);
|
||||
var result = new Player[dirs.Length];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = new Player(dirs[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Player(string folder)
|
||||
{
|
||||
DirectoryName = new DirectoryInfo(folder).Name;
|
||||
|
||||
Personal = new Personal(folder);
|
||||
Photo = new PhotoStudioIsland(folder);
|
||||
PostBox = new PostBox(folder);
|
||||
Profile = new Profile(folder);
|
||||
|
||||
if (EncryptedFilePair.Exists(folder, WhereAreN.FileName))
|
||||
WhereAreN = new WhereAreN(folder);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,139 +2,141 @@
|
|||
using System.Collections.Generic;
|
||||
using static NHSE.Core.FileHashRevision;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for detecting a <see cref="EncryptedFilePair"/> revision.
|
||||
/// </summary>
|
||||
public static class RevisionChecker
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for detecting a <see cref="EncryptedFilePair"/> revision.
|
||||
/// Unique save file size list by patch.
|
||||
/// </summary>
|
||||
public static class RevisionChecker
|
||||
private static readonly SaveFileSizes[] SizesByRevision =
|
||||
[
|
||||
new(REV_100_MAIN, REV_100_PERSONAL, REV_100_PHOTO, REV_100_POSTBOX, REV_100_PROFILE), // 1.0.0
|
||||
new(REV_110_MAIN, REV_110_PERSONAL, REV_110_PHOTO, REV_110_POSTBOX, REV_110_PROFILE), // 1.1.0
|
||||
new(REV_120_MAIN, REV_120_PERSONAL, REV_120_PHOTO, REV_120_POSTBOX, REV_120_PROFILE), // 1.2.0
|
||||
new(REV_130_MAIN, REV_130_PERSONAL, REV_130_PHOTO, REV_130_POSTBOX, REV_130_PROFILE), // 1.3.0
|
||||
new(REV_140_MAIN, REV_140_PERSONAL, REV_140_PHOTO, REV_140_POSTBOX, REV_140_PROFILE), // 1.4.0
|
||||
new(REV_150_MAIN, REV_150_PERSONAL, REV_150_PHOTO, REV_150_POSTBOX, REV_150_PROFILE), // 1.5.0
|
||||
new(REV_160_MAIN, REV_160_PERSONAL, REV_160_PHOTO, REV_160_POSTBOX, REV_160_PROFILE), // 1.6.0
|
||||
new(REV_170_MAIN, REV_170_PERSONAL, REV_170_PHOTO, REV_170_POSTBOX, REV_170_PROFILE), // 1.7.0
|
||||
new(REV_180_MAIN, REV_180_PERSONAL, REV_180_PHOTO, REV_180_POSTBOX, REV_180_PROFILE), // 1.8.0
|
||||
new(REV_190_MAIN, REV_190_PERSONAL, REV_190_PHOTO, REV_190_POSTBOX, REV_190_PROFILE), // 1.9.0
|
||||
new(REV_1100_MAIN,REV_1100_PERSONAL,REV_1100_PHOTO,REV_1100_POSTBOX,REV_1100_PROFILE),// 1.10.0
|
||||
new(REV_1110_MAIN,REV_1110_PERSONAL,REV_1110_PHOTO,REV_1110_POSTBOX,REV_1110_PROFILE),// 1.11.0
|
||||
new(REV_200_MAIN, REV_200_PERSONAL, REV_200_PHOTO, REV_200_POSTBOX, REV_200_PROFILE, REV_200_WHEREAREN) // 2.0.0
|
||||
];
|
||||
|
||||
private static readonly FileHeaderInfo[] RevisionInfo =
|
||||
[
|
||||
new() { Major = 0x00067, Minor = 0x0006F, HeaderRevision = 0, Unk1 = 2, SaveRevision = 00, Unk2 = 2 }, // 1.0.0
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 01, Unk2 = 2 }, // 1.1.0
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 02, Unk2 = 2 }, // 1.1.1
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 03, Unk2 = 2 }, // 1.1.2
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 04, Unk2 = 2 }, // 1.1.3
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 05, Unk2 = 2 }, // 1.1.4
|
||||
new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 06, Unk2 = 2 }, // 1.2.0
|
||||
new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 07, Unk2 = 2 }, // 1.2.1
|
||||
new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 08, Unk2 = 2 }, // 1.3.0
|
||||
new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 09, Unk2 = 2 }, // 1.3.1
|
||||
new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 10, Unk2 = 2 }, // 1.4.0
|
||||
new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 11, Unk2 = 2 }, // 1.4.1
|
||||
new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 12, Unk2 = 2 }, // 1.4.2
|
||||
new() { Major = 0x60001, Minor = 0x6000C, HeaderRevision = 0, Unk1 = 2, SaveRevision = 13, Unk2 = 2 }, // 1.5.0
|
||||
new() { Major = 0x60001, Minor = 0x6000C, HeaderRevision = 0, Unk1 = 2, SaveRevision = 14, Unk2 = 2 }, // 1.5.1
|
||||
new() { Major = 0x70001, Minor = 0x70006, HeaderRevision = 0, Unk1 = 2, SaveRevision = 15, Unk2 = 2 }, // 1.6.0
|
||||
new() { Major = 0x74001, Minor = 0x74005, HeaderRevision = 0, Unk1 = 2, SaveRevision = 16, Unk2 = 2 }, // 1.7.0
|
||||
new() { Major = 0x78001, Minor = 0x78001, HeaderRevision = 0, Unk1 = 2, SaveRevision = 17, Unk2 = 2 }, // 1.8.0
|
||||
new() { Major = 0x7C001, Minor = 0x7C006, HeaderRevision = 0, Unk1 = 2, SaveRevision = 18, Unk2 = 2 }, // 1.9.0
|
||||
new() { Major = 0x7D001, Minor = 0x7D004, HeaderRevision = 0, Unk1 = 2, SaveRevision = 19, Unk2 = 2 }, // 1.10.0
|
||||
new() { Major = 0x7E001, Minor = 0x7E001, HeaderRevision = 0, Unk1 = 2, SaveRevision = 20, Unk2 = 2 }, // 1.11.0
|
||||
new() { Major = 0x7E001, Minor = 0x7E001, HeaderRevision = 0, Unk1 = 2, SaveRevision = 21, Unk2 = 2 }, // 1.11.1
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 22, Unk2 = 2 }, // 2.0.0
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 23, Unk2 = 2 }, // 2.0.1
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 24, Unk2 = 2 }, // 2.0.2
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 25, Unk2 = 2 }, // 2.0.3
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 26, Unk2 = 2 }, // 2.0.4
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 27, Unk2 = 2 }, // 2.0.5
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 28, Unk2 = 2 }, // 2.0.6
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 29, Unk2 = 2 }, // 2.0.7
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 30, Unk2 = 2 } // 2.0.8
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<SaveFileSizes> SizeInfo =
|
||||
[
|
||||
SizesByRevision[0], // 1.0.0
|
||||
SizesByRevision[1], // 1.1.0
|
||||
SizesByRevision[1], // 1.1.1
|
||||
SizesByRevision[1], // 1.1.2
|
||||
SizesByRevision[1], // 1.1.3
|
||||
SizesByRevision[1], // 1.1.4
|
||||
SizesByRevision[2], // 1.2.0
|
||||
SizesByRevision[2], // 1.2.1
|
||||
SizesByRevision[3], // 1.3.0
|
||||
SizesByRevision[3], // 1.3.1
|
||||
SizesByRevision[4], // 1.4.0
|
||||
SizesByRevision[4], // 1.4.1
|
||||
SizesByRevision[4], // 1.4.2
|
||||
SizesByRevision[5], // 1.5.0
|
||||
SizesByRevision[5], // 1.5.1
|
||||
SizesByRevision[6], // 1.6.0
|
||||
SizesByRevision[7], // 1.7.0
|
||||
SizesByRevision[8], // 1.8.0
|
||||
SizesByRevision[9], // 1.9.0
|
||||
SizesByRevision[10], // 1.10.0
|
||||
SizesByRevision[11], // 1.11.0
|
||||
SizesByRevision[11], // 1.11.1
|
||||
SizesByRevision[12], // 2.0.0
|
||||
SizesByRevision[12], // 2.0.1
|
||||
SizesByRevision[12], // 2.0.2
|
||||
SizesByRevision[12], // 2.0.3
|
||||
SizesByRevision[12], // 2.0.4
|
||||
SizesByRevision[12], // 2.0.5
|
||||
SizesByRevision[12], // 2.0.6
|
||||
SizesByRevision[12], // 2.0.7
|
||||
SizesByRevision[12] // 2.0.8
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<FileHashInfo> HashInfo =
|
||||
[
|
||||
REV_100, // 1.0.0
|
||||
REV_110, // 1.1.0
|
||||
REV_110, // 1.1.1
|
||||
REV_110, // 1.1.2
|
||||
REV_110, // 1.1.3
|
||||
REV_110, // 1.1.4
|
||||
REV_120, // 1.2.0
|
||||
REV_120, // 1.2.1
|
||||
REV_130, // 1.3.0
|
||||
REV_130, // 1.3.1
|
||||
REV_140, // 1.4.0
|
||||
REV_140, // 1.4.1
|
||||
REV_140, // 1.4.2
|
||||
REV_150, // 1.5.0
|
||||
REV_150, // 1.5.1
|
||||
REV_160, // 1.6.0
|
||||
REV_170, // 1.7.0
|
||||
REV_180, // 1.8.0
|
||||
REV_190, // 1.9.0
|
||||
REV_1100, // 1.10.0
|
||||
REV_1110, // 1.11.0
|
||||
REV_1110, // 1.11.1
|
||||
REV_200, // 2.0.0
|
||||
REV_200, // 2.0.1
|
||||
REV_200, // 2.0.2
|
||||
REV_200, // 2.0.3
|
||||
REV_200, // 2.0.4
|
||||
REV_200, // 2.0.5
|
||||
REV_200, // 2.0.6
|
||||
REV_200, // 2.0.7
|
||||
REV_200 // 2.0.8
|
||||
];
|
||||
|
||||
extension(FileHeaderInfo info)
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique save file size list by patch.
|
||||
/// </summary>
|
||||
private static readonly SaveFileSizes[] SizesByRevision =
|
||||
{
|
||||
new(REV_100_MAIN, REV_100_PERSONAL, REV_100_PHOTO, REV_100_POSTBOX, REV_100_PROFILE), // 1.0.0
|
||||
new(REV_110_MAIN, REV_110_PERSONAL, REV_110_PHOTO, REV_110_POSTBOX, REV_110_PROFILE), // 1.1.0
|
||||
new(REV_120_MAIN, REV_120_PERSONAL, REV_120_PHOTO, REV_120_POSTBOX, REV_120_PROFILE), // 1.2.0
|
||||
new(REV_130_MAIN, REV_130_PERSONAL, REV_130_PHOTO, REV_130_POSTBOX, REV_130_PROFILE), // 1.3.0
|
||||
new(REV_140_MAIN, REV_140_PERSONAL, REV_140_PHOTO, REV_140_POSTBOX, REV_140_PROFILE), // 1.4.0
|
||||
new(REV_150_MAIN, REV_150_PERSONAL, REV_150_PHOTO, REV_150_POSTBOX, REV_150_PROFILE), // 1.5.0
|
||||
new(REV_160_MAIN, REV_160_PERSONAL, REV_160_PHOTO, REV_160_POSTBOX, REV_160_PROFILE), // 1.6.0
|
||||
new(REV_170_MAIN, REV_170_PERSONAL, REV_170_PHOTO, REV_170_POSTBOX, REV_170_PROFILE), // 1.7.0
|
||||
new(REV_180_MAIN, REV_180_PERSONAL, REV_180_PHOTO, REV_180_POSTBOX, REV_180_PROFILE), // 1.8.0
|
||||
new(REV_190_MAIN, REV_190_PERSONAL, REV_190_PHOTO, REV_190_POSTBOX, REV_190_PROFILE), // 1.9.0
|
||||
new(REV_1100_MAIN,REV_1100_PERSONAL,REV_1100_PHOTO,REV_1100_POSTBOX,REV_1100_PROFILE),// 1.10.0
|
||||
new(REV_1110_MAIN,REV_1110_PERSONAL,REV_1110_PHOTO,REV_1110_POSTBOX,REV_1110_PROFILE),// 1.11.0
|
||||
new(REV_200_MAIN, REV_200_PERSONAL, REV_200_PHOTO, REV_200_POSTBOX, REV_200_PROFILE, REV_200_WHEREAREN), // 2.0.0
|
||||
};
|
||||
|
||||
private static readonly FileHeaderInfo[] RevisionInfo =
|
||||
{
|
||||
new() { Major = 0x00067, Minor = 0x0006F, HeaderRevision = 0, Unk1 = 2, SaveRevision = 00, Unk2 = 2 }, // 1.0.0
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 01, Unk2 = 2 }, // 1.1.0
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 02, Unk2 = 2 }, // 1.1.1
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 03, Unk2 = 2 }, // 1.1.2
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 04, Unk2 = 2 }, // 1.1.3
|
||||
new() { Major = 0x0006D, Minor = 0x00078, HeaderRevision = 0, Unk1 = 2, SaveRevision = 05, Unk2 = 2 }, // 1.1.4
|
||||
new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 06, Unk2 = 2 }, // 1.2.0
|
||||
new() { Major = 0x20006, Minor = 0x20008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 07, Unk2 = 2 }, // 1.2.1
|
||||
new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 08, Unk2 = 2 }, // 1.3.0
|
||||
new() { Major = 0x40002, Minor = 0x40008, HeaderRevision = 0, Unk1 = 2, SaveRevision = 09, Unk2 = 2 }, // 1.3.1
|
||||
new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 10, Unk2 = 2 }, // 1.4.0
|
||||
new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 11, Unk2 = 2 }, // 1.4.1
|
||||
new() { Major = 0x50001, Minor = 0x5000B, HeaderRevision = 0, Unk1 = 2, SaveRevision = 12, Unk2 = 2 }, // 1.4.2
|
||||
new() { Major = 0x60001, Minor = 0x6000C, HeaderRevision = 0, Unk1 = 2, SaveRevision = 13, Unk2 = 2 }, // 1.5.0
|
||||
new() { Major = 0x60001, Minor = 0x6000C, HeaderRevision = 0, Unk1 = 2, SaveRevision = 14, Unk2 = 2 }, // 1.5.1
|
||||
new() { Major = 0x70001, Minor = 0x70006, HeaderRevision = 0, Unk1 = 2, SaveRevision = 15, Unk2 = 2 }, // 1.6.0
|
||||
new() { Major = 0x74001, Minor = 0x74005, HeaderRevision = 0, Unk1 = 2, SaveRevision = 16, Unk2 = 2 }, // 1.7.0
|
||||
new() { Major = 0x78001, Minor = 0x78001, HeaderRevision = 0, Unk1 = 2, SaveRevision = 17, Unk2 = 2 }, // 1.8.0
|
||||
new() { Major = 0x7C001, Minor = 0x7C006, HeaderRevision = 0, Unk1 = 2, SaveRevision = 18, Unk2 = 2 }, // 1.9.0
|
||||
new() { Major = 0x7D001, Minor = 0x7D004, HeaderRevision = 0, Unk1 = 2, SaveRevision = 19, Unk2 = 2 }, // 1.10.0
|
||||
new() { Major = 0x7E001, Minor = 0x7E001, HeaderRevision = 0, Unk1 = 2, SaveRevision = 20, Unk2 = 2 }, // 1.11.0
|
||||
new() { Major = 0x7E001, Minor = 0x7E001, HeaderRevision = 0, Unk1 = 2, SaveRevision = 21, Unk2 = 2 }, // 1.11.1
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 22, Unk2 = 2 }, // 2.0.0
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 23, Unk2 = 2 }, // 2.0.1
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 24, Unk2 = 2 }, // 2.0.2
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 25, Unk2 = 2 }, // 2.0.3
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 26, Unk2 = 2 }, // 2.0.4
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 27, Unk2 = 2 }, // 2.0.5
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 28, Unk2 = 2 }, // 2.0.6
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 29, Unk2 = 2 }, // 2.0.7
|
||||
new() { Major = 0x80009, Minor = 0x80085, HeaderRevision = 0, Unk1 = 2, SaveRevision = 30, Unk2 = 2 }, // 2.0.8
|
||||
};
|
||||
|
||||
public static readonly IReadOnlyList<SaveFileSizes> SizeInfo = new[]
|
||||
{
|
||||
SizesByRevision[0], // 1.0.0
|
||||
SizesByRevision[1], // 1.1.0
|
||||
SizesByRevision[1], // 1.1.1
|
||||
SizesByRevision[1], // 1.1.2
|
||||
SizesByRevision[1], // 1.1.3
|
||||
SizesByRevision[1], // 1.1.4
|
||||
SizesByRevision[2], // 1.2.0
|
||||
SizesByRevision[2], // 1.2.1
|
||||
SizesByRevision[3], // 1.3.0
|
||||
SizesByRevision[3], // 1.3.1
|
||||
SizesByRevision[4], // 1.4.0
|
||||
SizesByRevision[4], // 1.4.1
|
||||
SizesByRevision[4], // 1.4.2
|
||||
SizesByRevision[5], // 1.5.0
|
||||
SizesByRevision[5], // 1.5.1
|
||||
SizesByRevision[6], // 1.6.0
|
||||
SizesByRevision[7], // 1.7.0
|
||||
SizesByRevision[8], // 1.8.0
|
||||
SizesByRevision[9], // 1.9.0
|
||||
SizesByRevision[10], // 1.10.0
|
||||
SizesByRevision[11], // 1.11.0
|
||||
SizesByRevision[11], // 1.11.1
|
||||
SizesByRevision[12], // 2.0.0
|
||||
SizesByRevision[12], // 2.0.1
|
||||
SizesByRevision[12], // 2.0.2
|
||||
SizesByRevision[12], // 2.0.3
|
||||
SizesByRevision[12], // 2.0.4
|
||||
SizesByRevision[12], // 2.0.5
|
||||
SizesByRevision[12], // 2.0.6
|
||||
SizesByRevision[12], // 2.0.7
|
||||
SizesByRevision[12], // 2.0.8
|
||||
};
|
||||
|
||||
public static readonly IReadOnlyList<FileHashInfo> HashInfo = new[]
|
||||
{
|
||||
REV_100, // 1.0.0
|
||||
REV_110, // 1.1.0
|
||||
REV_110, // 1.1.1
|
||||
REV_110, // 1.1.2
|
||||
REV_110, // 1.1.3
|
||||
REV_110, // 1.1.4
|
||||
REV_120, // 1.2.0
|
||||
REV_120, // 1.2.1
|
||||
REV_130, // 1.3.0
|
||||
REV_130, // 1.3.1
|
||||
REV_140, // 1.4.0
|
||||
REV_140, // 1.4.1
|
||||
REV_140, // 1.4.2
|
||||
REV_150, // 1.5.0
|
||||
REV_150, // 1.5.1
|
||||
REV_160, // 1.6.0
|
||||
REV_170, // 1.7.0
|
||||
REV_180, // 1.8.0
|
||||
REV_190, // 1.9.0
|
||||
REV_1100, // 1.10.0
|
||||
REV_1110, // 1.11.0
|
||||
REV_1110, // 1.11.1
|
||||
REV_200, // 2.0.0
|
||||
REV_200, // 2.0.1
|
||||
REV_200, // 2.0.2
|
||||
REV_200, // 2.0.3
|
||||
REV_200, // 2.0.4
|
||||
REV_200, // 2.0.5
|
||||
REV_200, // 2.0.6
|
||||
REV_200, // 2.0.7
|
||||
REV_200, // 2.0.8
|
||||
};
|
||||
|
||||
public static bool IsRevisionKnown(this FileHeaderInfo info) => info.GetKnownRevisionIndex() >= 0;
|
||||
public static int GetKnownRevisionIndex(this FileHeaderInfo info) => Array.FindIndex(RevisionInfo, z => z.Equals(info));
|
||||
public bool IsRevisionKnown() => info.GetKnownRevisionIndex() >= 0;
|
||||
public int GetKnownRevisionIndex() => Array.FindIndex(RevisionInfo, z => z.Equals(info));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,24 @@
|
|||
namespace NHSE.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores file sizes for various savedata files at different patch revisions.
|
||||
/// </summary>
|
||||
public class SaveFileSizes
|
||||
{
|
||||
public readonly uint Main;
|
||||
public readonly uint Personal;
|
||||
public readonly uint PhotoStudioIsland;
|
||||
public readonly uint PostBox;
|
||||
public readonly uint Profile;
|
||||
public readonly uint WhereAreN;
|
||||
namespace NHSE.Core;
|
||||
|
||||
public SaveFileSizes(uint main, uint personal, uint photo, uint postbox, uint profile, uint wherearen = 0)
|
||||
{
|
||||
Main = main;
|
||||
Personal = personal;
|
||||
PhotoStudioIsland = photo;
|
||||
PostBox = postbox;
|
||||
Profile = profile;
|
||||
WhereAreN = wherearen;
|
||||
}
|
||||
/// <summary>
|
||||
/// Stores file sizes for various savedata files at different patch revisions.
|
||||
/// </summary>
|
||||
public class SaveFileSizes
|
||||
{
|
||||
public readonly uint Main;
|
||||
public readonly uint Personal;
|
||||
public readonly uint PhotoStudioIsland;
|
||||
public readonly uint PostBox;
|
||||
public readonly uint Profile;
|
||||
public readonly uint WhereAreN;
|
||||
|
||||
public SaveFileSizes(uint main, uint personal, uint photo, uint postbox, uint profile, uint wherearen = 0)
|
||||
{
|
||||
Main = main;
|
||||
Personal = personal;
|
||||
PhotoStudioIsland = photo;
|
||||
PostBox = postbox;
|
||||
Profile = profile;
|
||||
WhereAreN = wherearen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,209 +1,208 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offset info and object retrieval logic for <see cref="MainSave"/>
|
||||
/// </summary>
|
||||
public abstract class MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// Offset info and object retrieval logic for <see cref="MainSave"/>
|
||||
/// </summary>
|
||||
public abstract class MainSaveOffsets
|
||||
public const int PlayerCount = 8;
|
||||
public const int VillagerCount = 10;
|
||||
public virtual int PatternCount => PatternCount1;
|
||||
protected const int PatternCount1 = 50;
|
||||
protected const int PatternCount2 = 100;
|
||||
public const int PatternTailorCount = 8;
|
||||
public const int BuildingCount = 46;
|
||||
public const int RecycleBinCount = 40;
|
||||
|
||||
public abstract int Animal { get; }
|
||||
|
||||
public abstract int LandMyDesign { get; }
|
||||
public abstract int PatternsPRO { get; }
|
||||
public abstract int PatternFlag { get; }
|
||||
public abstract int PatternTailor { get; }
|
||||
|
||||
public abstract int PatternsEditFlagStart { get; }
|
||||
public abstract int PatternsProEditFlagStart { get; }
|
||||
|
||||
public abstract int WeatherArea { get; }
|
||||
public abstract int WeatherRandSeed { get; }
|
||||
|
||||
public abstract int MainFieldStructure { get; }
|
||||
|
||||
public abstract int EventFlagLand { get; }
|
||||
public abstract int FieldItem { get; }
|
||||
public abstract int LandMakingMap { get; }
|
||||
public abstract int OutsideField { get; }
|
||||
public abstract int MyDesignMap { get; }
|
||||
public abstract int PlayerHouseList { get; }
|
||||
public abstract int NpcHouseList { get; }
|
||||
public abstract int ShopKabu { get; }
|
||||
public abstract int Museum { get; }
|
||||
public abstract int Visitor { get; }
|
||||
|
||||
public abstract int SaveFg { get; }
|
||||
public abstract int BulletinBoard { get; }
|
||||
public abstract int AirportThemeColor { get; }
|
||||
|
||||
public abstract int LostItemBox { get; }
|
||||
public abstract int LastSavedTime { get; }
|
||||
|
||||
public abstract int VillagerSize { get; }
|
||||
public abstract int VillagerHouseSize { get; }
|
||||
public abstract int PlayerHouseSize { get; }
|
||||
public abstract int PlayerRoomSize { get; }
|
||||
|
||||
public abstract IVillager ReadVillager(Memory<byte> data);
|
||||
public abstract IVillagerHouse ReadVillagerHouse(Memory<byte> data);
|
||||
public abstract IPlayerHouse ReadPlayerHouse(Memory<byte> data);
|
||||
public abstract IPlayerRoom ReadPlayerRoom(Memory<byte> data);
|
||||
|
||||
public static MainSaveOffsets GetOffsets(FileHeaderInfo Info)
|
||||
{
|
||||
public const int PlayerCount = 8;
|
||||
public const int VillagerCount = 10;
|
||||
public virtual int PatternCount => PatternCount1;
|
||||
protected const int PatternCount1 = 50;
|
||||
protected const int PatternCount2 = 100;
|
||||
public const int PatternTailorCount = 8;
|
||||
public const int BuildingCount = 46;
|
||||
public const int RecycleBinCount = 40;
|
||||
|
||||
public abstract int Animal { get; }
|
||||
|
||||
public abstract int LandMyDesign { get; }
|
||||
public abstract int PatternsPRO { get; }
|
||||
public abstract int PatternFlag { get; }
|
||||
public abstract int PatternTailor { get; }
|
||||
|
||||
public abstract int PatternsEditFlagStart { get; }
|
||||
public abstract int PatternsProEditFlagStart { get; }
|
||||
|
||||
public abstract int WeatherArea { get; }
|
||||
public abstract int WeatherRandSeed { get; }
|
||||
|
||||
public abstract int MainFieldStructure { get; }
|
||||
|
||||
public abstract int EventFlagLand { get; }
|
||||
public abstract int FieldItem { get; }
|
||||
public abstract int LandMakingMap { get; }
|
||||
public abstract int OutsideField { get; }
|
||||
public abstract int MyDesignMap { get; }
|
||||
public abstract int PlayerHouseList { get; }
|
||||
public abstract int NpcHouseList { get; }
|
||||
public abstract int ShopKabu { get; }
|
||||
public abstract int Museum { get; }
|
||||
public abstract int Visitor { get; }
|
||||
|
||||
public abstract int SaveFg { get; }
|
||||
public abstract int BulletinBoard { get; }
|
||||
public abstract int AirportThemeColor { get; }
|
||||
|
||||
public abstract int LostItemBox { get; }
|
||||
public abstract int LastSavedTime { get; }
|
||||
|
||||
public abstract int VillagerSize { get; }
|
||||
public abstract int VillagerHouseSize { get; }
|
||||
public abstract int PlayerHouseSize { get; }
|
||||
public abstract int PlayerRoomSize { get; }
|
||||
|
||||
public abstract IVillager ReadVillager(byte[] data);
|
||||
public abstract IVillagerHouse ReadVillagerHouse(byte[] data);
|
||||
public abstract IPlayerHouse ReadPlayerHouse(byte[] data);
|
||||
public abstract IPlayerRoom ReadPlayerRoom(byte[] data);
|
||||
|
||||
public static MainSaveOffsets GetOffsets(FileHeaderInfo Info)
|
||||
var rev = Info.GetKnownRevisionIndex();
|
||||
return rev switch
|
||||
{
|
||||
var rev = Info.GetKnownRevisionIndex();
|
||||
return rev switch
|
||||
{
|
||||
0 => new MainSaveOffsets10(),
|
||||
1 => new MainSaveOffsets11(),
|
||||
2 => new MainSaveOffsets11(),
|
||||
3 => new MainSaveOffsets11(),
|
||||
4 => new MainSaveOffsets11(),
|
||||
5 => new MainSaveOffsets11(),
|
||||
6 => new MainSaveOffsets12(),
|
||||
7 => new MainSaveOffsets12(),
|
||||
8 => new MainSaveOffsets13(),
|
||||
9 => new MainSaveOffsets13(),
|
||||
10 => new MainSaveOffsets14(),
|
||||
11 => new MainSaveOffsets14(),
|
||||
12 => new MainSaveOffsets14(),
|
||||
13 => new MainSaveOffsets15(),
|
||||
14 => new MainSaveOffsets15(),
|
||||
15 => new MainSaveOffsets16(),
|
||||
16 => new MainSaveOffsets17(),
|
||||
17 => new MainSaveOffsets18(),
|
||||
18 => new MainSaveOffsets19(),
|
||||
19 => new MainSaveOffsets110(),
|
||||
20 => new MainSaveOffsets111(),
|
||||
21 => new MainSaveOffsets111(),
|
||||
22 => new MainSaveOffsets20(),
|
||||
23 => new MainSaveOffsets20(),
|
||||
24 => new MainSaveOffsets20(),
|
||||
25 => new MainSaveOffsets20(),
|
||||
26 => new MainSaveOffsets20(),
|
||||
27 => new MainSaveOffsets20(),
|
||||
28 => new MainSaveOffsets20(),
|
||||
29 => new MainSaveOffsets20(),
|
||||
30 => new MainSaveOffsets20(),
|
||||
_ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info),
|
||||
};
|
||||
}
|
||||
|
||||
public DesignPattern ReadPattern(byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return ReadPatternAtOffset(data, LandMyDesign + (index * DesignPattern.SIZE));
|
||||
}
|
||||
|
||||
public static DesignPattern ReadPatternAtOffset(byte[] data, int offset)
|
||||
{
|
||||
var v = data.Slice(offset, DesignPattern.SIZE);
|
||||
return new DesignPattern(v);
|
||||
}
|
||||
|
||||
public void WritePattern(DesignPattern p, byte[] data, int index, byte[] playerID, byte[] townID)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
playerID.CopyTo(p.Data, 0x54); // overwrite playerID bytes so player owns
|
||||
townID.CopyTo(p.Data, 0x38); // overwrite townID bytes so player owns
|
||||
byte[] wipeflag = new byte[] { 0x02, 0xEE, 0x00, 0x00 }; // wipe so player owns
|
||||
wipeflag.CopyTo(p.Data, 0x70);
|
||||
p.Data.CopyTo(data, LandMyDesign + (index * DesignPattern.SIZE));
|
||||
byte[] editedflag = new byte[] { 0x00 };
|
||||
editedflag.CopyTo(data, PatternsEditFlagStart + index); // set edited flag for name import to work
|
||||
}
|
||||
|
||||
public DesignPatternPRO ReadPatternPRO(byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var ofs = PatternsPRO + (index * DesignPatternPRO.SIZE);
|
||||
return ReadPatternPROAtOffset(data, ofs);
|
||||
}
|
||||
|
||||
public static DesignPatternPRO ReadPatternPROAtOffset(byte[] data, int ofs)
|
||||
{
|
||||
var v = data.Slice(ofs, DesignPatternPRO.SIZE);
|
||||
return new DesignPatternPRO(v);
|
||||
}
|
||||
|
||||
public void WritePatternPRO(DesignPatternPRO p, byte[] data, int index, byte[] playerID, byte[] townID)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
playerID.CopyTo(p.Data, 0x54); // overwrite playerID bytes so player owns
|
||||
townID.CopyTo(p.Data, 0x38); // overwrite townID bytes so player owns
|
||||
byte[] wipeflag = new byte[] { 0x00, 0x00, 0x00, 0x00 }; // wipe so player owns
|
||||
wipeflag.CopyTo(p.Data, 0x70);
|
||||
p.Data.CopyTo(data, PatternsPRO + (index * DesignPatternPRO.SIZE));
|
||||
byte[] editedflag = new byte[] { 0x00 };
|
||||
editedflag.CopyTo(data, PatternsProEditFlagStart + index);
|
||||
}
|
||||
|
||||
public IVillager ReadVillager(byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
var size = VillagerSize;
|
||||
var v = data.Slice(Animal + (index * size), size);
|
||||
return ReadVillager(v);
|
||||
}
|
||||
|
||||
public void WriteVillager(IVillager v, byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var size = VillagerSize;
|
||||
v.Write().CopyTo(data, Animal + (index * size));
|
||||
}
|
||||
|
||||
public IVillagerHouse ReadVillagerHouse(byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
var size = VillagerHouseSize;
|
||||
var v = data.Slice(NpcHouseList + (index * size), size);
|
||||
return ReadVillagerHouse(v);
|
||||
}
|
||||
|
||||
public void WriteVillagerHouse(IVillagerHouse v, byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var size = VillagerHouseSize;
|
||||
v.Write().CopyTo(data, NpcHouseList + (index * size));
|
||||
}
|
||||
|
||||
public IPlayerHouse ReadPlayerHouse(byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= PlayerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
var size = PlayerHouseSize;
|
||||
var v = data.Slice(PlayerHouseList + (index * size), size);
|
||||
return ReadPlayerHouse(v);
|
||||
}
|
||||
|
||||
public void WritePlayerHouse(IPlayerHouse v, byte[] data, int index)
|
||||
{
|
||||
if ((uint)index >= PlayerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var size = PlayerHouseSize;
|
||||
v.Write().CopyTo(data, PlayerHouseList + (index * size));
|
||||
}
|
||||
0 => new MainSaveOffsets10(),
|
||||
1 => new MainSaveOffsets11(),
|
||||
2 => new MainSaveOffsets11(),
|
||||
3 => new MainSaveOffsets11(),
|
||||
4 => new MainSaveOffsets11(),
|
||||
5 => new MainSaveOffsets11(),
|
||||
6 => new MainSaveOffsets12(),
|
||||
7 => new MainSaveOffsets12(),
|
||||
8 => new MainSaveOffsets13(),
|
||||
9 => new MainSaveOffsets13(),
|
||||
10 => new MainSaveOffsets14(),
|
||||
11 => new MainSaveOffsets14(),
|
||||
12 => new MainSaveOffsets14(),
|
||||
13 => new MainSaveOffsets15(),
|
||||
14 => new MainSaveOffsets15(),
|
||||
15 => new MainSaveOffsets16(),
|
||||
16 => new MainSaveOffsets17(),
|
||||
17 => new MainSaveOffsets18(),
|
||||
18 => new MainSaveOffsets19(),
|
||||
19 => new MainSaveOffsets110(),
|
||||
20 => new MainSaveOffsets111(),
|
||||
21 => new MainSaveOffsets111(),
|
||||
22 => new MainSaveOffsets20(),
|
||||
23 => new MainSaveOffsets20(),
|
||||
24 => new MainSaveOffsets20(),
|
||||
25 => new MainSaveOffsets20(),
|
||||
26 => new MainSaveOffsets20(),
|
||||
27 => new MainSaveOffsets20(),
|
||||
28 => new MainSaveOffsets20(),
|
||||
29 => new MainSaveOffsets20(),
|
||||
30 => new MainSaveOffsets20(),
|
||||
_ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public DesignPattern ReadPattern(ReadOnlySpan<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
return ReadPatternAtOffset(data, LandMyDesign + (index * DesignPattern.SIZE));
|
||||
}
|
||||
|
||||
public static DesignPattern ReadPatternAtOffset(ReadOnlySpan<byte> data, int offset)
|
||||
{
|
||||
var v = data.Slice(offset, DesignPattern.SIZE).ToArray();
|
||||
return new DesignPattern(v);
|
||||
}
|
||||
|
||||
public void WritePattern(DesignPattern p, Span<byte> data, int index, Span<byte> playerID, Span<byte> townID)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
playerID.CopyTo(p.Data[0x54..]); // overwrite playerID bytes so player owns
|
||||
townID.CopyTo(p.Data[0x38..]); // overwrite townID bytes so player owns
|
||||
ReadOnlySpan<byte> wipeflag = [0x02, 0xEE, 0x00, 0x00]; // wipe so player owns
|
||||
wipeflag.CopyTo(p.Data[0x70..]);
|
||||
p.Data.CopyTo(data[(LandMyDesign + (index * DesignPattern.SIZE))..]);
|
||||
ReadOnlySpan<byte> editedflag = [0x00];
|
||||
editedflag.CopyTo(data[(PatternsEditFlagStart + index)..]); // set edited flag for name import to work
|
||||
}
|
||||
|
||||
public DesignPatternPRO ReadPatternPRO(ReadOnlySpan<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var ofs = PatternsPRO + (index * DesignPatternPRO.SIZE);
|
||||
return ReadPatternPROAtOffset(data, ofs);
|
||||
}
|
||||
|
||||
public static DesignPatternPRO ReadPatternPROAtOffset(ReadOnlySpan<byte> data, int ofs)
|
||||
{
|
||||
var v = data.Slice(ofs, DesignPatternPRO.SIZE).ToArray();
|
||||
return new DesignPatternPRO(v);
|
||||
}
|
||||
|
||||
public void WritePatternPRO(DesignPatternPRO p, Span<byte> data, int index, Span<byte> playerID, Span<byte> townID)
|
||||
{
|
||||
if ((uint)index >= PatternCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
playerID.CopyTo(p.Data[0x54..]); // overwrite playerID bytes so player owns
|
||||
townID.CopyTo(p.Data[0x38..]); // overwrite townID bytes so player owns
|
||||
ReadOnlySpan<byte> wipeflag = [0x00, 0x00, 0x00, 0x00]; // wipe so player owns
|
||||
wipeflag.CopyTo(p.Data[0x70..]);
|
||||
p.Data.CopyTo(data[(PatternsPRO + (index * DesignPatternPRO.SIZE))..]);
|
||||
ReadOnlySpan<byte> editedflag = [0x00];
|
||||
editedflag.CopyTo(data[(PatternsProEditFlagStart + index)..]); // set edited flag for name import to work
|
||||
}
|
||||
|
||||
public IVillager ReadVillager(ReadOnlySpan<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
var size = VillagerSize;
|
||||
var v = data.Slice(Animal + (index * size), size).ToArray();
|
||||
return ReadVillager(v);
|
||||
}
|
||||
|
||||
public void WriteVillager(IVillager v, Span<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var size = VillagerSize;
|
||||
v.Write().CopyTo(data[(Animal + (index * size))..]);
|
||||
}
|
||||
|
||||
public IVillagerHouse ReadVillagerHouse(ReadOnlySpan<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
var size = VillagerHouseSize;
|
||||
var v = data.Slice(NpcHouseList + (index * size), size).ToArray();
|
||||
return ReadVillagerHouse(v);
|
||||
}
|
||||
|
||||
public void WriteVillagerHouse(IVillagerHouse v, Span<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= VillagerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var size = VillagerHouseSize;
|
||||
v.Write().CopyTo(data[(NpcHouseList + (index * size))..]);
|
||||
}
|
||||
|
||||
public IPlayerHouse ReadPlayerHouse(ReadOnlySpan<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= PlayerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
var size = PlayerHouseSize;
|
||||
var v = data.Slice(PlayerHouseList + (index * size), size).ToArray();
|
||||
return ReadPlayerHouse(v);
|
||||
}
|
||||
|
||||
public void WritePlayerHouse(IPlayerHouse v, Span<byte> data, int index)
|
||||
{
|
||||
if ((uint)index >= PlayerCount)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
var size = PlayerHouseSize;
|
||||
v.Write().CopyTo(data[(PlayerHouseList + (index * size))..]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +1,66 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets10 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets10 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x108;
|
||||
public override int Animal => GSaveLandStart + 0x08;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x108;
|
||||
public override int Animal => GSaveLandStart + 0x08;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D71E8;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D71E8;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x200FF4;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x200FF4;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x2017F4;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x2017F4;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DBAA0;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40DAA0;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DBAA0;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40DAA0;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x40ECE8;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x411A80;
|
||||
public override int Visitor => GSaveLandStart + 0x414E84;
|
||||
public override int SaveFg => GSaveLandStart + 0x4150B4;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x4159F8;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F6600;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x40ECE8;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x411A80;
|
||||
public override int Visitor => GSaveLandStart + 0x414E84;
|
||||
public override int SaveFg => GSaveLandStart + 0x4150B4;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x4159F8;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F6600;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FA1E0;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FA1E0;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5C1E20;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5C6748;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5C1E20;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5C6748;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager1(data);
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager1(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,64 +1,65 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets11 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets11 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6F98;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6F98;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x412218;
|
||||
public override int Visitor => GSaveLandStart + 0x41561C;
|
||||
public override int SaveFg => GSaveLandStart + 0x41584C;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416190;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F6D98;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x412218;
|
||||
public override int Visitor => GSaveLandStart + 0x41561C;
|
||||
public override int SaveFg => GSaveLandStart + 0x41584C;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416190;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F6D98;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FAA80;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FAA80;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5C33F0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5C7D48;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5C33F0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5C7D48;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager1(data);
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager1(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,66 +1,67 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets110 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets110 : MainSaveOffsets
|
||||
{
|
||||
public override int PatternCount => PatternCount2;
|
||||
public override int PatternCount => PatternCount2;
|
||||
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2610;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2610;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23c0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23c0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22d9b8;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22d9b8;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22e1b8;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22e1b8;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x308be4;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x43abe4;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x308be4;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x43abe4;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x43be2c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x43f1e0;
|
||||
public override int Visitor => GSaveLandStart + 0x4425e4;
|
||||
public override int SaveFg => GSaveLandStart + 0x442814;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x443158;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x523d60;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x43be2c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x43f1e0;
|
||||
public override int Visitor => GSaveLandStart + 0x4425e4;
|
||||
public override int SaveFg => GSaveLandStart + 0x442814;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x443158;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x523d60;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x527ab0;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x527ab0;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3408c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x345220;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3408c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x345220;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,67 +1,68 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="MainSaveOffsets110"/></remarks>
|
||||
public class MainSaveOffsets111 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="MainSaveOffsets110"/></remarks>
|
||||
public class MainSaveOffsets111 : MainSaveOffsets
|
||||
{
|
||||
public override int PatternCount => PatternCount2;
|
||||
public override int PatternCount => PatternCount2;
|
||||
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2610;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2610;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23c0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23c0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22d9b8;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22d9b8;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22e1b8;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22e1b8;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x308be4;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x43abe4;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x308be4;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x43abe4;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x43be2c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x43f1e0;
|
||||
public override int Visitor => GSaveLandStart + 0x4425e4;
|
||||
public override int SaveFg => GSaveLandStart + 0x442814;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x443158;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x523d60;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x43be2c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x43f1e0;
|
||||
public override int Visitor => GSaveLandStart + 0x4425e4;
|
||||
public override int SaveFg => GSaveLandStart + 0x442814;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x443158;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x523d60;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x527ab0;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x527ab0;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3408c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x345220;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3408c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x345220;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,64 +1,65 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets12 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets12 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x4124C4;
|
||||
public override int Visitor => GSaveLandStart + 0x4158C8;
|
||||
public override int SaveFg => GSaveLandStart + 0x415AF8;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416440;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F7048;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x4124C4;
|
||||
public override int Visitor => GSaveLandStart + 0x4158C8;
|
||||
public override int SaveFg => GSaveLandStart + 0x415AF8;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416440;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F7048;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FAD30;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FAD30;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5CF370;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5D3CC8;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5CF370;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5D3CC8;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager1(data);
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager1(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,64 +1,65 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets13 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets13 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x4124C4;
|
||||
public override int Visitor => GSaveLandStart + 0x4158C8;
|
||||
public override int SaveFg => GSaveLandStart + 0x415AF8;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416440;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F7048;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x4124C4;
|
||||
public override int Visitor => GSaveLandStart + 0x4158C8;
|
||||
public override int SaveFg => GSaveLandStart + 0x415AF8;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416440;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F7048;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FAD60;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FAD60;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5CF3F0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5D3D48;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x5CF3F0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x5D3D48;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager1(data);
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager1(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,64 +1,65 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets14 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets14 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1D7200;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1D6FB0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20100C;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20180C;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2DC238;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x40E238;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x4124C4;
|
||||
public override int Visitor => GSaveLandStart + 0x4158C8;
|
||||
public override int SaveFg => GSaveLandStart + 0x415AF8;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416440;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F7048;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x40F480;
|
||||
public override int ShopKabu => GSaveShop + 0x2AD0; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x4124C4;
|
||||
public override int Visitor => GSaveLandStart + 0x4158C8;
|
||||
public override int SaveFg => GSaveLandStart + 0x415AF8;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x416440;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x4F7048;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FADA0;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x4FADA0;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x605E70;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x60A708;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x605E70;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x60A708;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager1(data);
|
||||
public override int VillagerSize => Villager1.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager1(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,64 +1,65 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets15 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets15 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20c40c;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20c40c;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20cc0c;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20cc0c;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e7638;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x419638;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e7638;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x419638;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x41a880;
|
||||
public override int ShopKabu => GSaveShop + 0x2b10; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x41d904;
|
||||
public override int Visitor => GSaveLandStart + 0x420d08;
|
||||
public override int SaveFg => GSaveLandStart + 0x420f38;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x421880;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x502488;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x41a880;
|
||||
public override int ShopKabu => GSaveShop + 0x2b10; // part of shop
|
||||
public override int Museum => GSaveLandStart + 0x41d904;
|
||||
public override int Visitor => GSaveLandStart + 0x420d08;
|
||||
public override int SaveFg => GSaveLandStart + 0x420f38;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x421880;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x502488;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x5061e0;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x5061e0;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x6159f0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x61a288;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x6159f0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x61a288;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,64 +1,65 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets16 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets16 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20c40c;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20c40c;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20cc0c;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20cc0c;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e7638;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x419638;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e7638;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x419638;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x41a880;
|
||||
public override int ShopKabu => GSaveShop + 0x2be0; // part of shop; tailor increased size
|
||||
public override int Museum => GSaveLandStart + 0x41d9d4;
|
||||
public override int Visitor => GSaveLandStart + 0x420dd8;
|
||||
public override int SaveFg => GSaveLandStart + 0x421008;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x421950;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x502558;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x41a880;
|
||||
public override int ShopKabu => GSaveShop + 0x2be0; // part of shop; tailor increased size
|
||||
public override int Museum => GSaveLandStart + 0x41d9d4;
|
||||
public override int Visitor => GSaveLandStart + 0x420dd8;
|
||||
public override int SaveFg => GSaveLandStart + 0x421008;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x421950;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x502558;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x5062b0;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x5062b0;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x61a4f0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x61ed88;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x61a4f0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x61ed88;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,64 +1,65 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets17 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets17 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20a408;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20a408;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20ac08;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20ac08;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e5634;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x417634;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e5634;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x417634;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x41887c;
|
||||
public override int ShopKabu => GSaveShop + 0x2cb0; // part of shop; tailor increased size
|
||||
public override int Museum => GSaveLandStart + 0x41bba0;
|
||||
public override int Visitor => GSaveLandStart + 0x41efa4;
|
||||
public override int SaveFg => GSaveLandStart + 0x41f1d4;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x41fb18;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x500720;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x41887c;
|
||||
public override int ShopKabu => GSaveShop + 0x2cb0; // part of shop; tailor increased size
|
||||
public override int Museum => GSaveLandStart + 0x41bba0;
|
||||
public override int Visitor => GSaveLandStart + 0x41efa4;
|
||||
public override int SaveFg => GSaveLandStart + 0x41f1d4;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x41fb18;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x500720;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x504470;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x504470;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x340680;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x344f18;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x340680;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x344f18;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,65 +1,66 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="MainSaveOffsets17"/></remarks>.
|
||||
public class MainSaveOffsets18 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="MainSaveOffsets17"/></remarks>.
|
||||
public class MainSaveOffsets18 : MainSaveOffsets
|
||||
{
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20a408;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x20a408;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20ac08;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x20ac08;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e5634;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x417634;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x2e5634;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x417634;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x41887c;
|
||||
public override int ShopKabu => GSaveShop + 0x2cb0; // part of shop; tailor increased size
|
||||
public override int Museum => GSaveLandStart + 0x41bba0;
|
||||
public override int Visitor => GSaveLandStart + 0x41efa4;
|
||||
public override int SaveFg => GSaveLandStart + 0x41f1d4;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x41fb18;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x500720;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x41887c;
|
||||
public override int ShopKabu => GSaveShop + 0x2cb0; // part of shop; tailor increased size
|
||||
public override int Museum => GSaveLandStart + 0x41bba0;
|
||||
public override int Visitor => GSaveLandStart + 0x41efa4;
|
||||
public override int SaveFg => GSaveLandStart + 0x41f1d4;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x41fb18;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x500720;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x504470;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x504470;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x340680;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x344f18;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x340680;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x344f18;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,66 +1,67 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets19 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
public class MainSaveOffsets19 : MainSaveOffsets
|
||||
{
|
||||
public override int PatternCount => PatternCount2;
|
||||
public override int PatternCount => PatternCount2;
|
||||
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e2600;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e23B0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22d9a8;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22d9a8;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22e1a8;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22e1a8;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x308bd4;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x43abd4;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x308bd4;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x43abd4;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x43be1c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x43f1d0;
|
||||
public override int Visitor => GSaveLandStart + 0x4425d4;
|
||||
public override int SaveFg => GSaveLandStart + 0x442804;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x443148;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x523d50;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x43be1c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x43f1d0;
|
||||
public override int Visitor => GSaveLandStart + 0x4425d4;
|
||||
public override int SaveFg => GSaveLandStart + 0x442804;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x443148;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x523d50;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x527aa0;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x527aa0;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3408c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x345220;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3408c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x345220;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse1(data);
|
||||
public override int VillagerHouseSize => VillagerHouse1.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse1(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom1(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse1.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse1(data);
|
||||
public override int PlayerRoomSize => PlayerRoom1.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom1(data);
|
||||
}
|
||||
|
|
@ -1,67 +1,68 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="MainSaveOffsets110"/></remarks>
|
||||
public class MainSaveOffsets20 : MainSaveOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="MainSaveOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="MainSaveOffsets110"/></remarks>
|
||||
public class MainSaveOffsets20 : MainSaveOffsets
|
||||
{
|
||||
public override int PatternCount => PatternCount2;
|
||||
public override int PatternCount => PatternCount2;
|
||||
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
#region GSaveLand
|
||||
public const int GSaveLandStart = 0x110;
|
||||
public override int Animal => GSaveLandStart + 0x10;
|
||||
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e3848;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
public override int LandMyDesign => GSaveLandStart + 0x1e3848;
|
||||
public override int PatternsPRO => LandMyDesign + (PatternCount2 * DesignPattern.SIZE);
|
||||
public override int PatternFlag => PatternsPRO + (PatternCount2 * DesignPatternPRO.SIZE);
|
||||
public override int PatternTailor => PatternFlag + DesignPattern.SIZE;
|
||||
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
public override int PatternsEditFlagStart => GSaveLandStart + 0x8BE150; // x100, HasPlayerEdited?
|
||||
public override int PatternsProEditFlagStart => PatternsEditFlagStart + 0x64; // x100, HasPlayerEdited?
|
||||
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e35f0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
public const int GSaveWeather = GSaveLandStart + 0x1e35f0;
|
||||
public override int WeatherArea => GSaveWeather + 0x14; // Hemisphere
|
||||
public override int WeatherRandSeed => GSaveWeather + 0x18;
|
||||
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22ebf0;
|
||||
public override int EventFlagLand => GSaveLandStart + 0x22ebf0;
|
||||
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22f3f0;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
// GSaveMainField
|
||||
public const int GSaveMainFieldStart = GSaveLandStart + 0x22f3f0;
|
||||
public override int FieldItem => GSaveMainFieldStart + 0x00000;
|
||||
public override int LandMakingMap => GSaveMainFieldStart + 0xAAA00;
|
||||
public override int MainFieldStructure => GSaveMainFieldStart + 0xCF600;
|
||||
public override int OutsideField => GSaveMainFieldStart + 0xCF998;
|
||||
public override int MyDesignMap => GSaveMainFieldStart + 0xCFA34;
|
||||
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x30a6bc;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x44f7fc;
|
||||
public override int PlayerHouseList => GSaveLandStart + 0x30a6bc;
|
||||
public override int NpcHouseList => GSaveLandStart + 0x44f7fc;
|
||||
|
||||
public const int GSaveShop = GSaveLandStart + 0x45b50c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x45ec44;
|
||||
public override int Visitor => GSaveLandStart + 0x462048;
|
||||
public override int SaveFg => GSaveLandStart + 0x462278;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x462bc0;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x5437c8;
|
||||
#endregion
|
||||
public const int GSaveShop = GSaveLandStart + 0x45b50c;
|
||||
public override int ShopKabu => GSaveShop + 0x2d40; // part of shop; tailor & zakka increased size
|
||||
public override int Museum => GSaveLandStart + 0x45ec44;
|
||||
public override int Visitor => GSaveLandStart + 0x462048;
|
||||
public override int SaveFg => GSaveLandStart + 0x462278;
|
||||
public override int BulletinBoard => GSaveLandStart + 0x462bc0;
|
||||
public override int AirportThemeColor => GSaveLandStart + 0x5437c8;
|
||||
#endregion
|
||||
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x547520;
|
||||
#region GSaveLandOther
|
||||
public const int GSaveLandOtherStart = 0x547520;
|
||||
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3726c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x377020;
|
||||
#endregion
|
||||
public override int LostItemBox => GSaveLandOtherStart + 0x3726c0;
|
||||
public override int LastSavedTime => GSaveLandOtherStart + 0x377020;
|
||||
#endregion
|
||||
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(byte[] data) => new Villager2(data);
|
||||
public override int VillagerSize => Villager2.SIZE;
|
||||
public override IVillager ReadVillager(Memory<byte> data) => new Villager2(data);
|
||||
|
||||
public override int VillagerHouseSize => VillagerHouse2.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(byte[] data) => new VillagerHouse2(data);
|
||||
public override int VillagerHouseSize => VillagerHouse2.SIZE;
|
||||
public override IVillagerHouse ReadVillagerHouse(Memory<byte> data) => new VillagerHouse2(data);
|
||||
|
||||
public override int PlayerHouseSize => PlayerHouse2.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(byte[] data) => new PlayerHouse2(data);
|
||||
public override int PlayerRoomSize => PlayerRoom2.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(byte[] data) => new PlayerRoom2(data);
|
||||
}
|
||||
}
|
||||
public override int PlayerHouseSize => PlayerHouse2.SIZE;
|
||||
public override IPlayerHouse ReadPlayerHouse(Memory<byte> data) => new PlayerHouse2(data);
|
||||
public override int PlayerRoomSize => PlayerRoom2.SIZE;
|
||||
public override IPlayerRoom ReadPlayerRoom(Memory<byte> data) => new PlayerRoom2(data);
|
||||
}
|
||||
|
|
@ -1,85 +1,84 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Offset info and object retrieval logic for <see cref="Personal"/>
|
||||
/// </summary>
|
||||
public abstract class PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// Offset info and object retrieval logic for <see cref="Personal"/>
|
||||
/// </summary>
|
||||
public abstract class PersonalOffsets
|
||||
public abstract int PersonalId { get; }
|
||||
public abstract int EventFlagsPlayer { get; }
|
||||
public abstract int CountAchievement { get; }
|
||||
public abstract int Wallet { get; }
|
||||
public abstract int NowPoint { get; }
|
||||
public abstract int TotalPoint { get; }
|
||||
public abstract int Birthday { get; }
|
||||
|
||||
public abstract int ProfileMain { get; }
|
||||
public abstract int ProfilePhoto { get; }
|
||||
public abstract int ProfileBirthday { get; }
|
||||
public abstract int ProfileFruit { get; }
|
||||
public abstract int ProfileTimestamp { get; }
|
||||
public abstract int ProfileIsMakeVillage { get; }
|
||||
|
||||
public abstract int Pockets1 { get; }
|
||||
public abstract int Pockets2 { get; }
|
||||
public abstract int ItemChest { get; }
|
||||
public abstract int ItemCollectBit { get; }
|
||||
public abstract int ItemRemakeCollectBit { get; }
|
||||
public abstract int Manpu { get; } // reactions
|
||||
|
||||
public abstract int Bank { get; }
|
||||
public abstract int Recipes { get; }
|
||||
|
||||
public int MaxAchievementID { get; } = 512;
|
||||
public int Pockets1Count { get; } = 20;
|
||||
public int Pockets2Count { get; } = 20;
|
||||
public int ItemChestCount { get; } = 5000;
|
||||
public abstract int MaxRecipeID { get; }
|
||||
public abstract int MaxRemakeBitFlag { get; }
|
||||
|
||||
public static PersonalOffsets GetOffsets(FileHeaderInfo Info)
|
||||
{
|
||||
public abstract int PersonalId { get; }
|
||||
public abstract int EventFlagsPlayer { get; }
|
||||
public abstract int CountAchievement { get; }
|
||||
public abstract int Wallet { get; }
|
||||
public abstract int NowPoint { get; }
|
||||
public abstract int TotalPoint { get; }
|
||||
public abstract int Birthday { get; }
|
||||
|
||||
public abstract int ProfileMain { get; }
|
||||
public abstract int ProfilePhoto { get; }
|
||||
public abstract int ProfileBirthday { get; }
|
||||
public abstract int ProfileFruit { get; }
|
||||
public abstract int ProfileTimestamp { get; }
|
||||
public abstract int ProfileIsMakeVillage { get; }
|
||||
|
||||
public abstract int Pockets1 { get; }
|
||||
public abstract int Pockets2 { get; }
|
||||
public abstract int ItemChest { get; }
|
||||
public abstract int ItemCollectBit { get; }
|
||||
public abstract int ItemRemakeCollectBit { get; }
|
||||
public abstract int Manpu { get; } // reactions
|
||||
|
||||
public abstract int Bank { get; }
|
||||
public abstract int Recipes { get; }
|
||||
|
||||
public int MaxAchievementID { get; } = 512;
|
||||
public int Pockets1Count { get; } = 20;
|
||||
public int Pockets2Count { get; } = 20;
|
||||
public int ItemChestCount { get; } = 5000;
|
||||
public abstract int MaxRecipeID { get; }
|
||||
public abstract int MaxRemakeBitFlag { get; }
|
||||
|
||||
public static PersonalOffsets GetOffsets(FileHeaderInfo Info)
|
||||
var rev = Info.GetKnownRevisionIndex();
|
||||
return rev switch
|
||||
{
|
||||
var rev = Info.GetKnownRevisionIndex();
|
||||
return rev switch
|
||||
{
|
||||
0 => new PersonalOffsets10(),
|
||||
1 => new PersonalOffsets11(),
|
||||
2 => new PersonalOffsets11(),
|
||||
3 => new PersonalOffsets11(),
|
||||
4 => new PersonalOffsets11(),
|
||||
5 => new PersonalOffsets11(),
|
||||
6 => new PersonalOffsets12(),
|
||||
7 => new PersonalOffsets12(),
|
||||
8 => new PersonalOffsets13(),
|
||||
9 => new PersonalOffsets13(),
|
||||
10 => new PersonalOffsets14(),
|
||||
11 => new PersonalOffsets14(),
|
||||
12 => new PersonalOffsets14(),
|
||||
13 => new PersonalOffsets15(),
|
||||
14 => new PersonalOffsets15(),
|
||||
15 => new PersonalOffsets16(),
|
||||
16 => new PersonalOffsets17(),
|
||||
17 => new PersonalOffsets18(),
|
||||
18 => new PersonalOffsets19(),
|
||||
19 => new PersonalOffsets110(),
|
||||
20 => new PersonalOffsets111(),
|
||||
21 => new PersonalOffsets111(),
|
||||
22 => new PersonalOffsets20(),
|
||||
23 => new PersonalOffsets20(),
|
||||
24 => new PersonalOffsets20(),
|
||||
25 => new PersonalOffsets20(),
|
||||
26 => new PersonalOffsets20(),
|
||||
27 => new PersonalOffsets20(),
|
||||
28 => new PersonalOffsets20(),
|
||||
29 => new PersonalOffsets20(),
|
||||
30 => new PersonalOffsets20(),
|
||||
_ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info),
|
||||
};
|
||||
}
|
||||
|
||||
public abstract IReactionStore ReadReactions(byte[] data);
|
||||
public abstract void SetReactions(byte[] data, IReactionStore value);
|
||||
0 => new PersonalOffsets10(),
|
||||
1 => new PersonalOffsets11(),
|
||||
2 => new PersonalOffsets11(),
|
||||
3 => new PersonalOffsets11(),
|
||||
4 => new PersonalOffsets11(),
|
||||
5 => new PersonalOffsets11(),
|
||||
6 => new PersonalOffsets12(),
|
||||
7 => new PersonalOffsets12(),
|
||||
8 => new PersonalOffsets13(),
|
||||
9 => new PersonalOffsets13(),
|
||||
10 => new PersonalOffsets14(),
|
||||
11 => new PersonalOffsets14(),
|
||||
12 => new PersonalOffsets14(),
|
||||
13 => new PersonalOffsets15(),
|
||||
14 => new PersonalOffsets15(),
|
||||
15 => new PersonalOffsets16(),
|
||||
16 => new PersonalOffsets17(),
|
||||
17 => new PersonalOffsets18(),
|
||||
18 => new PersonalOffsets19(),
|
||||
19 => new PersonalOffsets110(),
|
||||
20 => new PersonalOffsets111(),
|
||||
21 => new PersonalOffsets111(),
|
||||
22 => new PersonalOffsets20(),
|
||||
23 => new PersonalOffsets20(),
|
||||
24 => new PersonalOffsets20(),
|
||||
25 => new PersonalOffsets20(),
|
||||
26 => new PersonalOffsets20(),
|
||||
27 => new PersonalOffsets20(),
|
||||
28 => new PersonalOffsets20(),
|
||||
29 => new PersonalOffsets20(),
|
||||
30 => new PersonalOffsets20(),
|
||||
_ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info),
|
||||
};
|
||||
}
|
||||
|
||||
public abstract IReactionStore ReadReactions(ReadOnlySpan<byte> data);
|
||||
public abstract void SetReactions(Span<byte> data, IReactionStore value);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets10 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets10 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x108;
|
||||
private const int Player = 0x108;
|
||||
|
||||
public override int PersonalId => Player + 0xAF98;
|
||||
public override int EventFlagsPlayer => Player + 0xAFD0;
|
||||
public override int PersonalId => Player + 0xAF98;
|
||||
public override int EventFlagsPlayer => Player + 0xAFD0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFD0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFD0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x11478;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x11478;
|
||||
|
||||
public override int ProfileMain => Player + 0x11488;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23040;
|
||||
public override int ProfileFruit => ProfileMain + 0x23044;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230B4;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230B8;
|
||||
public override int ProfileMain => Player + 0x11488;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23040;
|
||||
public override int ProfileFruit => ProfileMain + 0x23044;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230B4;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230B8;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x35BD0;
|
||||
private const int PlayerOther = 0x35BD0;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x4;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x180;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA04C;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7A0;
|
||||
public override int Manpu => PlayerOther + 0xAF70;
|
||||
public override int Bank => PlayerOther + 0x33014;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x4;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x180;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA04C;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7A0;
|
||||
public override int Manpu => PlayerOther + 0xAF70;
|
||||
public override int Bank => PlayerOther + 0x33014;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x2A0;
|
||||
public override int MaxRemakeBitFlag => 0x754 * 32;
|
||||
public override int MaxRecipeID => 0x2A0;
|
||||
public override int MaxRemakeBitFlag => 0x754 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets11 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets11 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98;
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98;
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x11488;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x11488;
|
||||
|
||||
public override int ProfileMain => Player + 0x114A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x114A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x35C10;
|
||||
private const int PlayerOther = 0x35C10;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x33024;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x33024;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x2C8;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x2C8;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,48 +1,49 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets18"/>.</remarks>
|
||||
public sealed class PersonalOffsets110 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets18"/>.</remarks>
|
||||
public sealed class PersonalOffsets110 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,48 +1,49 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets18"/>.</remarks>
|
||||
public sealed class PersonalOffsets111 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets18"/>.</remarks>
|
||||
public sealed class PersonalOffsets111 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x3D6;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x3D6;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets12 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets12 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1168C;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1168C;
|
||||
|
||||
public override int ProfileMain => Player + 0x116A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x116A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x35E40;
|
||||
private const int PlayerOther = 0x35E40;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x345E4;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x345E4;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x2DA; // mermaid stuff
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x2DA; // mermaid stuff
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets13 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets13 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1168C;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1168C;
|
||||
|
||||
public override int ProfileMain => Player + 0x116A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x116A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x35E40;
|
||||
private const int PlayerOther = 0x35E40;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x345E4;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x345E4;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x2DA;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x2DA;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets14 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets14 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1168C;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1168C;
|
||||
|
||||
public override int ProfileMain => Player + 0x116A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x116A0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x35E40;
|
||||
private const int PlayerOther = 0x35E40;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x350C4;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x350C4;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x2DA;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x2DA;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure<GSavePlayerManpu>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets15 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets15 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x35244;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x35244;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x2E1;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x2E1;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets16 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets16 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x35244;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x35244;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x2E1;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x2E1;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets17 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets17 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,48 +1,49 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets17"/>.</remarks>
|
||||
public sealed class PersonalOffsets18 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets17"/>.</remarks>
|
||||
public sealed class PersonalOffsets18 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,48 +1,49 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets18"/>.</remarks>
|
||||
public sealed class PersonalOffsets19 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
/// <remarks>Same as <see cref="PersonalOffsets18"/>.</remarks>
|
||||
public sealed class PersonalOffsets19 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x22594;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x308;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,47 +1,48 @@
|
|||
namespace NHSE.Core
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets20 : PersonalOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class PersonalOffsets20 : PersonalOffsets
|
||||
{
|
||||
private const int Player = 0x110;
|
||||
private const int Player = 0x110;
|
||||
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
public override int PersonalId => Player + 0xAFA8;
|
||||
public override int EventFlagsPlayer => Player + 0xAFE0;
|
||||
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
private const int GSaveLifeSupport = Player + 0xBFE0;
|
||||
public override int CountAchievement => GSaveLifeSupport + 0xE98; // CountAchievement
|
||||
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
public override int NowPoint => GSaveLifeSupport + 0x5498; // Nook Miles
|
||||
public override int TotalPoint => NowPoint + 8; // Total Nook Miles Earned
|
||||
public override int Birthday => Player + 0x1228c;
|
||||
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
public override int ProfileMain => Player + 0x122a0;
|
||||
public override int ProfilePhoto => ProfileMain + 0x14;
|
||||
public override int ProfileBirthday => ProfileMain + 0x23058;
|
||||
public override int ProfileFruit => ProfileMain + 0x2305C;
|
||||
public override int ProfileTimestamp => ProfileMain + 0x230CC;
|
||||
public override int ProfileIsMakeVillage => ProfileMain + 0x230D0;
|
||||
|
||||
// end player
|
||||
// end player
|
||||
|
||||
private const int PlayerOther = 0x36a50;
|
||||
private const int PlayerOther = 0x36a50;
|
||||
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x24afc;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
public override int Pockets1 => PlayerOther + 0x10;
|
||||
public override int Pockets2 => Pockets1 + (8 * Pockets1Count) + 0x18;
|
||||
public override int Wallet => Pockets2 + (8 * Pockets2Count) + 0x18;
|
||||
public override int ItemChest => PlayerOther + 0x18C;
|
||||
public override int ItemCollectBit => PlayerOther + 0xA058;
|
||||
public override int ItemRemakeCollectBit => PlayerOther + 0xA7AC;
|
||||
public override int Manpu => PlayerOther + 0xAF7C;
|
||||
public override int Bank => PlayerOther + 0x24afc;
|
||||
public override int Recipes => Bank + 0x10;
|
||||
|
||||
public override int MaxRecipeID => 0x430;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
public override int MaxRecipeID => 0x430;
|
||||
public override int MaxRemakeBitFlag => 0x7D0 * 32;
|
||||
|
||||
public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu);
|
||||
}
|
||||
}
|
||||
public override IReactionStore ReadReactions(ReadOnlySpan<byte> data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure<GSavePlayerManpu15>();
|
||||
public override void SetReactions(Span<byte> data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data[Manpu..]);
|
||||
}
|
||||
|
|
@ -1,30 +1,29 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Offset info and object retrieval logic for <see cref="Personal"/>
|
||||
/// </summary>
|
||||
public abstract class WhereAreNOffsets
|
||||
{
|
||||
public abstract int Poki { get; }
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static WhereAreNOffsets GetOffsets(FileHeaderInfo Info)
|
||||
/// <summary>
|
||||
/// Offset info and object retrieval logic for <see cref="Personal"/>
|
||||
/// </summary>
|
||||
public abstract class WhereAreNOffsets
|
||||
{
|
||||
public abstract int Poki { get; }
|
||||
|
||||
public static WhereAreNOffsets GetOffsets(FileHeaderInfo Info)
|
||||
{
|
||||
var rev = Info.GetKnownRevisionIndex();
|
||||
return rev switch
|
||||
{
|
||||
var rev = Info.GetKnownRevisionIndex();
|
||||
return rev switch
|
||||
{
|
||||
22 => new WhereAreNOffsets20(),
|
||||
23 => new WhereAreNOffsets20(),
|
||||
24 => new WhereAreNOffsets20(),
|
||||
25 => new WhereAreNOffsets20(),
|
||||
26 => new WhereAreNOffsets20(),
|
||||
27 => new WhereAreNOffsets20(),
|
||||
28 => new WhereAreNOffsets20(),
|
||||
29 => new WhereAreNOffsets20(),
|
||||
30 => new WhereAreNOffsets20(),
|
||||
_ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info),
|
||||
};
|
||||
}
|
||||
22 => new WhereAreNOffsets20(),
|
||||
23 => new WhereAreNOffsets20(),
|
||||
24 => new WhereAreNOffsets20(),
|
||||
25 => new WhereAreNOffsets20(),
|
||||
26 => new WhereAreNOffsets20(),
|
||||
27 => new WhereAreNOffsets20(),
|
||||
28 => new WhereAreNOffsets20(),
|
||||
29 => new WhereAreNOffsets20(),
|
||||
30 => new WhereAreNOffsets20(),
|
||||
_ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class WhereAreNOffsets20 : WhereAreNOffsets
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="PersonalOffsets"/>
|
||||
/// </summary>
|
||||
public sealed class WhereAreNOffsets20 : WhereAreNOffsets
|
||||
{
|
||||
public override int Poki => 0xB84228;
|
||||
}
|
||||
}
|
||||
public override int Poki => 0xB84228;
|
||||
}
|
||||
|
|
@ -1,36 +1,35 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Global repository for game strings; initialized to a specified language.
|
||||
/// </summary>
|
||||
public static class GameInfo
|
||||
{
|
||||
private static readonly GameStrings?[] Languages = new GameStrings[GameLanguage.LanguageCount];
|
||||
|
||||
public static string CurrentLanguage { get; private set; } = GameLanguage.DefaultLanguage;
|
||||
public static GameStrings Strings { get; private set; } = GetStrings(CurrentLanguage);
|
||||
|
||||
/// <summary>
|
||||
/// Global repository for game strings; initialized to a specified language.
|
||||
/// Gets the Game Strings for a specific language.
|
||||
/// </summary>
|
||||
public static class GameInfo
|
||||
/// <param name="lang">2 character language ID</param>
|
||||
public static GameStrings GetStrings(string lang)
|
||||
{
|
||||
private static readonly GameStrings?[] Languages = new GameStrings[GameLanguage.LanguageCount];
|
||||
|
||||
public static string CurrentLanguage { get; private set; } = GameLanguage.DefaultLanguage;
|
||||
public static GameStrings Strings { get; private set; } = GetStrings(CurrentLanguage);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Game Strings for a specific language.
|
||||
/// </summary>
|
||||
/// <param name="lang">2 character language ID</param>
|
||||
public static GameStrings GetStrings(string lang)
|
||||
{
|
||||
int index = GameLanguage.GetLanguageIndex(lang);
|
||||
return GetStrings(index);
|
||||
}
|
||||
|
||||
public static GameStrings GetStrings(int index)
|
||||
{
|
||||
return Languages[index] ??= new GameStrings(GameLanguage.Language2Char(index));
|
||||
}
|
||||
|
||||
public static string SetLanguage2Char(int index)
|
||||
{
|
||||
var lang = GameLanguage.Language2Char(index);
|
||||
CurrentLanguage = lang;
|
||||
Strings = GetStrings(lang);
|
||||
return lang;
|
||||
}
|
||||
int index = GameLanguage.GetLanguageIndex(lang);
|
||||
return GetStrings(index);
|
||||
}
|
||||
}
|
||||
|
||||
public static GameStrings GetStrings(int index)
|
||||
{
|
||||
return Languages[index] ??= new GameStrings(GameLanguage.Language2Char(index));
|
||||
}
|
||||
|
||||
public static string SetLanguage2Char(int index)
|
||||
{
|
||||
var lang = GameLanguage.Language2Char(index);
|
||||
CurrentLanguage = lang;
|
||||
Strings = GetStrings(lang);
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +1,35 @@
|
|||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Metadata for dealing with language codes
|
||||
/// </summary>
|
||||
public static class GameLanguage
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata for dealing with language codes
|
||||
/// </summary>
|
||||
public static class GameLanguage
|
||||
public const string DefaultLanguage = "en"; // English
|
||||
public static int DefaultLanguageIndex => Array.IndexOf(LanguageCodes, DefaultLanguage);
|
||||
public static string Language2Char(int lang) => lang > LanguageCodes.Length ? DefaultLanguage : LanguageCodes[lang];
|
||||
|
||||
public static int LanguageCount => LanguageCodes.Length;
|
||||
|
||||
public static int GetLanguageIndex(string lang)
|
||||
{
|
||||
public const string DefaultLanguage = "en"; // English
|
||||
public static int DefaultLanguageIndex => Array.IndexOf(LanguageCodes, DefaultLanguage);
|
||||
public static string Language2Char(int lang) => lang > LanguageCodes.Length ? DefaultLanguage : LanguageCodes[lang];
|
||||
int l = Array.IndexOf(LanguageCodes, lang);
|
||||
return l < 0 ? DefaultLanguageIndex : l;
|
||||
}
|
||||
|
||||
public static int LanguageCount => LanguageCodes.Length;
|
||||
/// <summary>
|
||||
/// Language codes supported for loading string resources
|
||||
/// </summary>
|
||||
private static readonly string[] LanguageCodes = ["en", "jp", "de", "es", "fr", "it", "ko", "zhs", "zht"];
|
||||
|
||||
public static int GetLanguageIndex(string lang)
|
||||
{
|
||||
int l = Array.IndexOf(LanguageCodes, lang);
|
||||
return l < 0 ? DefaultLanguageIndex : l;
|
||||
}
|
||||
public static string[] GetStrings(string ident, string lang, string type = "text")
|
||||
{
|
||||
string[] data = ResourceUtil.GetStringList(ident, lang, type);
|
||||
if (data.Length == 0)
|
||||
data = ResourceUtil.GetStringList(ident, DefaultLanguage, type);
|
||||
|
||||
/// <summary>
|
||||
/// Language codes supported for loading string resources
|
||||
/// </summary>
|
||||
private static readonly string[] LanguageCodes = { "en", "jp", "de", "es", "fr", "it", "ko", "zhs", "zht" };
|
||||
|
||||
public static string[] GetStrings(string ident, string lang, string type = "text")
|
||||
{
|
||||
string[] data = ResourceUtil.GetStringList(ident, lang, type);
|
||||
if (data.Length == 0)
|
||||
data = ResourceUtil.GetStringList(ident, DefaultLanguage, type);
|
||||
|
||||
return data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,255 +1,246 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores game localization strings for use by logic.
|
||||
/// </summary>
|
||||
public sealed class GameStrings : IRemakeString
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores game localization strings for use by logic.
|
||||
/// </summary>
|
||||
public sealed class GameStrings : IRemakeString
|
||||
private readonly string lang;
|
||||
|
||||
public readonly string[] villagers;
|
||||
public readonly string[] itemlist;
|
||||
public readonly string[] itemlistdisplay;
|
||||
public readonly string[] villagerDefaultPhrases;
|
||||
public readonly Dictionary<string, string> VillagerMap;
|
||||
public readonly Dictionary<string, string> VillagerDefaultPhraseMap;
|
||||
public readonly List<ComboItem> ItemDataSource;
|
||||
public readonly Dictionary<string, string> InternalNameTranslation = [];
|
||||
|
||||
public IReadOnlyDictionary<string, string> BodyParts { get; }
|
||||
public IReadOnlyDictionary<string, string> BodyColor { get; }
|
||||
public IReadOnlyDictionary<string, string> FabricParts { get; }
|
||||
public IReadOnlyDictionary<string, string> FabricColor { get; }
|
||||
|
||||
private string[] Get(string ident) => GameLanguage.GetStrings(ident, lang);
|
||||
|
||||
public GameStrings(string l)
|
||||
{
|
||||
private readonly string lang;
|
||||
lang = l;
|
||||
villagers = Get("villager");
|
||||
VillagerMap = GetMap(villagers);
|
||||
villagerDefaultPhrases = Get("phrase");
|
||||
VillagerDefaultPhraseMap = GetMap(villagerDefaultPhrases);
|
||||
itemlist = Get("item");
|
||||
itemlistdisplay = GetItemDisplayList(itemlist);
|
||||
ItemDataSource = CreateItemDataSource(itemlistdisplay);
|
||||
|
||||
public readonly string[] villagers;
|
||||
public readonly string[] itemlist;
|
||||
public readonly string[] itemlistdisplay;
|
||||
public readonly string[] villagerDefaultPhrases;
|
||||
public readonly Dictionary<string, string> VillagerMap;
|
||||
public readonly Dictionary<string, string> VillagerDefaultPhraseMap;
|
||||
public readonly List<ComboItem> ItemDataSource;
|
||||
public readonly Dictionary<string, string> InternalNameTranslation = new();
|
||||
|
||||
public IReadOnlyDictionary<string, string> BodyParts { get; }
|
||||
public IReadOnlyDictionary<string, string> BodyColor { get; }
|
||||
public IReadOnlyDictionary<string, string> FabricParts { get; }
|
||||
public IReadOnlyDictionary<string, string> FabricColor { get; }
|
||||
|
||||
private string[] Get(string ident) => GameLanguage.GetStrings(ident, lang);
|
||||
|
||||
public GameStrings(string l)
|
||||
{
|
||||
lang = l;
|
||||
villagers = Get("villager");
|
||||
VillagerMap = GetMap(villagers);
|
||||
villagerDefaultPhrases = Get("phrase");
|
||||
VillagerDefaultPhraseMap = GetMap(villagerDefaultPhrases);
|
||||
itemlist = Get("item");
|
||||
itemlistdisplay = GetItemDisplayList(itemlist);
|
||||
ItemDataSource = CreateItemDataSource(itemlistdisplay);
|
||||
|
||||
BodyParts = GetDictionary(Get("body_parts"));
|
||||
BodyColor = GetDictionary(Get("body_color"));
|
||||
FabricParts = GetDictionary(Get("fabric_parts"));
|
||||
FabricColor = GetDictionary(Get("fabric_color"));
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, string> GetDictionary(IEnumerable<string> lines, char split = '\t')
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
foreach (var s in lines)
|
||||
{
|
||||
if (s.Length == 0)
|
||||
continue;
|
||||
var index = s.IndexOf(split);
|
||||
var key = s.Substring(0, index);
|
||||
var value = s.Substring(index + 1);
|
||||
result.Add(key, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ComboItem> CreateItemDataSource(string[] strings)
|
||||
{
|
||||
var dataSource = ComboItemUtil.GetArray(strings);
|
||||
|
||||
// load special
|
||||
dataSource.Add(new ComboItem(itemlist[0], Item.NONE));
|
||||
dataSource.SortByText();
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public List<ComboItem> CreateItemDataSource(IReadOnlyCollection<ushort> dict, bool none = true)
|
||||
{
|
||||
var display = itemlistdisplay;
|
||||
var result = new List<ComboItem>(dict.Count);
|
||||
foreach (var x in dict)
|
||||
result.Add(new ComboItem(display[x], x));
|
||||
|
||||
if (none)
|
||||
result.Add(new ComboItem(itemlist[0], Item.NONE));
|
||||
|
||||
result.SortByText();
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<ComboItem> CreateItemDataSource(IReadOnlyCollection<KeyValuePair<ushort, ushort>> dict, bool none = true)
|
||||
{
|
||||
var display = itemlistdisplay;
|
||||
var result = new List<ComboItem>(dict.Count);
|
||||
foreach (var x in dict)
|
||||
result.Add(new ComboItem(display[x.Value], x.Key));
|
||||
|
||||
if (none)
|
||||
result.Add(new ComboItem(itemlist[0], Item.NONE));
|
||||
|
||||
result.SortByText();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetMap(IReadOnlyCollection<string> arr)
|
||||
{
|
||||
var map = new Dictionary<string, string>(arr.Count);
|
||||
foreach (var kvp in arr)
|
||||
{
|
||||
var index = kvp.IndexOf('\t');
|
||||
if (index < 0)
|
||||
continue;
|
||||
var abbrev = kvp.Substring(0, index);
|
||||
var name = kvp.Substring(index + 1);
|
||||
map.Add(abbrev, name);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public string GetVillager(string name)
|
||||
{
|
||||
return VillagerMap.TryGetValue(name, out var result) ? result : name;
|
||||
}
|
||||
|
||||
public string GetVillagerDefaultPhrase(string name)
|
||||
{
|
||||
return VillagerDefaultPhraseMap.TryGetValue(name, out var result) ? result : name; // I know it shouldn't be name but I have to return something
|
||||
}
|
||||
|
||||
public static string[] GetItemDisplayList(string[] items)
|
||||
{
|
||||
items = (string[])items.Clone();
|
||||
items[0] = string.Empty;
|
||||
var set = new HashSet<string>();
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
if (string.IsNullOrEmpty(item))
|
||||
items[i] = $"(Item #{i:000})";
|
||||
else if (set.Contains(item))
|
||||
items[i] += $" (#{i:000})";
|
||||
else
|
||||
set.Add(item);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public string GetItemName(Item item)
|
||||
{
|
||||
var index = item.ItemId;
|
||||
if (index == Item.NONE)
|
||||
return itemlist[0];
|
||||
if (index == Item.EXTENSION)
|
||||
return GetItemName(item.ExtensionItemId);
|
||||
|
||||
var kind = ItemInfo.GetItemKind(index);
|
||||
|
||||
if (kind.IsFlowerGene(index))
|
||||
{
|
||||
var display = GetItemName(index);
|
||||
if (item.Genes != 0)
|
||||
return $"{display} - {item.Genes}";
|
||||
}
|
||||
|
||||
if (kind == ItemKind.Kind_DIYRecipe || kind == ItemKind.Kind_MessageBottle)
|
||||
{
|
||||
var display = itemlistdisplay[index];
|
||||
var recipeID = (ushort)item.FreeParam;
|
||||
var isKnown = RecipeList.Recipes.TryGetValue(recipeID, out var result);
|
||||
var makes = isKnown ? GetItemName(result) : recipeID.ToString("000");
|
||||
return $"{display} - {makes}";
|
||||
}
|
||||
|
||||
if (kind == ItemKind.Kind_FossilUnknown)
|
||||
{
|
||||
var display = itemlistdisplay[index];
|
||||
var fossilID = (ushort)item.FreeParam;
|
||||
var fossilName = GetItemName(fossilID);
|
||||
return $"{display} - {fossilName}";
|
||||
}
|
||||
|
||||
if (kind == ItemKind.Kind_Tree)
|
||||
{
|
||||
var display = GetItemName(index);
|
||||
var willDrop = item.Count;
|
||||
if (willDrop != 0)
|
||||
{
|
||||
var dropName = GetItemName(willDrop);
|
||||
return $"{display} - {dropName}";
|
||||
}
|
||||
}
|
||||
|
||||
return GetItemName(index);
|
||||
}
|
||||
|
||||
public string GetItemName(ushort index)
|
||||
{
|
||||
if (index >= itemlistdisplay.Length)
|
||||
return GetItemName60000(index);
|
||||
return itemlistdisplay[index];
|
||||
}
|
||||
|
||||
private static string GetItemName60000(ushort index)
|
||||
{
|
||||
if (FieldItemList.Items.TryGetValue(index, out var val))
|
||||
return val.Name;
|
||||
|
||||
// 63,000 ???
|
||||
if (index == Item.LLOYD)
|
||||
return "Lloyd";
|
||||
|
||||
return "???";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns clothing or item recolors not a part of ItemRemake with brackets in their names
|
||||
/// </summary>
|
||||
/// <param name="id">ItemID of the color variation search</param>
|
||||
/// <param name="baseItemName">Item name without the associated recolors</param>
|
||||
/// <returns>Map of ItemID, ItemName</returns>
|
||||
public List<ComboItem> GetAssociatedItems(ushort id, out string baseItemName)
|
||||
{
|
||||
baseItemName = string.Empty;
|
||||
var stringMatch = GetItemName(id);
|
||||
var index = stringMatch.IndexOf('(');
|
||||
if (index < 0)
|
||||
return new List<ComboItem>();
|
||||
|
||||
var search = baseItemName = stringMatch.Substring(0, index);
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
return ItemDataSource.FindAll(x => x.Text.StartsWith(search));
|
||||
else
|
||||
return new List<ComboItem>();
|
||||
}
|
||||
|
||||
public bool HasAssociatedItems(string baseName, out List<ComboItem>? items)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(baseName))
|
||||
{
|
||||
items = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
baseName = baseName.Trim().ToLower();
|
||||
if (!baseName.EndsWith(" "))
|
||||
baseName += " ";
|
||||
baseName += "(";
|
||||
|
||||
items = ItemDataSource.FindAll(x => x.Text.ToLower().StartsWith(baseName));
|
||||
return items.Count > 0;
|
||||
}
|
||||
BodyParts = GetDictionary(Get("body_parts"));
|
||||
BodyColor = GetDictionary(Get("body_color"));
|
||||
FabricParts = GetDictionary(Get("fabric_parts"));
|
||||
FabricColor = GetDictionary(Get("fabric_color"));
|
||||
}
|
||||
|
||||
public interface IRemakeString
|
||||
private static Dictionary<string, string> GetDictionary(ReadOnlySpan<string> lines, char split = '\t')
|
||||
{
|
||||
IReadOnlyDictionary<string, string> BodyParts { get; }
|
||||
IReadOnlyDictionary<string, string> BodyColor { get; }
|
||||
IReadOnlyDictionary<string, string> FabricParts { get; }
|
||||
IReadOnlyDictionary<string, string> FabricColor { get; }
|
||||
var result = new Dictionary<string, string>();
|
||||
foreach (var s in lines)
|
||||
{
|
||||
if (s.Length == 0)
|
||||
continue;
|
||||
var index = s.IndexOf(split);
|
||||
var key = s[..index];
|
||||
var value = s[(index + 1)..];
|
||||
result.Add(key, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ComboItem> CreateItemDataSource(ReadOnlySpan<string> strings)
|
||||
{
|
||||
var dataSource = ComboItemUtil.GetArray(strings);
|
||||
|
||||
// load special
|
||||
dataSource.Add(new ComboItem(itemlist[0], Item.NONE));
|
||||
dataSource.SortByText();
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
public List<ComboItem> CreateItemDataSource(ReadOnlySpan<ushort> dict, bool none = true)
|
||||
{
|
||||
var display = itemlistdisplay;
|
||||
var result = new List<ComboItem>(dict.Length);
|
||||
foreach (var x in dict)
|
||||
result.Add(new ComboItem(display[x], x));
|
||||
|
||||
if (none)
|
||||
result.Add(new ComboItem(itemlist[0], Item.NONE));
|
||||
|
||||
result.SortByText();
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<ComboItem> CreateItemDataSource(IReadOnlyCollection<KeyValuePair<ushort, ushort>> dict, bool none = true)
|
||||
{
|
||||
var display = itemlistdisplay;
|
||||
var result = new List<ComboItem>(dict.Count);
|
||||
foreach (var x in dict)
|
||||
result.Add(new ComboItem(display[x.Value], x.Key));
|
||||
|
||||
if (none)
|
||||
result.Add(new ComboItem(itemlist[0], Item.NONE));
|
||||
|
||||
result.SortByText();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetMap(ReadOnlySpan<string> arr)
|
||||
{
|
||||
var map = new Dictionary<string, string>(arr.Length);
|
||||
foreach (var kvp in arr)
|
||||
{
|
||||
var index = kvp.IndexOf('\t');
|
||||
if (index < 0)
|
||||
continue;
|
||||
var abbrev = kvp[..index];
|
||||
var name = kvp[(index + 1)..];
|
||||
map.Add(abbrev, name);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public string GetVillager(string name) => VillagerMap.GetValueOrDefault(name, name);
|
||||
|
||||
public string GetVillagerDefaultPhrase(string name) => VillagerDefaultPhraseMap.GetValueOrDefault(name, name); // I know it shouldn't be `name` but I have to return something
|
||||
|
||||
public static string[] GetItemDisplayList(ReadOnlySpan<string> arr)
|
||||
{
|
||||
var items = arr.ToArray();
|
||||
items[0] = string.Empty;
|
||||
var set = new HashSet<string>();
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
if (string.IsNullOrEmpty(item))
|
||||
items[i] = $"(Item #{i:000})";
|
||||
else if (!set.Add(item))
|
||||
items[i] += $" (#{i:000})";
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public string GetItemName(Item item)
|
||||
{
|
||||
var index = item.ItemId;
|
||||
if (index == Item.NONE)
|
||||
return itemlist[0];
|
||||
if (index == Item.EXTENSION)
|
||||
return GetItemName(item.ExtensionItemId);
|
||||
|
||||
var kind = ItemInfo.GetItemKind(index);
|
||||
|
||||
if (kind.IsFlowerGene(index))
|
||||
{
|
||||
var display = GetItemName(index);
|
||||
if (item.Genes != 0)
|
||||
return $"{display} - {item.Genes}";
|
||||
}
|
||||
|
||||
if (kind is ItemKind.Kind_DIYRecipe or ItemKind.Kind_MessageBottle)
|
||||
{
|
||||
var display = itemlistdisplay[index];
|
||||
var recipeID = (ushort)item.FreeParam;
|
||||
var isKnown = RecipeList.Recipes.TryGetValue(recipeID, out var result);
|
||||
var makes = isKnown ? GetItemName(result) : recipeID.ToString("000");
|
||||
return $"{display} - {makes}";
|
||||
}
|
||||
|
||||
if (kind == ItemKind.Kind_FossilUnknown)
|
||||
{
|
||||
var display = itemlistdisplay[index];
|
||||
var fossilID = (ushort)item.FreeParam;
|
||||
var fossilName = GetItemName(fossilID);
|
||||
return $"{display} - {fossilName}";
|
||||
}
|
||||
|
||||
if (kind == ItemKind.Kind_Tree)
|
||||
{
|
||||
var display = GetItemName(index);
|
||||
var willDrop = item.Count;
|
||||
if (willDrop != 0)
|
||||
{
|
||||
var dropName = GetItemName(willDrop);
|
||||
return $"{display} - {dropName}";
|
||||
}
|
||||
}
|
||||
|
||||
return GetItemName(index);
|
||||
}
|
||||
|
||||
public string GetItemName(ushort index)
|
||||
{
|
||||
if (index >= itemlistdisplay.Length)
|
||||
return GetItemName60000(index);
|
||||
return itemlistdisplay[index];
|
||||
}
|
||||
|
||||
private static string GetItemName60000(ushort index)
|
||||
{
|
||||
if (FieldItemList.Items.TryGetValue(index, out var val))
|
||||
return val.Name;
|
||||
|
||||
// 63,000 ???
|
||||
if (index == Item.LLOYD)
|
||||
return "Lloyd";
|
||||
|
||||
return "???";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns clothing or item recolors not a part of ItemRemake with brackets in their names
|
||||
/// </summary>
|
||||
/// <param name="id">ItemID of the color variation search</param>
|
||||
/// <param name="baseItemName">Item name without the associated recolors</param>
|
||||
/// <returns>Map of ItemID, ItemName</returns>
|
||||
public List<ComboItem> GetAssociatedItems(ushort id, out string baseItemName)
|
||||
{
|
||||
baseItemName = string.Empty;
|
||||
var stringMatch = GetItemName(id);
|
||||
var index = stringMatch.IndexOf('(');
|
||||
if (index < 0)
|
||||
return [];
|
||||
|
||||
var search = baseItemName = stringMatch[..index];
|
||||
if (!string.IsNullOrWhiteSpace(search))
|
||||
return ItemDataSource.FindAll(x => x.Text.StartsWith(search));
|
||||
return [];
|
||||
}
|
||||
|
||||
public bool HasAssociatedItems(string baseName, out List<ComboItem>? items)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(baseName))
|
||||
{
|
||||
items = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
baseName = baseName.Trim().ToLower();
|
||||
if (!baseName.EndsWith(' '))
|
||||
baseName += " ";
|
||||
baseName += "(";
|
||||
|
||||
items = ItemDataSource.FindAll(x => x.Text.StartsWith(baseName, StringComparison.CurrentCultureIgnoreCase));
|
||||
return items.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRemakeString
|
||||
{
|
||||
IReadOnlyDictionary<string, string> BodyParts { get; }
|
||||
IReadOnlyDictionary<string, string> BodyColor { get; }
|
||||
IReadOnlyDictionary<string, string> FabricParts { get; }
|
||||
IReadOnlyDictionary<string, string> FabricColor { get; }
|
||||
}
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Material used for a <see cref="BridgeType"/> construction (footstep sounds?).
|
||||
/// </summary>
|
||||
public enum BridgeMaterial : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Material used for a <see cref="BridgeType"/> construction (footstep sounds?).
|
||||
/// </summary>
|
||||
public enum BridgeMaterial : byte
|
||||
{
|
||||
BridgeStone = 0x00,
|
||||
BridgeSuspension = 0x01,
|
||||
BridgeJapanese = 0x02,
|
||||
BridgeLog = 0x04,
|
||||
BridgeRed = 0x05,
|
||||
BridgeIron = 0x06,
|
||||
BridgeReserved = 0x07,
|
||||
BridgeWood = 0x0A,
|
||||
BridgeBricks = 0x0B,
|
||||
}
|
||||
}
|
||||
BridgeStone = 0x00,
|
||||
BridgeSuspension = 0x01,
|
||||
BridgeJapanese = 0x02,
|
||||
BridgeLog = 0x04,
|
||||
BridgeRed = 0x05,
|
||||
BridgeIron = 0x06,
|
||||
BridgeReserved = 0x07,
|
||||
BridgeWood = 0x0A,
|
||||
BridgeBricks = 0x0B,
|
||||
}
|
||||
|
|
@ -1,63 +1,62 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Bridge model
|
||||
/// </summary>
|
||||
public enum BridgeType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Bridge model
|
||||
/// </summary>
|
||||
public enum BridgeType : ushort
|
||||
{
|
||||
BridgeStone03 = 0x00,
|
||||
BridgeStone04 = 0x01,
|
||||
BridgeStone05 = 0x02,
|
||||
BridgeStoneDiagonal025 = 0x03,
|
||||
BridgeStoneDiagonal030 = 0x04,
|
||||
BridgeStoneDiagonal035 = 0x05,
|
||||
BridgeSuspension04 = 0x06,
|
||||
BridgeSuspension03 = 0x07,
|
||||
BridgeSuspension05 = 0x08,
|
||||
BridgeSuspensionDiagonal025 = 0x09,
|
||||
BridgeSuspensionDiagonal030 = 0x0A,
|
||||
BridgeSuspensionDiagonal035 = 0x0B,
|
||||
BridgeJapanese04 = 0x0C,
|
||||
BridgeLog04 = 0x0D,
|
||||
BridgeLog03 = 0x0E,
|
||||
BridgeLog05 = 0x0F,
|
||||
BridgeLogDiagonal025 = 0x10,
|
||||
BridgeLogDiagonal030 = 0x11,
|
||||
BridgeLogDiagonal035 = 0x12,
|
||||
BridgeJapanese03 = 0x13,
|
||||
BridgeJapanese05 = 0x14,
|
||||
BridgeJapaneseDiagonal025 = 0x15,
|
||||
BridgeJapaneseDiagonal030 = 0x16,
|
||||
BridgeJapaneseDiagonal035 = 0x17,
|
||||
BridgeRed04 = 0x18,
|
||||
BridgeIron04 = 0x19,
|
||||
BridgeReserved03 = 0x1A,
|
||||
BridgeReserved04 = 0x1B,
|
||||
BridgeReserved05 = 0x1C,
|
||||
BridgeReservedDiagonal025 = 0x1D,
|
||||
BridgeReservedDiagonal030 = 0x1E,
|
||||
BridgeReservedDiagonal035 = 0x1F,
|
||||
BridgeRed03 = 0x20,
|
||||
BridgeRed05 = 0x21,
|
||||
BridgeRedDiagonal025 = 0x22,
|
||||
BridgeRedDiagonal030 = 0x23,
|
||||
BridgeRedDiagonal035 = 0x24,
|
||||
BridgeWood04 = 0x25,
|
||||
BridgeBricks04 = 0x26,
|
||||
BridgeIron03 = 0x27,
|
||||
BridgeIron05 = 0x28,
|
||||
BridgeIronDiagonal025 = 0x29,
|
||||
BridgeIronDiagonal030 = 0x2A,
|
||||
BridgeIronDiagonal035 = 0x2B,
|
||||
BridgeWood03 = 0x2C,
|
||||
BridgeWood05 = 0x2D,
|
||||
BridgeWoodDiagonal025 = 0x2E,
|
||||
BridgeWoodDiagonal030 = 0x2F,
|
||||
BridgeWoodDiagonal035 = 0x30,
|
||||
BridgeBricks03 = 0x31,
|
||||
BridgeBricks05 = 0x34,
|
||||
BridgeBricksDiagonal025 = 0x35,
|
||||
BridgeBricksDiagonal030 = 0x36,
|
||||
BridgeBricksDiagonal035 = 0x37,
|
||||
}
|
||||
}
|
||||
BridgeStone03 = 0x00,
|
||||
BridgeStone04 = 0x01,
|
||||
BridgeStone05 = 0x02,
|
||||
BridgeStoneDiagonal025 = 0x03,
|
||||
BridgeStoneDiagonal030 = 0x04,
|
||||
BridgeStoneDiagonal035 = 0x05,
|
||||
BridgeSuspension04 = 0x06,
|
||||
BridgeSuspension03 = 0x07,
|
||||
BridgeSuspension05 = 0x08,
|
||||
BridgeSuspensionDiagonal025 = 0x09,
|
||||
BridgeSuspensionDiagonal030 = 0x0A,
|
||||
BridgeSuspensionDiagonal035 = 0x0B,
|
||||
BridgeJapanese04 = 0x0C,
|
||||
BridgeLog04 = 0x0D,
|
||||
BridgeLog03 = 0x0E,
|
||||
BridgeLog05 = 0x0F,
|
||||
BridgeLogDiagonal025 = 0x10,
|
||||
BridgeLogDiagonal030 = 0x11,
|
||||
BridgeLogDiagonal035 = 0x12,
|
||||
BridgeJapanese03 = 0x13,
|
||||
BridgeJapanese05 = 0x14,
|
||||
BridgeJapaneseDiagonal025 = 0x15,
|
||||
BridgeJapaneseDiagonal030 = 0x16,
|
||||
BridgeJapaneseDiagonal035 = 0x17,
|
||||
BridgeRed04 = 0x18,
|
||||
BridgeIron04 = 0x19,
|
||||
BridgeReserved03 = 0x1A,
|
||||
BridgeReserved04 = 0x1B,
|
||||
BridgeReserved05 = 0x1C,
|
||||
BridgeReservedDiagonal025 = 0x1D,
|
||||
BridgeReservedDiagonal030 = 0x1E,
|
||||
BridgeReservedDiagonal035 = 0x1F,
|
||||
BridgeRed03 = 0x20,
|
||||
BridgeRed05 = 0x21,
|
||||
BridgeRedDiagonal025 = 0x22,
|
||||
BridgeRedDiagonal030 = 0x23,
|
||||
BridgeRedDiagonal035 = 0x24,
|
||||
BridgeWood04 = 0x25,
|
||||
BridgeBricks04 = 0x26,
|
||||
BridgeIron03 = 0x27,
|
||||
BridgeIron05 = 0x28,
|
||||
BridgeIronDiagonal025 = 0x29,
|
||||
BridgeIronDiagonal030 = 0x2A,
|
||||
BridgeIronDiagonal035 = 0x2B,
|
||||
BridgeWood03 = 0x2C,
|
||||
BridgeWood05 = 0x2D,
|
||||
BridgeWoodDiagonal025 = 0x2E,
|
||||
BridgeWoodDiagonal030 = 0x2F,
|
||||
BridgeWoodDiagonal035 = 0x30,
|
||||
BridgeBricks03 = 0x31,
|
||||
BridgeBricks05 = 0x34,
|
||||
BridgeBricksDiagonal025 = 0x35,
|
||||
BridgeBricksDiagonal030 = 0x36,
|
||||
BridgeBricksDiagonal035 = 0x37,
|
||||
}
|
||||
|
|
@ -1,54 +1,54 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Interact-able structure that can be entered by the player.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 1)]
|
||||
public class Building
|
||||
{
|
||||
/// <summary>
|
||||
/// Interact-able structure that can be entered by the player.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Size = SIZE, Pack = 1)]
|
||||
public class Building
|
||||
public const int SIZE = 0x14;
|
||||
|
||||
[field: FieldOffset(0x00)] public BuildingType BuildingType { get; set; }
|
||||
[field: FieldOffset(0x02)] public ushort X { get; set; }
|
||||
[field: FieldOffset(0x04)] public ushort Y { get; set; }
|
||||
|
||||
[field: FieldOffset(0x06)] public byte Angle { get; set; }
|
||||
[field: FieldOffset(0x07)] public sbyte Bit { get; set; }
|
||||
|
||||
[field: FieldOffset(0x08)] public ushort Type { get; set; }
|
||||
[field: FieldOffset(0x0A)] public byte TypeArg { get; set; }
|
||||
|
||||
[field: FieldOffset(0x0C)] public ushort UniqueID { get; set; }
|
||||
[field: FieldOffset(0x10)] public uint Unused { get; set; }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
public const int SIZE = 0x14;
|
||||
|
||||
[field: FieldOffset(0x00)] public BuildingType BuildingType { get; set; }
|
||||
[field: FieldOffset(0x02)] public ushort X { get; set; }
|
||||
[field: FieldOffset(0x04)] public ushort Y { get; set; }
|
||||
|
||||
[field: FieldOffset(0x06)] public byte Angle { get; set; }
|
||||
[field: FieldOffset(0x07)] public sbyte Bit { get; set; }
|
||||
|
||||
[field: FieldOffset(0x08)] public ushort Type { get; set; }
|
||||
[field: FieldOffset(0x0A)] public byte TypeArg { get; set; }
|
||||
|
||||
[field: FieldOffset(0x0C)] public ushort UniqueID { get; set; }
|
||||
[field: FieldOffset(0x10)] public uint Unused { get; set; }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
BuildingType = 0;
|
||||
X = Y = Angle = 0;
|
||||
Bit = 0;
|
||||
Type = TypeArg = 0;
|
||||
UniqueID = 0;
|
||||
Unused = 0;
|
||||
}
|
||||
|
||||
public void CopyFrom(Building building)
|
||||
{
|
||||
BuildingType = building.BuildingType;
|
||||
X = building.X;
|
||||
Y = building.Y;
|
||||
Angle = building.Angle;
|
||||
Bit = building.Bit;
|
||||
Type = building.Type;
|
||||
TypeArg = building.TypeArg;
|
||||
UniqueID = building.UniqueID;
|
||||
Unused = building.Unused;
|
||||
}
|
||||
|
||||
public static Building[] GetArray(byte[] data) => data.GetArray<Building>(SIZE);
|
||||
public static byte[] SetArray(IReadOnlyList<Building> data) => data.SetArray(SIZE);
|
||||
public override string ToString() => $"{X:000},{Y:000} - {BuildingType}";
|
||||
BuildingType = 0;
|
||||
X = Y = Angle = 0;
|
||||
Bit = 0;
|
||||
Type = TypeArg = 0;
|
||||
UniqueID = 0;
|
||||
Unused = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyFrom(Building building)
|
||||
{
|
||||
BuildingType = building.BuildingType;
|
||||
X = building.X;
|
||||
Y = building.Y;
|
||||
Angle = building.Angle;
|
||||
Bit = building.Bit;
|
||||
Type = building.Type;
|
||||
TypeArg = building.TypeArg;
|
||||
UniqueID = building.UniqueID;
|
||||
Unused = building.Unused;
|
||||
}
|
||||
|
||||
public static Building[] GetArray(ReadOnlySpan<byte> data) => data.GetArray<Building>(SIZE);
|
||||
public static byte[] SetArray(IReadOnlyList<Building> data) => data.SetArray(SIZE);
|
||||
public override string ToString() => $"{X:000},{Y:000} - {BuildingType}";
|
||||
}
|
||||
|
|
@ -1,39 +1,38 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Building model & interior that is loaded when the <see cref="Building"/> is entered.
|
||||
/// </summary>
|
||||
public enum BuildingType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Building model & interior that is loaded when the <see cref="Building"/> is entered.
|
||||
/// </summary>
|
||||
public enum BuildingType : ushort
|
||||
{
|
||||
None = 0,
|
||||
PlayerHouse1 = 1,
|
||||
PlayerHouse2 = 2,
|
||||
PlayerHouse3 = 3,
|
||||
PlayerHouse4 = 4,
|
||||
PlayerHouse5 = 5,
|
||||
PlayerHouse6 = 6,
|
||||
PlayerHouse7 = 7,
|
||||
PlayerHouse8 = 8,
|
||||
Villager1 = 9,
|
||||
Villager2 = 10,
|
||||
Villager3 = 11,
|
||||
Villager4 = 12,
|
||||
Villager5 = 13,
|
||||
Villager6 = 14,
|
||||
Villager7 = 15,
|
||||
Villager8 = 16,
|
||||
Villager9 = 17,
|
||||
Villager10 = 18,
|
||||
NooksCranny = 19,
|
||||
ResidentCenterStructure = 20,
|
||||
Museum = 21,
|
||||
Airport = 22,
|
||||
ResidentCenterTent = 23,
|
||||
AblesSisters = 24,
|
||||
Campsite = 25,
|
||||
Bridge = 26,
|
||||
Incline = 27,
|
||||
ReddsTreasureTrawler = 28,
|
||||
Studio = 29,
|
||||
}
|
||||
}
|
||||
None = 0,
|
||||
PlayerHouse1 = 1,
|
||||
PlayerHouse2 = 2,
|
||||
PlayerHouse3 = 3,
|
||||
PlayerHouse4 = 4,
|
||||
PlayerHouse5 = 5,
|
||||
PlayerHouse6 = 6,
|
||||
PlayerHouse7 = 7,
|
||||
PlayerHouse8 = 8,
|
||||
Villager1 = 9,
|
||||
Villager2 = 10,
|
||||
Villager3 = 11,
|
||||
Villager4 = 12,
|
||||
Villager5 = 13,
|
||||
Villager6 = 14,
|
||||
Villager7 = 15,
|
||||
Villager8 = 16,
|
||||
Villager9 = 17,
|
||||
Villager10 = 18,
|
||||
NooksCranny = 19,
|
||||
ResidentCenterStructure = 20,
|
||||
Museum = 21,
|
||||
Airport = 22,
|
||||
ResidentCenterTent = 23,
|
||||
AblesSisters = 24,
|
||||
Campsite = 25,
|
||||
Bridge = 26,
|
||||
Incline = 27,
|
||||
ReddsTreasureTrawler = 28,
|
||||
Studio = 29,
|
||||
}
|
||||
|
|
@ -1,262 +1,261 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Door model for a house.
|
||||
/// </summary>
|
||||
public enum DoorKind : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Door model for a house.
|
||||
/// </summary>
|
||||
public enum DoorKind : ushort
|
||||
{
|
||||
HouseDoorStandardAR = 0x00,
|
||||
HouseDoorStandardAS = 0x02,
|
||||
HouseDoorIronAS = 0x03,
|
||||
HouseDoorIronAR = 0x04,
|
||||
HouseDoorIronpartsAS = 0x05,
|
||||
HouseDoorIronpartsAR = 0x06,
|
||||
HouseTentPADoor = 0x07,
|
||||
HouseTentNADoor = 0x08,
|
||||
HouseDoorReliefAS = 0x09,
|
||||
HouseDoorReliefAR = 0x0A,
|
||||
HouseDoorJapaneseAS = 0x0E,
|
||||
HouseDoorJapaneseAR = 0x0F,
|
||||
HouseDoorWindowAS = 0x10,
|
||||
HouseDoorWindowAR = 0x11,
|
||||
HouseDoorIronGrillAS = 0x12,
|
||||
HouseDoorIronGrillAR = 0x13,
|
||||
HouseDoorChineseAS = 0x14,
|
||||
HouseDoorChineseAR = 0x15,
|
||||
HouseDoorSimpleAS = 0x16,
|
||||
HouseDoorSimpleAR = 0x17,
|
||||
_1 = 0x18,
|
||||
_2 = 0x19,
|
||||
HouseDoorCarvingAS = 0x1A,
|
||||
HouseDoorCarvingAR = 0x1B,
|
||||
HouseDoorCercleWindowAS = 0x1C,
|
||||
HouseDoorCercleWindowAR = 0x1D,
|
||||
HouseDoorVerticalWindowAS = 0x1E,
|
||||
HouseDoorVerticalWindowAR = 0x1F,
|
||||
HouseDoorLatticeAS = 0x20,
|
||||
HouseDoorLatticeAR = 0x21,
|
||||
HouseDoorSimplicityAS = 0x22,
|
||||
HouseDoorSimplicityAR = 0x23,
|
||||
_3 = 0x25,
|
||||
_4 = 0x26,
|
||||
HouseTentNBDoor = 0x27,
|
||||
HouseDoorIronGrillBS = 0x28,
|
||||
HouseDoorIronGrillCS = 0x29,
|
||||
HouseDoorIronGrillDS = 0x2A,
|
||||
HouseDoorIronGrillES = 0x2B,
|
||||
HouseDoorIronGrillFS = 0x2C,
|
||||
HouseDoorIronGrillBR = 0x2D,
|
||||
HouseDoorIronGrillCR = 0x2E,
|
||||
HouseDoorIronGrillDR = 0x2F,
|
||||
HouseDoorIronGrillER = 0x30,
|
||||
HouseDoorIronGrillFR = 0x31,
|
||||
HouseDoorCarvingBS = 0x32,
|
||||
HouseDoorCarvingCS = 0x33,
|
||||
HouseDoorCarvingDS = 0x34,
|
||||
HouseDoorCarvingES = 0x35,
|
||||
HouseDoorCarvingBR = 0x36,
|
||||
HouseDoorCarvingCR = 0x37,
|
||||
HouseDoorCarvingDR = 0x38,
|
||||
HouseDoorCarvingER = 0x39,
|
||||
HouseDoorIronGrillGS = 0x3A,
|
||||
HouseDoorIronGrillGR = 0x3B,
|
||||
HouseDoorIronGrillHS = 0x3C,
|
||||
HouseDoorIronGrillHR = 0x3D,
|
||||
HouseDoorVerticalWindowBS = 0x3E,
|
||||
HouseDoorVerticalWindowCS = 0x3F,
|
||||
HouseDoorVerticalWindowDS = 0x40,
|
||||
HouseDoorVerticalWindowES = 0x41,
|
||||
HouseDoorVerticalWindowFS = 0x42,
|
||||
HouseDoorVerticalWindowGS = 0x43,
|
||||
HouseDoorVerticalWindowHS = 0x44,
|
||||
HouseDoorVerticalWindowBR = 0x45,
|
||||
HouseDoorVerticalWindowCR = 0x46,
|
||||
HouseDoorVerticalWindowDR = 0x47,
|
||||
HouseDoorVerticalWindowER = 0x48,
|
||||
HouseDoorVerticalWindowFR = 0x49,
|
||||
HouseDoorVerticalWindowGR = 0x4A,
|
||||
HouseDoorVerticalWindowHR = 0x4B,
|
||||
HouseDoorReliefBS = 0x4C,
|
||||
HouseDoorReliefCS = 0x4D,
|
||||
HouseDoorReliefDS = 0x4E,
|
||||
HouseDoorReliefES = 0x4F,
|
||||
HouseDoorReliefFS = 0x50,
|
||||
HouseDoorReliefGS = 0x51,
|
||||
HouseDoorReliefHS = 0x52,
|
||||
HouseDoorReliefBR = 0x53,
|
||||
HouseDoorReliefCR = 0x54,
|
||||
HouseDoorReliefDR = 0x55,
|
||||
HouseDoorReliefER = 0x56,
|
||||
HouseDoorReliefFR = 0x57,
|
||||
HouseDoorReliefGR = 0x58,
|
||||
HouseDoorReliefHR = 0x59,
|
||||
HouseDoorIronpartsBS = 0x5A,
|
||||
HouseDoorIronpartsCS = 0x5B,
|
||||
HouseDoorIronpartsDS = 0x5C,
|
||||
HouseDoorIronpartsES = 0x5D,
|
||||
HouseDoorIronpartsFS = 0x5E,
|
||||
HouseDoorIronpartsGS = 0x5F,
|
||||
HouseDoorIronpartsHS = 0x60,
|
||||
HouseDoorIronpartsBR = 0x61,
|
||||
HouseDoorIronpartsCR = 0x62,
|
||||
HouseDoorIronpartsDR = 0x63,
|
||||
HouseDoorIronpartsER = 0x64,
|
||||
HouseDoorIronpartsFR = 0x65,
|
||||
HouseDoorIronpartsGR = 0x66,
|
||||
HouseDoorIronpartsHR = 0x67,
|
||||
HouseDoorSimpleBS = 0x84,
|
||||
HouseDoorSimpleCS = 0x85,
|
||||
HouseDoorSimpleDS = 0x86,
|
||||
HouseDoorSimpleES = 0x87,
|
||||
HouseDoorSimpleFS = 0x88,
|
||||
HouseDoorSimpleGS = 0x89,
|
||||
HouseDoorSimpleHS = 0x8A,
|
||||
HouseDoorSimpleBR = 0x8B,
|
||||
HouseDoorSimpleCR = 0x8C,
|
||||
HouseDoorSimpleDR = 0x8D,
|
||||
HouseDoorSimpleER = 0x8E,
|
||||
HouseDoorSimpleFR = 0x8F,
|
||||
HouseDoorSimpleGR = 0x90,
|
||||
HouseDoorSimpleHR = 0x91,
|
||||
HouseDoorJapaneseBS = 0x92,
|
||||
HouseDoorJapaneseCS = 0x93,
|
||||
HouseDoorJapaneseDS = 0x94,
|
||||
HouseDoorJapaneseES = 0x95,
|
||||
HouseDoorJapaneseFS = 0x96,
|
||||
HouseDoorJapaneseBR = 0x97,
|
||||
HouseDoorJapaneseCR = 0x98,
|
||||
HouseDoorJapaneseDR = 0x99,
|
||||
HouseDoorJapaneseER = 0x9A,
|
||||
HouseDoorJapaneseFR = 0x9B,
|
||||
HouseDoorLatticeBS = 0x9C,
|
||||
HouseDoorLatticeCS = 0x9D,
|
||||
HouseDoorLatticeDS = 0x9E,
|
||||
HouseDoorLatticeES = 0x9F,
|
||||
HouseDoorLatticeFS = 0xA0,
|
||||
HouseDoorLatticeGS = 0xA1,
|
||||
HouseDoorLatticeHS = 0xA2,
|
||||
HouseDoorLatticeBR = 0xA3,
|
||||
HouseDoorLatticeCR = 0xA4,
|
||||
HouseDoorLatticeDR = 0xA5,
|
||||
HouseDoorLatticeER = 0xA6,
|
||||
HouseDoorLatticeFR = 0xA7,
|
||||
HouseDoorLatticeGR = 0xA8,
|
||||
HouseDoorLatticeHR = 0xA9,
|
||||
HouseDoorCercleWindowBS = 0xAA,
|
||||
HouseDoorCercleWindowCS = 0xAB,
|
||||
HouseDoorCercleWindowDS = 0xAC,
|
||||
HouseDoorCercleWindowES = 0xAD,
|
||||
HouseDoorCercleWindowFS = 0xAE,
|
||||
HouseDoorCercleWindowGS = 0xAF,
|
||||
HouseDoorCercleWindowHS = 0xB0,
|
||||
HouseDoorCercleWindowBR = 0xB1,
|
||||
HouseDoorCercleWindowCR = 0xB2,
|
||||
HouseDoorCercleWindowDR = 0xB3,
|
||||
HouseDoorCercleWindowER = 0xB4,
|
||||
HouseDoorCercleWindowFR = 0xB5,
|
||||
HouseDoorCercleWindowGR = 0xB6,
|
||||
HouseDoorCercleWindowHR = 0xB7,
|
||||
HouseDoorJapaneseGS = 0xB8,
|
||||
HouseDoorJapaneseGR = 0xB9,
|
||||
HouseDoorSimplicityBS = 0xBA,
|
||||
HouseDoorSimplicityCS = 0xBB,
|
||||
HouseDoorSimplicityDS = 0xBC,
|
||||
HouseDoorSimplicityES = 0xBD,
|
||||
HouseDoorSimplicityFS = 0xBE,
|
||||
HouseDoorSimplicityGS = 0xBF,
|
||||
HouseDoorSimplicityHS = 0xC0,
|
||||
HouseDoorSimplicityBR = 0xC1,
|
||||
HouseDoorSimplicityCR = 0xC2,
|
||||
HouseDoorSimplicityDR = 0xC3,
|
||||
HouseDoorSimplicityER = 0xC4,
|
||||
HouseDoorSimplicityFR = 0xC5,
|
||||
HouseDoorSimplicityGR = 0xC6,
|
||||
HouseDoorSimplicityHR = 0xC7,
|
||||
HouseDoorChineseBS = 0xC8,
|
||||
HouseDoorChineseCS = 0xC9,
|
||||
HouseDoorChineseDS = 0xCA,
|
||||
HouseDoorChineseES = 0xCB,
|
||||
HouseDoorChineseFS = 0xCC,
|
||||
HouseDoorChineseGS = 0xCD,
|
||||
HouseDoorChineseHS = 0xCE,
|
||||
HouseDoorChineseBR = 0xCF,
|
||||
HouseDoorChineseCR = 0xD0,
|
||||
HouseDoorChineseDR = 0xD1,
|
||||
HouseDoorChineseER = 0xD2,
|
||||
HouseDoorChineseFR = 0xD3,
|
||||
HouseDoorChineseGR = 0xD4,
|
||||
HouseDoorChineseHR = 0xD5,
|
||||
HouseDoorWindowBS = 0xDD,
|
||||
HouseDoorWindowCS = 0xDE,
|
||||
HouseDoorWindowDS = 0xDF,
|
||||
HouseDoorWindowES = 0xE0,
|
||||
HouseDoorWindowFS = 0xE1,
|
||||
HouseDoorWindowGS = 0xE2,
|
||||
HouseDoorWindowHS = 0xE3,
|
||||
HouseDoorWindowBR = 0xE4,
|
||||
HouseDoorWindowCR = 0xE5,
|
||||
HouseDoorWindowDR = 0xE6,
|
||||
HouseDoorWindowER = 0xE7,
|
||||
HouseDoorWindowFR = 0xE8,
|
||||
HouseDoorWindowGR = 0xE9,
|
||||
HouseDoorWindowHR = 0xEA,
|
||||
HouseDoorIronBS = 0xEB,
|
||||
HouseDoorIronCS = 0xEC,
|
||||
HouseDoorIronDS = 0xED,
|
||||
HouseDoorIronES = 0xEE,
|
||||
HouseDoorIronFS = 0xEF,
|
||||
HouseDoorIronGS = 0xF0,
|
||||
HouseDoorIronHS = 0xF1,
|
||||
HouseDoorIronBR = 0xF2,
|
||||
HouseDoorIronCR = 0xF3,
|
||||
HouseDoorIronDR = 0xF4,
|
||||
HouseDoorIronER = 0xF5,
|
||||
HouseDoorIronFR = 0xF6,
|
||||
HouseDoorIronGR = 0xF7,
|
||||
HouseDoorIronHR = 0xF8,
|
||||
HouseDoorStandardBS = 0xF9,
|
||||
HouseDoorStandardCS = 0xFA,
|
||||
HouseDoorStandardDS = 0xFB,
|
||||
HouseDoorStandardES = 0xFC,
|
||||
HouseDoorStandardFS = 0xFD,
|
||||
HouseDoorStandardGS = 0xFE,
|
||||
HouseDoorStandardHS = 0xFF,
|
||||
HouseDoorStandardBR = 0x100,
|
||||
HouseDoorStandardCR = 0x101,
|
||||
HouseDoorStandardDR = 0x102,
|
||||
HouseDoorStandardER = 0x103,
|
||||
HouseDoorStandardFR = 0x104,
|
||||
HouseDoorStandardGR = 0x105,
|
||||
HouseDoorStandardHR = 0x106,
|
||||
HouseDoorReliefIS = 0x107,
|
||||
HouseDoorReliefIR = 0x108,
|
||||
HouseDoorReliefJS = 0x109,
|
||||
HouseDoorReliefJR = 0x10A,
|
||||
HouseDoorCarvingFS = 0x10B,
|
||||
HouseDoorCarvingFR = 0x10C,
|
||||
HouseDoorCercleWindowIS = 0x10D,
|
||||
HouseDoorCercleWindowIR = 0x10E,
|
||||
HouseDoorCercleWindowJS = 0x10F,
|
||||
HouseDoorCercleWindowJR = 0x110,
|
||||
HouseDoorVerticalWindowIS = 0x111,
|
||||
HouseDoorVerticalWindowIR = 0x112,
|
||||
HouseDoorVerticalWindowJS = 0x113,
|
||||
HouseDoorVerticalWindowJR = 0x114,
|
||||
HouseDoorWindowIS = 0x115,
|
||||
HouseDoorWindowIR = 0x116,
|
||||
HouseDoorWindowJS = 0x117,
|
||||
HouseDoorWindowJR = 0x118,
|
||||
HouseDoorStandardIS = 0x119,
|
||||
HouseDoorStandardIR = 0x11A,
|
||||
HouseDoorStandardJS = 0x11B,
|
||||
HouseDoorStandardJR = 0x11C,
|
||||
HouseDoorSimplicityIS = 0x11D,
|
||||
HouseDoorSimplicityIR = 0x11E,
|
||||
HouseDoorSimplicityJS = 0x11F,
|
||||
HouseDoorSimplicityJR = 0x120,
|
||||
HouseDoorIronGrillIS = 0x121,
|
||||
HouseDoorIronGrillIR = 0x122,
|
||||
HouseDoorIronGrillJS = 0x123,
|
||||
HouseDoorIronGrillJR = 0x124,
|
||||
}
|
||||
}
|
||||
HouseDoorStandardAR = 0x00,
|
||||
HouseDoorStandardAS = 0x02,
|
||||
HouseDoorIronAS = 0x03,
|
||||
HouseDoorIronAR = 0x04,
|
||||
HouseDoorIronpartsAS = 0x05,
|
||||
HouseDoorIronpartsAR = 0x06,
|
||||
HouseTentPADoor = 0x07,
|
||||
HouseTentNADoor = 0x08,
|
||||
HouseDoorReliefAS = 0x09,
|
||||
HouseDoorReliefAR = 0x0A,
|
||||
HouseDoorJapaneseAS = 0x0E,
|
||||
HouseDoorJapaneseAR = 0x0F,
|
||||
HouseDoorWindowAS = 0x10,
|
||||
HouseDoorWindowAR = 0x11,
|
||||
HouseDoorIronGrillAS = 0x12,
|
||||
HouseDoorIronGrillAR = 0x13,
|
||||
HouseDoorChineseAS = 0x14,
|
||||
HouseDoorChineseAR = 0x15,
|
||||
HouseDoorSimpleAS = 0x16,
|
||||
HouseDoorSimpleAR = 0x17,
|
||||
_1 = 0x18,
|
||||
_2 = 0x19,
|
||||
HouseDoorCarvingAS = 0x1A,
|
||||
HouseDoorCarvingAR = 0x1B,
|
||||
HouseDoorCercleWindowAS = 0x1C,
|
||||
HouseDoorCercleWindowAR = 0x1D,
|
||||
HouseDoorVerticalWindowAS = 0x1E,
|
||||
HouseDoorVerticalWindowAR = 0x1F,
|
||||
HouseDoorLatticeAS = 0x20,
|
||||
HouseDoorLatticeAR = 0x21,
|
||||
HouseDoorSimplicityAS = 0x22,
|
||||
HouseDoorSimplicityAR = 0x23,
|
||||
_3 = 0x25,
|
||||
_4 = 0x26,
|
||||
HouseTentNBDoor = 0x27,
|
||||
HouseDoorIronGrillBS = 0x28,
|
||||
HouseDoorIronGrillCS = 0x29,
|
||||
HouseDoorIronGrillDS = 0x2A,
|
||||
HouseDoorIronGrillES = 0x2B,
|
||||
HouseDoorIronGrillFS = 0x2C,
|
||||
HouseDoorIronGrillBR = 0x2D,
|
||||
HouseDoorIronGrillCR = 0x2E,
|
||||
HouseDoorIronGrillDR = 0x2F,
|
||||
HouseDoorIronGrillER = 0x30,
|
||||
HouseDoorIronGrillFR = 0x31,
|
||||
HouseDoorCarvingBS = 0x32,
|
||||
HouseDoorCarvingCS = 0x33,
|
||||
HouseDoorCarvingDS = 0x34,
|
||||
HouseDoorCarvingES = 0x35,
|
||||
HouseDoorCarvingBR = 0x36,
|
||||
HouseDoorCarvingCR = 0x37,
|
||||
HouseDoorCarvingDR = 0x38,
|
||||
HouseDoorCarvingER = 0x39,
|
||||
HouseDoorIronGrillGS = 0x3A,
|
||||
HouseDoorIronGrillGR = 0x3B,
|
||||
HouseDoorIronGrillHS = 0x3C,
|
||||
HouseDoorIronGrillHR = 0x3D,
|
||||
HouseDoorVerticalWindowBS = 0x3E,
|
||||
HouseDoorVerticalWindowCS = 0x3F,
|
||||
HouseDoorVerticalWindowDS = 0x40,
|
||||
HouseDoorVerticalWindowES = 0x41,
|
||||
HouseDoorVerticalWindowFS = 0x42,
|
||||
HouseDoorVerticalWindowGS = 0x43,
|
||||
HouseDoorVerticalWindowHS = 0x44,
|
||||
HouseDoorVerticalWindowBR = 0x45,
|
||||
HouseDoorVerticalWindowCR = 0x46,
|
||||
HouseDoorVerticalWindowDR = 0x47,
|
||||
HouseDoorVerticalWindowER = 0x48,
|
||||
HouseDoorVerticalWindowFR = 0x49,
|
||||
HouseDoorVerticalWindowGR = 0x4A,
|
||||
HouseDoorVerticalWindowHR = 0x4B,
|
||||
HouseDoorReliefBS = 0x4C,
|
||||
HouseDoorReliefCS = 0x4D,
|
||||
HouseDoorReliefDS = 0x4E,
|
||||
HouseDoorReliefES = 0x4F,
|
||||
HouseDoorReliefFS = 0x50,
|
||||
HouseDoorReliefGS = 0x51,
|
||||
HouseDoorReliefHS = 0x52,
|
||||
HouseDoorReliefBR = 0x53,
|
||||
HouseDoorReliefCR = 0x54,
|
||||
HouseDoorReliefDR = 0x55,
|
||||
HouseDoorReliefER = 0x56,
|
||||
HouseDoorReliefFR = 0x57,
|
||||
HouseDoorReliefGR = 0x58,
|
||||
HouseDoorReliefHR = 0x59,
|
||||
HouseDoorIronpartsBS = 0x5A,
|
||||
HouseDoorIronpartsCS = 0x5B,
|
||||
HouseDoorIronpartsDS = 0x5C,
|
||||
HouseDoorIronpartsES = 0x5D,
|
||||
HouseDoorIronpartsFS = 0x5E,
|
||||
HouseDoorIronpartsGS = 0x5F,
|
||||
HouseDoorIronpartsHS = 0x60,
|
||||
HouseDoorIronpartsBR = 0x61,
|
||||
HouseDoorIronpartsCR = 0x62,
|
||||
HouseDoorIronpartsDR = 0x63,
|
||||
HouseDoorIronpartsER = 0x64,
|
||||
HouseDoorIronpartsFR = 0x65,
|
||||
HouseDoorIronpartsGR = 0x66,
|
||||
HouseDoorIronpartsHR = 0x67,
|
||||
HouseDoorSimpleBS = 0x84,
|
||||
HouseDoorSimpleCS = 0x85,
|
||||
HouseDoorSimpleDS = 0x86,
|
||||
HouseDoorSimpleES = 0x87,
|
||||
HouseDoorSimpleFS = 0x88,
|
||||
HouseDoorSimpleGS = 0x89,
|
||||
HouseDoorSimpleHS = 0x8A,
|
||||
HouseDoorSimpleBR = 0x8B,
|
||||
HouseDoorSimpleCR = 0x8C,
|
||||
HouseDoorSimpleDR = 0x8D,
|
||||
HouseDoorSimpleER = 0x8E,
|
||||
HouseDoorSimpleFR = 0x8F,
|
||||
HouseDoorSimpleGR = 0x90,
|
||||
HouseDoorSimpleHR = 0x91,
|
||||
HouseDoorJapaneseBS = 0x92,
|
||||
HouseDoorJapaneseCS = 0x93,
|
||||
HouseDoorJapaneseDS = 0x94,
|
||||
HouseDoorJapaneseES = 0x95,
|
||||
HouseDoorJapaneseFS = 0x96,
|
||||
HouseDoorJapaneseBR = 0x97,
|
||||
HouseDoorJapaneseCR = 0x98,
|
||||
HouseDoorJapaneseDR = 0x99,
|
||||
HouseDoorJapaneseER = 0x9A,
|
||||
HouseDoorJapaneseFR = 0x9B,
|
||||
HouseDoorLatticeBS = 0x9C,
|
||||
HouseDoorLatticeCS = 0x9D,
|
||||
HouseDoorLatticeDS = 0x9E,
|
||||
HouseDoorLatticeES = 0x9F,
|
||||
HouseDoorLatticeFS = 0xA0,
|
||||
HouseDoorLatticeGS = 0xA1,
|
||||
HouseDoorLatticeHS = 0xA2,
|
||||
HouseDoorLatticeBR = 0xA3,
|
||||
HouseDoorLatticeCR = 0xA4,
|
||||
HouseDoorLatticeDR = 0xA5,
|
||||
HouseDoorLatticeER = 0xA6,
|
||||
HouseDoorLatticeFR = 0xA7,
|
||||
HouseDoorLatticeGR = 0xA8,
|
||||
HouseDoorLatticeHR = 0xA9,
|
||||
HouseDoorCercleWindowBS = 0xAA,
|
||||
HouseDoorCercleWindowCS = 0xAB,
|
||||
HouseDoorCercleWindowDS = 0xAC,
|
||||
HouseDoorCercleWindowES = 0xAD,
|
||||
HouseDoorCercleWindowFS = 0xAE,
|
||||
HouseDoorCercleWindowGS = 0xAF,
|
||||
HouseDoorCercleWindowHS = 0xB0,
|
||||
HouseDoorCercleWindowBR = 0xB1,
|
||||
HouseDoorCercleWindowCR = 0xB2,
|
||||
HouseDoorCercleWindowDR = 0xB3,
|
||||
HouseDoorCercleWindowER = 0xB4,
|
||||
HouseDoorCercleWindowFR = 0xB5,
|
||||
HouseDoorCercleWindowGR = 0xB6,
|
||||
HouseDoorCercleWindowHR = 0xB7,
|
||||
HouseDoorJapaneseGS = 0xB8,
|
||||
HouseDoorJapaneseGR = 0xB9,
|
||||
HouseDoorSimplicityBS = 0xBA,
|
||||
HouseDoorSimplicityCS = 0xBB,
|
||||
HouseDoorSimplicityDS = 0xBC,
|
||||
HouseDoorSimplicityES = 0xBD,
|
||||
HouseDoorSimplicityFS = 0xBE,
|
||||
HouseDoorSimplicityGS = 0xBF,
|
||||
HouseDoorSimplicityHS = 0xC0,
|
||||
HouseDoorSimplicityBR = 0xC1,
|
||||
HouseDoorSimplicityCR = 0xC2,
|
||||
HouseDoorSimplicityDR = 0xC3,
|
||||
HouseDoorSimplicityER = 0xC4,
|
||||
HouseDoorSimplicityFR = 0xC5,
|
||||
HouseDoorSimplicityGR = 0xC6,
|
||||
HouseDoorSimplicityHR = 0xC7,
|
||||
HouseDoorChineseBS = 0xC8,
|
||||
HouseDoorChineseCS = 0xC9,
|
||||
HouseDoorChineseDS = 0xCA,
|
||||
HouseDoorChineseES = 0xCB,
|
||||
HouseDoorChineseFS = 0xCC,
|
||||
HouseDoorChineseGS = 0xCD,
|
||||
HouseDoorChineseHS = 0xCE,
|
||||
HouseDoorChineseBR = 0xCF,
|
||||
HouseDoorChineseCR = 0xD0,
|
||||
HouseDoorChineseDR = 0xD1,
|
||||
HouseDoorChineseER = 0xD2,
|
||||
HouseDoorChineseFR = 0xD3,
|
||||
HouseDoorChineseGR = 0xD4,
|
||||
HouseDoorChineseHR = 0xD5,
|
||||
HouseDoorWindowBS = 0xDD,
|
||||
HouseDoorWindowCS = 0xDE,
|
||||
HouseDoorWindowDS = 0xDF,
|
||||
HouseDoorWindowES = 0xE0,
|
||||
HouseDoorWindowFS = 0xE1,
|
||||
HouseDoorWindowGS = 0xE2,
|
||||
HouseDoorWindowHS = 0xE3,
|
||||
HouseDoorWindowBR = 0xE4,
|
||||
HouseDoorWindowCR = 0xE5,
|
||||
HouseDoorWindowDR = 0xE6,
|
||||
HouseDoorWindowER = 0xE7,
|
||||
HouseDoorWindowFR = 0xE8,
|
||||
HouseDoorWindowGR = 0xE9,
|
||||
HouseDoorWindowHR = 0xEA,
|
||||
HouseDoorIronBS = 0xEB,
|
||||
HouseDoorIronCS = 0xEC,
|
||||
HouseDoorIronDS = 0xED,
|
||||
HouseDoorIronES = 0xEE,
|
||||
HouseDoorIronFS = 0xEF,
|
||||
HouseDoorIronGS = 0xF0,
|
||||
HouseDoorIronHS = 0xF1,
|
||||
HouseDoorIronBR = 0xF2,
|
||||
HouseDoorIronCR = 0xF3,
|
||||
HouseDoorIronDR = 0xF4,
|
||||
HouseDoorIronER = 0xF5,
|
||||
HouseDoorIronFR = 0xF6,
|
||||
HouseDoorIronGR = 0xF7,
|
||||
HouseDoorIronHR = 0xF8,
|
||||
HouseDoorStandardBS = 0xF9,
|
||||
HouseDoorStandardCS = 0xFA,
|
||||
HouseDoorStandardDS = 0xFB,
|
||||
HouseDoorStandardES = 0xFC,
|
||||
HouseDoorStandardFS = 0xFD,
|
||||
HouseDoorStandardGS = 0xFE,
|
||||
HouseDoorStandardHS = 0xFF,
|
||||
HouseDoorStandardBR = 0x100,
|
||||
HouseDoorStandardCR = 0x101,
|
||||
HouseDoorStandardDR = 0x102,
|
||||
HouseDoorStandardER = 0x103,
|
||||
HouseDoorStandardFR = 0x104,
|
||||
HouseDoorStandardGR = 0x105,
|
||||
HouseDoorStandardHR = 0x106,
|
||||
HouseDoorReliefIS = 0x107,
|
||||
HouseDoorReliefIR = 0x108,
|
||||
HouseDoorReliefJS = 0x109,
|
||||
HouseDoorReliefJR = 0x10A,
|
||||
HouseDoorCarvingFS = 0x10B,
|
||||
HouseDoorCarvingFR = 0x10C,
|
||||
HouseDoorCercleWindowIS = 0x10D,
|
||||
HouseDoorCercleWindowIR = 0x10E,
|
||||
HouseDoorCercleWindowJS = 0x10F,
|
||||
HouseDoorCercleWindowJR = 0x110,
|
||||
HouseDoorVerticalWindowIS = 0x111,
|
||||
HouseDoorVerticalWindowIR = 0x112,
|
||||
HouseDoorVerticalWindowJS = 0x113,
|
||||
HouseDoorVerticalWindowJR = 0x114,
|
||||
HouseDoorWindowIS = 0x115,
|
||||
HouseDoorWindowIR = 0x116,
|
||||
HouseDoorWindowJS = 0x117,
|
||||
HouseDoorWindowJR = 0x118,
|
||||
HouseDoorStandardIS = 0x119,
|
||||
HouseDoorStandardIR = 0x11A,
|
||||
HouseDoorStandardJS = 0x11B,
|
||||
HouseDoorStandardJR = 0x11C,
|
||||
HouseDoorSimplicityIS = 0x11D,
|
||||
HouseDoorSimplicityIR = 0x11E,
|
||||
HouseDoorSimplicityJS = 0x11F,
|
||||
HouseDoorSimplicityJR = 0x120,
|
||||
HouseDoorIronGrillIS = 0x121,
|
||||
HouseDoorIronGrillIR = 0x122,
|
||||
HouseDoorIronGrillJS = 0x123,
|
||||
HouseDoorIronGrillJR = 0x124,
|
||||
}
|
||||
|
|
@ -1,175 +1,174 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Roof model for a house.
|
||||
/// </summary>
|
||||
public enum RoofType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Roof model for a house.
|
||||
/// </summary>
|
||||
public enum RoofType : ushort
|
||||
{
|
||||
HouseRoofPA04StandardA = 0x00,
|
||||
HouseRoofPA04StandardB = 0x03,
|
||||
_1 = 0x04,
|
||||
_2 = 0x05,
|
||||
HouseRoofPA04ThatchedA = 0x06,
|
||||
HouseRoofPA04StoneA = 0x07,
|
||||
HouseRoofPA04WesterntileA = 0x08,
|
||||
_3 = 0x09,
|
||||
HouseRoofPA00StandardA = 0x0A,
|
||||
HouseRoofPA00StandardB = 0x0B,
|
||||
HouseRoofPA00StandardC = 0x0C,
|
||||
HouseRoofPA00StandardD = 0x0D,
|
||||
HouseRoofPA00StandardE = 0x0E,
|
||||
HouseRoofPA00StandardF = 0x0F,
|
||||
HouseRoofPA00StandardG = 0x10,
|
||||
HouseRoofPA00StandardH = 0x11,
|
||||
_4 = 0x12,
|
||||
HouseRoofNASlateA = 0x13,
|
||||
HouseRoofNAMetalA = 0x14,
|
||||
HouseRoofNATileA = 0x15,
|
||||
HouseRoofPA04ThatchedB = 0x17,
|
||||
HouseRoofPA04ThatchedC = 0x18,
|
||||
HouseRoofPA04ThatchedD = 0x19,
|
||||
HouseRoofPA04ThatchedE = 0x1A,
|
||||
HouseRoofNBWoodtileA = 0x1B,
|
||||
HouseRoofNBThatchedA = 0x1C,
|
||||
HouseRoofNBWoodpanelA = 0x1D,
|
||||
HouseRoofNCJapaneseA = 0x1E,
|
||||
HouseRoofNCThatchedA = 0x1F,
|
||||
HouseRoofNCStoneweightA = 0x20,
|
||||
_5 = 0x21,
|
||||
_6 = 0x22,
|
||||
HouseRoofNDStoneA = 0x23,
|
||||
HouseRoofNDThatchedA = 0x24,
|
||||
HouseRoofNDWoodA = 0x25,
|
||||
HouseRoofPA01StandardA = 0x26,
|
||||
HouseRoofPA03StandardA = 0x27,
|
||||
HouseRoofNATileB = 0x28,
|
||||
HouseRoofNATileC = 0x29,
|
||||
HouseRoofNATileD = 0x2A,
|
||||
HouseRoofNATileE = 0x2B,
|
||||
HouseRoofNATileF = 0x2C,
|
||||
HouseRoofNATileG = 0x2D,
|
||||
HouseRoofNATileH = 0x2E,
|
||||
HouseRoofPA02StandardA = 0x2F,
|
||||
HouseRoofNAMetalB = 0x30,
|
||||
HouseRoofNAMetalC = 0x31,
|
||||
HouseRoofNAMetalD = 0x32,
|
||||
HouseRoofNAMetalE = 0x33,
|
||||
HouseRoofNAMetalF = 0x34,
|
||||
HouseRoofNAMetalG = 0x35,
|
||||
HouseRoofNAMetalH = 0x36,
|
||||
HouseRoofNASlateB = 0x37,
|
||||
HouseRoofNASlateC = 0x38,
|
||||
HouseRoofNASlateD = 0x39,
|
||||
HouseRoofNASlateE = 0x3A,
|
||||
HouseRoofNASlateF = 0x3B,
|
||||
HouseRoofNASlateG = 0x3C,
|
||||
HouseRoofNASlateH = 0x3D,
|
||||
HouseRoofNBWoodtileB = 0x3E,
|
||||
HouseRoofNBWoodtileC = 0x3F,
|
||||
HouseRoofNBWoodtileD = 0x40,
|
||||
HouseRoofNBWoodtileE = 0x41,
|
||||
HouseRoofNBWoodtileF = 0x42,
|
||||
HouseRoofNBWoodtileG = 0x43,
|
||||
HouseRoofNBWoodtileH = 0x44,
|
||||
HouseRoofNBWoodpanelB = 0x45,
|
||||
HouseRoofNBWoodpanelC = 0x46,
|
||||
HouseRoofNBWoodpanelD = 0x47,
|
||||
HouseRoofNBWoodpanelE = 0x48,
|
||||
HouseRoofNBWoodpanelF = 0x49,
|
||||
HouseRoofNBWoodpanelG = 0x4A,
|
||||
HouseRoofNBWoodpanelH = 0x4B,
|
||||
HouseRoofPA01StandardB = 0x4C,
|
||||
HouseRoofPA01StandardC = 0x4D,
|
||||
HouseRoofPA01StandardD = 0x4E,
|
||||
HouseRoofPA01StandardE = 0x4F,
|
||||
HouseRoofPA01StandardF = 0x50,
|
||||
HouseRoofPA01StandardG = 0x51,
|
||||
HouseRoofPA01StandardH = 0x52,
|
||||
HouseRoofNBThatchedB = 0x53,
|
||||
HouseRoofNBThatchedC = 0x54,
|
||||
HouseRoofNBThatchedD = 0x55,
|
||||
HouseRoofNBThatchedE = 0x56,
|
||||
HouseRoofNBThatchedF = 0x57,
|
||||
HouseRoofNBThatchedG = 0x58,
|
||||
HouseRoofNBThatchedH = 0x59,
|
||||
HouseRoofNCJapaneseB = 0x5A,
|
||||
HouseRoofNCJapaneseC = 0x5B,
|
||||
HouseRoofNCJapaneseD = 0x5C,
|
||||
HouseRoofNCJapaneseE = 0x5D,
|
||||
HouseRoofNCJapaneseF = 0x5E,
|
||||
HouseRoofNCJapaneseG = 0x5F,
|
||||
HouseRoofNCJapaneseH = 0x60,
|
||||
HouseRoofNCStoneweightB = 0x61,
|
||||
HouseRoofNCStoneweightC = 0x62,
|
||||
HouseRoofNCStoneweightD = 0x63,
|
||||
HouseRoofNCStoneweightE = 0x64,
|
||||
HouseRoofNCStoneweightF = 0x65,
|
||||
HouseRoofNDWoodB = 0x66,
|
||||
HouseRoofNDWoodC = 0x67,
|
||||
HouseRoofNDWoodD = 0x68,
|
||||
HouseRoofNDWoodE = 0x69,
|
||||
HouseRoofNDWoodF = 0x6A,
|
||||
HouseRoofNDWoodG = 0x6B,
|
||||
HouseRoofNDWoodH = 0x6C,
|
||||
HouseRoofNDStoneB = 0x6D,
|
||||
HouseRoofNDStoneC = 0x6E,
|
||||
HouseRoofNDStoneD = 0x6F,
|
||||
HouseRoofNDStoneE = 0x70,
|
||||
HouseRoofNDStoneF = 0x71,
|
||||
HouseRoofNDStoneG = 0x72,
|
||||
HouseRoofNDStoneH = 0x73,
|
||||
HouseRoofNCThatchedB = 0x74,
|
||||
HouseRoofNCThatchedC = 0x75,
|
||||
HouseRoofNCThatchedD = 0x76,
|
||||
HouseRoofNCThatchedE = 0x77,
|
||||
HouseRoofNCThatchedF = 0x78,
|
||||
HouseRoofNDThatchedB = 0x79,
|
||||
HouseRoofNDThatchedC = 0x7A,
|
||||
HouseRoofNDThatchedD = 0x7B,
|
||||
HouseRoofNDThatchedE = 0x7C,
|
||||
HouseRoofNDThatchedF = 0x7D,
|
||||
HouseRoofPA02StandardB = 0x7E,
|
||||
HouseRoofPA02StandardC = 0x7F,
|
||||
HouseRoofPA02StandardD = 0x80,
|
||||
HouseRoofPA02StandardE = 0x81,
|
||||
HouseRoofPA02StandardF = 0x82,
|
||||
HouseRoofPA02StandardG = 0x83,
|
||||
HouseRoofPA02StandardH = 0x84,
|
||||
HouseRoofPA03StandardB = 0x85,
|
||||
HouseRoofPA03StandardC = 0x86,
|
||||
HouseRoofPA03StandardD = 0x87,
|
||||
HouseRoofPA03StandardE = 0x88,
|
||||
HouseRoofPA03StandardF = 0x89,
|
||||
HouseRoofPA03StandardG = 0x8A,
|
||||
HouseRoofPA03StandardH = 0x8B,
|
||||
HouseRoofPA04StandardC = 0x93,
|
||||
HouseRoofPA04StandardD = 0x94,
|
||||
HouseRoofPA04StandardE = 0x95,
|
||||
HouseRoofPA04StandardF = 0x96,
|
||||
HouseRoofPA04StandardG = 0x97,
|
||||
HouseRoofPA04StandardH = 0x98,
|
||||
HouseRoofPA04StoneB = 0x99,
|
||||
HouseRoofPA04StoneC = 0x9A,
|
||||
HouseRoofPA04StoneD = 0x9B,
|
||||
HouseRoofPA04StoneE = 0x9C,
|
||||
HouseRoofPA04StoneF = 0x9D,
|
||||
HouseRoofPA04StoneG = 0x9E,
|
||||
HouseRoofPA04StoneH = 0x9F,
|
||||
HouseRoofPA04WesterntileB = 0xA0,
|
||||
HouseRoofPA04WesterntileC = 0xA1,
|
||||
HouseRoofPA04WesterntileD = 0xA2,
|
||||
HouseRoofPA04WesterntileE = 0xA3,
|
||||
HouseRoofPA04WesterntileF = 0xA4,
|
||||
HouseRoofPA04WesterntileG = 0xA5,
|
||||
HouseRoofPA04WesterntileH = 0xA6,
|
||||
HouseRoofPA04ThatchedF = 0xA7,
|
||||
HouseRoofPA04ThatchedG = 0xA8,
|
||||
HouseRoofPA04ThatchedH = 0xA9,
|
||||
HouseRoofNAMetalI = 0xAA,
|
||||
HouseRoofNBWoodtileI = 0xAB,
|
||||
HouseRoofNBWoodtileJ = 0xAC,
|
||||
HouseRoofNASlateI = 0xAD,
|
||||
HouseRoofNDWoodI = 0xAE,
|
||||
HouseRoofNDWoodJ = 0xAF,
|
||||
}
|
||||
}
|
||||
HouseRoofPA04StandardA = 0x00,
|
||||
HouseRoofPA04StandardB = 0x03,
|
||||
_1 = 0x04,
|
||||
_2 = 0x05,
|
||||
HouseRoofPA04ThatchedA = 0x06,
|
||||
HouseRoofPA04StoneA = 0x07,
|
||||
HouseRoofPA04WesterntileA = 0x08,
|
||||
_3 = 0x09,
|
||||
HouseRoofPA00StandardA = 0x0A,
|
||||
HouseRoofPA00StandardB = 0x0B,
|
||||
HouseRoofPA00StandardC = 0x0C,
|
||||
HouseRoofPA00StandardD = 0x0D,
|
||||
HouseRoofPA00StandardE = 0x0E,
|
||||
HouseRoofPA00StandardF = 0x0F,
|
||||
HouseRoofPA00StandardG = 0x10,
|
||||
HouseRoofPA00StandardH = 0x11,
|
||||
_4 = 0x12,
|
||||
HouseRoofNASlateA = 0x13,
|
||||
HouseRoofNAMetalA = 0x14,
|
||||
HouseRoofNATileA = 0x15,
|
||||
HouseRoofPA04ThatchedB = 0x17,
|
||||
HouseRoofPA04ThatchedC = 0x18,
|
||||
HouseRoofPA04ThatchedD = 0x19,
|
||||
HouseRoofPA04ThatchedE = 0x1A,
|
||||
HouseRoofNBWoodtileA = 0x1B,
|
||||
HouseRoofNBThatchedA = 0x1C,
|
||||
HouseRoofNBWoodpanelA = 0x1D,
|
||||
HouseRoofNCJapaneseA = 0x1E,
|
||||
HouseRoofNCThatchedA = 0x1F,
|
||||
HouseRoofNCStoneweightA = 0x20,
|
||||
_5 = 0x21,
|
||||
_6 = 0x22,
|
||||
HouseRoofNDStoneA = 0x23,
|
||||
HouseRoofNDThatchedA = 0x24,
|
||||
HouseRoofNDWoodA = 0x25,
|
||||
HouseRoofPA01StandardA = 0x26,
|
||||
HouseRoofPA03StandardA = 0x27,
|
||||
HouseRoofNATileB = 0x28,
|
||||
HouseRoofNATileC = 0x29,
|
||||
HouseRoofNATileD = 0x2A,
|
||||
HouseRoofNATileE = 0x2B,
|
||||
HouseRoofNATileF = 0x2C,
|
||||
HouseRoofNATileG = 0x2D,
|
||||
HouseRoofNATileH = 0x2E,
|
||||
HouseRoofPA02StandardA = 0x2F,
|
||||
HouseRoofNAMetalB = 0x30,
|
||||
HouseRoofNAMetalC = 0x31,
|
||||
HouseRoofNAMetalD = 0x32,
|
||||
HouseRoofNAMetalE = 0x33,
|
||||
HouseRoofNAMetalF = 0x34,
|
||||
HouseRoofNAMetalG = 0x35,
|
||||
HouseRoofNAMetalH = 0x36,
|
||||
HouseRoofNASlateB = 0x37,
|
||||
HouseRoofNASlateC = 0x38,
|
||||
HouseRoofNASlateD = 0x39,
|
||||
HouseRoofNASlateE = 0x3A,
|
||||
HouseRoofNASlateF = 0x3B,
|
||||
HouseRoofNASlateG = 0x3C,
|
||||
HouseRoofNASlateH = 0x3D,
|
||||
HouseRoofNBWoodtileB = 0x3E,
|
||||
HouseRoofNBWoodtileC = 0x3F,
|
||||
HouseRoofNBWoodtileD = 0x40,
|
||||
HouseRoofNBWoodtileE = 0x41,
|
||||
HouseRoofNBWoodtileF = 0x42,
|
||||
HouseRoofNBWoodtileG = 0x43,
|
||||
HouseRoofNBWoodtileH = 0x44,
|
||||
HouseRoofNBWoodpanelB = 0x45,
|
||||
HouseRoofNBWoodpanelC = 0x46,
|
||||
HouseRoofNBWoodpanelD = 0x47,
|
||||
HouseRoofNBWoodpanelE = 0x48,
|
||||
HouseRoofNBWoodpanelF = 0x49,
|
||||
HouseRoofNBWoodpanelG = 0x4A,
|
||||
HouseRoofNBWoodpanelH = 0x4B,
|
||||
HouseRoofPA01StandardB = 0x4C,
|
||||
HouseRoofPA01StandardC = 0x4D,
|
||||
HouseRoofPA01StandardD = 0x4E,
|
||||
HouseRoofPA01StandardE = 0x4F,
|
||||
HouseRoofPA01StandardF = 0x50,
|
||||
HouseRoofPA01StandardG = 0x51,
|
||||
HouseRoofPA01StandardH = 0x52,
|
||||
HouseRoofNBThatchedB = 0x53,
|
||||
HouseRoofNBThatchedC = 0x54,
|
||||
HouseRoofNBThatchedD = 0x55,
|
||||
HouseRoofNBThatchedE = 0x56,
|
||||
HouseRoofNBThatchedF = 0x57,
|
||||
HouseRoofNBThatchedG = 0x58,
|
||||
HouseRoofNBThatchedH = 0x59,
|
||||
HouseRoofNCJapaneseB = 0x5A,
|
||||
HouseRoofNCJapaneseC = 0x5B,
|
||||
HouseRoofNCJapaneseD = 0x5C,
|
||||
HouseRoofNCJapaneseE = 0x5D,
|
||||
HouseRoofNCJapaneseF = 0x5E,
|
||||
HouseRoofNCJapaneseG = 0x5F,
|
||||
HouseRoofNCJapaneseH = 0x60,
|
||||
HouseRoofNCStoneweightB = 0x61,
|
||||
HouseRoofNCStoneweightC = 0x62,
|
||||
HouseRoofNCStoneweightD = 0x63,
|
||||
HouseRoofNCStoneweightE = 0x64,
|
||||
HouseRoofNCStoneweightF = 0x65,
|
||||
HouseRoofNDWoodB = 0x66,
|
||||
HouseRoofNDWoodC = 0x67,
|
||||
HouseRoofNDWoodD = 0x68,
|
||||
HouseRoofNDWoodE = 0x69,
|
||||
HouseRoofNDWoodF = 0x6A,
|
||||
HouseRoofNDWoodG = 0x6B,
|
||||
HouseRoofNDWoodH = 0x6C,
|
||||
HouseRoofNDStoneB = 0x6D,
|
||||
HouseRoofNDStoneC = 0x6E,
|
||||
HouseRoofNDStoneD = 0x6F,
|
||||
HouseRoofNDStoneE = 0x70,
|
||||
HouseRoofNDStoneF = 0x71,
|
||||
HouseRoofNDStoneG = 0x72,
|
||||
HouseRoofNDStoneH = 0x73,
|
||||
HouseRoofNCThatchedB = 0x74,
|
||||
HouseRoofNCThatchedC = 0x75,
|
||||
HouseRoofNCThatchedD = 0x76,
|
||||
HouseRoofNCThatchedE = 0x77,
|
||||
HouseRoofNCThatchedF = 0x78,
|
||||
HouseRoofNDThatchedB = 0x79,
|
||||
HouseRoofNDThatchedC = 0x7A,
|
||||
HouseRoofNDThatchedD = 0x7B,
|
||||
HouseRoofNDThatchedE = 0x7C,
|
||||
HouseRoofNDThatchedF = 0x7D,
|
||||
HouseRoofPA02StandardB = 0x7E,
|
||||
HouseRoofPA02StandardC = 0x7F,
|
||||
HouseRoofPA02StandardD = 0x80,
|
||||
HouseRoofPA02StandardE = 0x81,
|
||||
HouseRoofPA02StandardF = 0x82,
|
||||
HouseRoofPA02StandardG = 0x83,
|
||||
HouseRoofPA02StandardH = 0x84,
|
||||
HouseRoofPA03StandardB = 0x85,
|
||||
HouseRoofPA03StandardC = 0x86,
|
||||
HouseRoofPA03StandardD = 0x87,
|
||||
HouseRoofPA03StandardE = 0x88,
|
||||
HouseRoofPA03StandardF = 0x89,
|
||||
HouseRoofPA03StandardG = 0x8A,
|
||||
HouseRoofPA03StandardH = 0x8B,
|
||||
HouseRoofPA04StandardC = 0x93,
|
||||
HouseRoofPA04StandardD = 0x94,
|
||||
HouseRoofPA04StandardE = 0x95,
|
||||
HouseRoofPA04StandardF = 0x96,
|
||||
HouseRoofPA04StandardG = 0x97,
|
||||
HouseRoofPA04StandardH = 0x98,
|
||||
HouseRoofPA04StoneB = 0x99,
|
||||
HouseRoofPA04StoneC = 0x9A,
|
||||
HouseRoofPA04StoneD = 0x9B,
|
||||
HouseRoofPA04StoneE = 0x9C,
|
||||
HouseRoofPA04StoneF = 0x9D,
|
||||
HouseRoofPA04StoneG = 0x9E,
|
||||
HouseRoofPA04StoneH = 0x9F,
|
||||
HouseRoofPA04WesterntileB = 0xA0,
|
||||
HouseRoofPA04WesterntileC = 0xA1,
|
||||
HouseRoofPA04WesterntileD = 0xA2,
|
||||
HouseRoofPA04WesterntileE = 0xA3,
|
||||
HouseRoofPA04WesterntileF = 0xA4,
|
||||
HouseRoofPA04WesterntileG = 0xA5,
|
||||
HouseRoofPA04WesterntileH = 0xA6,
|
||||
HouseRoofPA04ThatchedF = 0xA7,
|
||||
HouseRoofPA04ThatchedG = 0xA8,
|
||||
HouseRoofPA04ThatchedH = 0xA9,
|
||||
HouseRoofNAMetalI = 0xAA,
|
||||
HouseRoofNBWoodtileI = 0xAB,
|
||||
HouseRoofNBWoodtileJ = 0xAC,
|
||||
HouseRoofNASlateI = 0xAD,
|
||||
HouseRoofNDWoodI = 0xAE,
|
||||
HouseRoofNDWoodJ = 0xAF,
|
||||
}
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Incline / Slope model
|
||||
/// </summary>
|
||||
public enum SlopeType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Incline / Slope model
|
||||
/// </summary>
|
||||
public enum SlopeType : ushort
|
||||
{
|
||||
SlopeStoneStair = 0x00,
|
||||
SlopeIronStair = 0x01,
|
||||
SlopeWood = 0x02,
|
||||
SlopeWoodStair = 0x03,
|
||||
SlopeBrickStair = 0x04,
|
||||
SlopeReserved = 0x05,
|
||||
SlopeNatural = 0x1D,
|
||||
SlopeWoodBlue = 0x1E,
|
||||
SlopeIronStairBlue = 0x1F,
|
||||
}
|
||||
}
|
||||
SlopeStoneStair = 0x00,
|
||||
SlopeIronStair = 0x01,
|
||||
SlopeWood = 0x02,
|
||||
SlopeWoodStair = 0x03,
|
||||
SlopeBrickStair = 0x04,
|
||||
SlopeReserved = 0x05,
|
||||
SlopeNatural = 0x1D,
|
||||
SlopeWoodBlue = 0x1E,
|
||||
SlopeIronStairBlue = 0x1F,
|
||||
}
|
||||
|
|
@ -1,33 +1,32 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Ambient sound for a house.
|
||||
/// </summary>
|
||||
public enum SoundAmbientKind : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Ambient sound for a house.
|
||||
/// </summary>
|
||||
public enum SoundAmbientKind : byte
|
||||
{
|
||||
Silence = 0x0,
|
||||
Rain = 0x1,
|
||||
Sea = 0x2,
|
||||
InWater = 0x3,
|
||||
Wind = 0x4,
|
||||
Plateau = 0x5,
|
||||
Jungle = 0x6,
|
||||
Crowd = 0x7,
|
||||
Cheers = 0x8,
|
||||
City = 0x9,
|
||||
Train = 0xA,
|
||||
Construction = 0xB,
|
||||
Space = 0xC,
|
||||
Echo = 0xD,
|
||||
Storm = 0xE,
|
||||
Cave = 0x10,
|
||||
Earthquake = 0x11,
|
||||
Squeak = 0x12,
|
||||
Country = 0x14,
|
||||
Factory = 0x15,
|
||||
Cyber = 0x1A,
|
||||
Healing = 0x1B,
|
||||
Forest = 0x1C,
|
||||
Duct = 0x1D,
|
||||
}
|
||||
}
|
||||
Silence = 0x0,
|
||||
Rain = 0x1,
|
||||
Sea = 0x2,
|
||||
InWater = 0x3,
|
||||
Wind = 0x4,
|
||||
Plateau = 0x5,
|
||||
Jungle = 0x6,
|
||||
Crowd = 0x7,
|
||||
Cheers = 0x8,
|
||||
City = 0x9,
|
||||
Train = 0xA,
|
||||
Construction = 0xB,
|
||||
Space = 0xC,
|
||||
Echo = 0xD,
|
||||
Storm = 0xE,
|
||||
Cave = 0x10,
|
||||
Earthquake = 0x11,
|
||||
Squeak = 0x12,
|
||||
Country = 0x14,
|
||||
Factory = 0x15,
|
||||
Cyber = 0x1A,
|
||||
Healing = 0x1B,
|
||||
Forest = 0x1C,
|
||||
Duct = 0x1D,
|
||||
}
|
||||
|
|
@ -1,28 +1,27 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves value metadata for customizing structures.
|
||||
/// </summary>
|
||||
public static class StructureUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves value metadata for customizing structures.
|
||||
/// </summary>
|
||||
public static class StructureUtil
|
||||
public static Dictionary<string, string[]> GetStructureHelpList()
|
||||
{
|
||||
public static Dictionary<string, string[]> GetStructureHelpList()
|
||||
var kvpa = new[]
|
||||
{
|
||||
var kvpa = new[]
|
||||
{
|
||||
EnumUtil.GetEnumList<BuildingType>(),
|
||||
EnumUtil.GetEnumList<BuildingType>(),
|
||||
|
||||
EnumUtil.GetEnumList<DoorKind>(),
|
||||
EnumUtil.GetEnumList<RoofType>(),
|
||||
EnumUtil.GetEnumList<WallType>(),
|
||||
EnumUtil.GetEnumList<DoorKind>(),
|
||||
EnumUtil.GetEnumList<RoofType>(),
|
||||
EnumUtil.GetEnumList<WallType>(),
|
||||
|
||||
EnumUtil.GetEnumList<BridgeType>(),
|
||||
EnumUtil.GetEnumList<BridgeMaterial>(),
|
||||
EnumUtil.GetEnumList<SlopeType>(),
|
||||
};
|
||||
return kvpa.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
EnumUtil.GetEnumList<BridgeType>(),
|
||||
EnumUtil.GetEnumList<BridgeMaterial>(),
|
||||
EnumUtil.GetEnumList<SlopeType>(),
|
||||
};
|
||||
return kvpa.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,158 +1,157 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Wall model for a house.
|
||||
/// </summary>
|
||||
public enum WallType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Wall model for a house.
|
||||
/// </summary>
|
||||
public enum WallType : ushort
|
||||
{
|
||||
HouseWallPA04StandardA = 0x00,
|
||||
HouseWallPA04StandardB = 0x05,
|
||||
HouseWallPA04StandardC = 0x06,
|
||||
HouseTentPA = 0x07,
|
||||
HouseTentNA = 0x08,
|
||||
HouseWallPA04WoodframeA = 0x09,
|
||||
HouseWallPA04StoneA = 0x0A,
|
||||
HouseWallNForSale = 0x0B,
|
||||
HouseWallNSoldOut = 0x0C,
|
||||
HouseWallPA00StandardA = 0x0D,
|
||||
HouseWallNAWoodsidingA = 0x0E,
|
||||
HouseWallNABrickA = 0x0F,
|
||||
HouseWallNAStuccoA = 0x10,
|
||||
HouseWallPA04StuccoA = 0x13,
|
||||
HouseWallPA04WoodframeB = 0x14,
|
||||
HouseWallPA04WoodframeC = 0x15,
|
||||
HouseWallPA04WoodframeD = 0x16,
|
||||
HouseWallPA04WoodframeE = 0x17,
|
||||
HouseWallNBStuccoA = 0x18,
|
||||
HouseWallNBLogA = 0x19,
|
||||
HouseWallNBStoneA = 0x1A,
|
||||
HouseWallNCJapaneseA = 0x1B,
|
||||
HouseWallNCOrientalA = 0x1C,
|
||||
HouseWallNCWoodA = 0x1D,
|
||||
HouseWallHouseMovingPA = 0x1E,
|
||||
HouseWallHouseMovingNA = 0x1F,
|
||||
HouseWallNDStuccoA = 0x20,
|
||||
HouseWallNDSoilA = 0x21,
|
||||
HouseWallNDWoodA = 0x22,
|
||||
HouseWallPA01StandardA = 0x23,
|
||||
HouseTentNB = 0x24,
|
||||
HouseWallPA03StandardA = 0x25,
|
||||
HouseWallNAStuccoB = 0x2A,
|
||||
HouseWallNAStuccoC = 0x2B,
|
||||
HouseWallNAStuccoD = 0x2C,
|
||||
HouseWallNAStuccoE = 0x2D,
|
||||
HouseWallNAStuccoF = 0x2E,
|
||||
HouseWallNAStuccoG = 0x2F,
|
||||
HouseWallNAStuccoH = 0x30,
|
||||
HouseWallNABrickB = 0x39,
|
||||
HouseWallNABrickC = 0x3A,
|
||||
HouseWallNABrickD = 0x3B,
|
||||
HouseWallNABrickE = 0x3C,
|
||||
HouseWallNABrickF = 0x3D,
|
||||
HouseWallNABrickG = 0x3E,
|
||||
HouseWallNABrickH = 0x3F,
|
||||
HouseWallPA02StandardA = 0x40,
|
||||
HouseWallNAWoodsidingB = 0x41,
|
||||
HouseWallNAWoodsidingC = 0x42,
|
||||
HouseWallNAWoodsidingD = 0x43,
|
||||
HouseWallNAWoodsidingE = 0x44,
|
||||
HouseWallNAWoodsidingF = 0x45,
|
||||
HouseWallNAWoodsidingG = 0x46,
|
||||
HouseWallNAWoodsidingH = 0x47,
|
||||
HouseWallNBStuccoB = 0x48,
|
||||
HouseWallNBStuccoC = 0x49,
|
||||
HouseWallNBStuccoD = 0x4A,
|
||||
HouseWallNBStuccoE = 0x4B,
|
||||
HouseWallNBStuccoF = 0x4C,
|
||||
HouseWallNBStuccoG = 0x4D,
|
||||
HouseWallNBStuccoH = 0x4E,
|
||||
HouseWallNBStoneB = 0x4F,
|
||||
HouseWallNBStoneC = 0x50,
|
||||
HouseWallNBStoneD = 0x51,
|
||||
HouseWallNBStoneE = 0x52,
|
||||
HouseWallNBStoneF = 0x53,
|
||||
HouseWallNBStoneG = 0x54,
|
||||
HouseWallNBStoneH = 0x55,
|
||||
HouseWallNBLogB = 0x56,
|
||||
HouseWallNBLogC = 0x57,
|
||||
HouseWallNBLogD = 0x58,
|
||||
HouseWallNBLogE = 0x59,
|
||||
HouseWallNBLogF = 0x5A,
|
||||
HouseWallNBLogG = 0x5B,
|
||||
HouseWallNBLogH = 0x5C,
|
||||
HouseWallNCOrientalB = 0x64,
|
||||
HouseWallNCOrientalC = 0x65,
|
||||
HouseWallNCOrientalD = 0x66,
|
||||
HouseWallNCOrientalE = 0x67,
|
||||
HouseWallNCOrientalF = 0x68,
|
||||
HouseWallNCOrientalG = 0x69,
|
||||
HouseWallNCOrientalH = 0x6A,
|
||||
HouseWallNCJapaneseB = 0x6B,
|
||||
HouseWallNCJapaneseC = 0x6C,
|
||||
HouseWallNCJapaneseD = 0x6D,
|
||||
HouseWallNCJapaneseE = 0x6E,
|
||||
HouseWallNCJapaneseF = 0x6F,
|
||||
HouseWallNCJapaneseG = 0x70,
|
||||
HouseWallNCJapaneseH = 0x71,
|
||||
HouseWallNCWoodB = 0x72,
|
||||
HouseWallNCWoodC = 0x73,
|
||||
HouseWallNCWoodD = 0x74,
|
||||
HouseWallNCWoodE = 0x75,
|
||||
HouseWallNCWoodF = 0x76,
|
||||
HouseWallNCWoodG = 0x77,
|
||||
HouseWallNCWoodH = 0x78,
|
||||
HouseWallNDSoilB = 0x79,
|
||||
HouseWallNDSoilC = 0x7A,
|
||||
HouseWallNDSoilD = 0x7B,
|
||||
HouseWallNDSoilE = 0x7C,
|
||||
HouseWallNDSoilF = 0x7D,
|
||||
HouseWallNDSoilG = 0x7E,
|
||||
HouseWallNDSoilH = 0x7F,
|
||||
HouseWallNDStuccoB = 0x80,
|
||||
HouseWallNDStuccoC = 0x81,
|
||||
HouseWallNDStuccoD = 0x82,
|
||||
HouseWallNDStuccoE = 0x83,
|
||||
HouseWallNDStuccoF = 0x84,
|
||||
HouseWallNDStuccoG = 0x85,
|
||||
HouseWallNDStuccoH = 0x86,
|
||||
HouseWallNDWoodB = 0x87,
|
||||
HouseWallNDWoodC = 0x88,
|
||||
HouseWallNDWoodD = 0x89,
|
||||
HouseWallNDWoodE = 0x8A,
|
||||
HouseWallNDWoodF = 0x8B,
|
||||
HouseWallNDWoodG = 0x8C,
|
||||
HouseWallNDWoodH = 0x8D,
|
||||
HouseWallPA04WoodframeF = 0x8E,
|
||||
HouseWallPA04WoodframeG = 0x8F,
|
||||
HouseWallPA04WoodframeH = 0x90,
|
||||
HouseWallPA04StoneB = 0x91,
|
||||
HouseWallPA04StoneC = 0x92,
|
||||
HouseWallPA04StoneD = 0x93,
|
||||
HouseWallPA04StoneE = 0x94,
|
||||
HouseWallPA04StoneF = 0x95,
|
||||
HouseWallPA04StoneG = 0x96,
|
||||
HouseWallPA04StoneH = 0x97,
|
||||
HouseWallPA04StuccoB = 0x98,
|
||||
HouseWallPA04StuccoC = 0x99,
|
||||
HouseWallPA04StuccoD = 0x9A,
|
||||
HouseWallPA04StuccoE = 0x9B,
|
||||
HouseWallPA04StuccoF = 0x9C,
|
||||
HouseWallPA04StuccoG = 0x9D,
|
||||
HouseWallPA04StuccoH = 0x9E,
|
||||
HouseWallPA04StandardD = 0x9F,
|
||||
HouseWallPA04StandardE = 0xA0,
|
||||
HouseWallPA04StandardF = 0xA1,
|
||||
HouseWallPA04StandardG = 0xA2,
|
||||
HouseWallPA04StandardH = 0xA3,
|
||||
HouseWallNAMetalA = 0xA4,
|
||||
HouseWallNAMetalB = 0xA5,
|
||||
HouseWallNAMetalC = 0xA6,
|
||||
HouseWallNAMetalD = 0xA7,
|
||||
HouseWallNAMetalE = 0xA8,
|
||||
HouseWallNAMetalF = 0xA9,
|
||||
HouseWallNAMetalG = 0xAA,
|
||||
HouseWallNAMetalH = 0xAB,
|
||||
HouseWallNAWoodsidingI = 0xAC,
|
||||
HouseWallNAWoodsidingJ = 0xAD,
|
||||
}
|
||||
}
|
||||
HouseWallPA04StandardA = 0x00,
|
||||
HouseWallPA04StandardB = 0x05,
|
||||
HouseWallPA04StandardC = 0x06,
|
||||
HouseTentPA = 0x07,
|
||||
HouseTentNA = 0x08,
|
||||
HouseWallPA04WoodframeA = 0x09,
|
||||
HouseWallPA04StoneA = 0x0A,
|
||||
HouseWallNForSale = 0x0B,
|
||||
HouseWallNSoldOut = 0x0C,
|
||||
HouseWallPA00StandardA = 0x0D,
|
||||
HouseWallNAWoodsidingA = 0x0E,
|
||||
HouseWallNABrickA = 0x0F,
|
||||
HouseWallNAStuccoA = 0x10,
|
||||
HouseWallPA04StuccoA = 0x13,
|
||||
HouseWallPA04WoodframeB = 0x14,
|
||||
HouseWallPA04WoodframeC = 0x15,
|
||||
HouseWallPA04WoodframeD = 0x16,
|
||||
HouseWallPA04WoodframeE = 0x17,
|
||||
HouseWallNBStuccoA = 0x18,
|
||||
HouseWallNBLogA = 0x19,
|
||||
HouseWallNBStoneA = 0x1A,
|
||||
HouseWallNCJapaneseA = 0x1B,
|
||||
HouseWallNCOrientalA = 0x1C,
|
||||
HouseWallNCWoodA = 0x1D,
|
||||
HouseWallHouseMovingPA = 0x1E,
|
||||
HouseWallHouseMovingNA = 0x1F,
|
||||
HouseWallNDStuccoA = 0x20,
|
||||
HouseWallNDSoilA = 0x21,
|
||||
HouseWallNDWoodA = 0x22,
|
||||
HouseWallPA01StandardA = 0x23,
|
||||
HouseTentNB = 0x24,
|
||||
HouseWallPA03StandardA = 0x25,
|
||||
HouseWallNAStuccoB = 0x2A,
|
||||
HouseWallNAStuccoC = 0x2B,
|
||||
HouseWallNAStuccoD = 0x2C,
|
||||
HouseWallNAStuccoE = 0x2D,
|
||||
HouseWallNAStuccoF = 0x2E,
|
||||
HouseWallNAStuccoG = 0x2F,
|
||||
HouseWallNAStuccoH = 0x30,
|
||||
HouseWallNABrickB = 0x39,
|
||||
HouseWallNABrickC = 0x3A,
|
||||
HouseWallNABrickD = 0x3B,
|
||||
HouseWallNABrickE = 0x3C,
|
||||
HouseWallNABrickF = 0x3D,
|
||||
HouseWallNABrickG = 0x3E,
|
||||
HouseWallNABrickH = 0x3F,
|
||||
HouseWallPA02StandardA = 0x40,
|
||||
HouseWallNAWoodsidingB = 0x41,
|
||||
HouseWallNAWoodsidingC = 0x42,
|
||||
HouseWallNAWoodsidingD = 0x43,
|
||||
HouseWallNAWoodsidingE = 0x44,
|
||||
HouseWallNAWoodsidingF = 0x45,
|
||||
HouseWallNAWoodsidingG = 0x46,
|
||||
HouseWallNAWoodsidingH = 0x47,
|
||||
HouseWallNBStuccoB = 0x48,
|
||||
HouseWallNBStuccoC = 0x49,
|
||||
HouseWallNBStuccoD = 0x4A,
|
||||
HouseWallNBStuccoE = 0x4B,
|
||||
HouseWallNBStuccoF = 0x4C,
|
||||
HouseWallNBStuccoG = 0x4D,
|
||||
HouseWallNBStuccoH = 0x4E,
|
||||
HouseWallNBStoneB = 0x4F,
|
||||
HouseWallNBStoneC = 0x50,
|
||||
HouseWallNBStoneD = 0x51,
|
||||
HouseWallNBStoneE = 0x52,
|
||||
HouseWallNBStoneF = 0x53,
|
||||
HouseWallNBStoneG = 0x54,
|
||||
HouseWallNBStoneH = 0x55,
|
||||
HouseWallNBLogB = 0x56,
|
||||
HouseWallNBLogC = 0x57,
|
||||
HouseWallNBLogD = 0x58,
|
||||
HouseWallNBLogE = 0x59,
|
||||
HouseWallNBLogF = 0x5A,
|
||||
HouseWallNBLogG = 0x5B,
|
||||
HouseWallNBLogH = 0x5C,
|
||||
HouseWallNCOrientalB = 0x64,
|
||||
HouseWallNCOrientalC = 0x65,
|
||||
HouseWallNCOrientalD = 0x66,
|
||||
HouseWallNCOrientalE = 0x67,
|
||||
HouseWallNCOrientalF = 0x68,
|
||||
HouseWallNCOrientalG = 0x69,
|
||||
HouseWallNCOrientalH = 0x6A,
|
||||
HouseWallNCJapaneseB = 0x6B,
|
||||
HouseWallNCJapaneseC = 0x6C,
|
||||
HouseWallNCJapaneseD = 0x6D,
|
||||
HouseWallNCJapaneseE = 0x6E,
|
||||
HouseWallNCJapaneseF = 0x6F,
|
||||
HouseWallNCJapaneseG = 0x70,
|
||||
HouseWallNCJapaneseH = 0x71,
|
||||
HouseWallNCWoodB = 0x72,
|
||||
HouseWallNCWoodC = 0x73,
|
||||
HouseWallNCWoodD = 0x74,
|
||||
HouseWallNCWoodE = 0x75,
|
||||
HouseWallNCWoodF = 0x76,
|
||||
HouseWallNCWoodG = 0x77,
|
||||
HouseWallNCWoodH = 0x78,
|
||||
HouseWallNDSoilB = 0x79,
|
||||
HouseWallNDSoilC = 0x7A,
|
||||
HouseWallNDSoilD = 0x7B,
|
||||
HouseWallNDSoilE = 0x7C,
|
||||
HouseWallNDSoilF = 0x7D,
|
||||
HouseWallNDSoilG = 0x7E,
|
||||
HouseWallNDSoilH = 0x7F,
|
||||
HouseWallNDStuccoB = 0x80,
|
||||
HouseWallNDStuccoC = 0x81,
|
||||
HouseWallNDStuccoD = 0x82,
|
||||
HouseWallNDStuccoE = 0x83,
|
||||
HouseWallNDStuccoF = 0x84,
|
||||
HouseWallNDStuccoG = 0x85,
|
||||
HouseWallNDStuccoH = 0x86,
|
||||
HouseWallNDWoodB = 0x87,
|
||||
HouseWallNDWoodC = 0x88,
|
||||
HouseWallNDWoodD = 0x89,
|
||||
HouseWallNDWoodE = 0x8A,
|
||||
HouseWallNDWoodF = 0x8B,
|
||||
HouseWallNDWoodG = 0x8C,
|
||||
HouseWallNDWoodH = 0x8D,
|
||||
HouseWallPA04WoodframeF = 0x8E,
|
||||
HouseWallPA04WoodframeG = 0x8F,
|
||||
HouseWallPA04WoodframeH = 0x90,
|
||||
HouseWallPA04StoneB = 0x91,
|
||||
HouseWallPA04StoneC = 0x92,
|
||||
HouseWallPA04StoneD = 0x93,
|
||||
HouseWallPA04StoneE = 0x94,
|
||||
HouseWallPA04StoneF = 0x95,
|
||||
HouseWallPA04StoneG = 0x96,
|
||||
HouseWallPA04StoneH = 0x97,
|
||||
HouseWallPA04StuccoB = 0x98,
|
||||
HouseWallPA04StuccoC = 0x99,
|
||||
HouseWallPA04StuccoD = 0x9A,
|
||||
HouseWallPA04StuccoE = 0x9B,
|
||||
HouseWallPA04StuccoF = 0x9C,
|
||||
HouseWallPA04StuccoG = 0x9D,
|
||||
HouseWallPA04StuccoH = 0x9E,
|
||||
HouseWallPA04StandardD = 0x9F,
|
||||
HouseWallPA04StandardE = 0xA0,
|
||||
HouseWallPA04StandardF = 0xA1,
|
||||
HouseWallPA04StandardG = 0xA2,
|
||||
HouseWallPA04StandardH = 0xA3,
|
||||
HouseWallNAMetalA = 0xA4,
|
||||
HouseWallNAMetalB = 0xA5,
|
||||
HouseWallNAMetalC = 0xA6,
|
||||
HouseWallNAMetalD = 0xA7,
|
||||
HouseWallNAMetalE = 0xA8,
|
||||
HouseWallNAMetalF = 0xA9,
|
||||
HouseWallNAMetalG = 0xAA,
|
||||
HouseWallNAMetalH = 0xAB,
|
||||
HouseWallNAWoodsidingI = 0xAC,
|
||||
HouseWallNAWoodsidingJ = 0xAD,
|
||||
}
|
||||
|
|
@ -1,152 +1,154 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Simple design pattern
|
||||
/// </summary>
|
||||
public class DesignPattern : IVillagerOrigin
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple design pattern
|
||||
/// </summary>
|
||||
public class DesignPattern : IVillagerOrigin
|
||||
public const int Width = 32;
|
||||
public const int Height = 32;
|
||||
|
||||
public const int SIZE = 0x2A8; // 3 bytes unused at end
|
||||
private const int PersonalOffset = 0x38;
|
||||
private const int PaletteDataStart = 0x78;
|
||||
public const int PaletteColorCount = 15; // y not 16???
|
||||
private const int PaletteColorSize = 3; // R, G, B
|
||||
private const int PixelDataOffset = PaletteDataStart + (PaletteColorCount * PaletteColorSize); // 0xA5
|
||||
private const int PixelCount = 0x400; // Width * Height
|
||||
//private const int PixelDataSize = PixelCount / 2; // 4bit|4bit pixel packing
|
||||
|
||||
public readonly Memory<byte> Raw;
|
||||
public Span<byte> Data => Raw.Span;
|
||||
|
||||
public DesignPattern(Memory<byte> data) => Raw = data;
|
||||
|
||||
public uint Hash
|
||||
{
|
||||
public const int Width = 32;
|
||||
public const int Height = 32;
|
||||
get => ReadUInt32LittleEndian(Data);
|
||||
set => WriteUInt32LittleEndian(Data, value);
|
||||
}
|
||||
|
||||
public const int SIZE = 0x2A8; // 3 bytes unused at end
|
||||
private const int PersonalOffset = 0x38;
|
||||
private const int PaletteDataStart = 0x78;
|
||||
public const int PaletteColorCount = 15; // y not 16???
|
||||
private const int PaletteColorSize = 3; // R, G, B
|
||||
private const int PixelDataOffset = PaletteDataStart + (PaletteColorCount * PaletteColorSize); // 0xA5
|
||||
private const int PixelCount = 0x400; // Width * Height
|
||||
//private const int PixelDataSize = PixelCount / 2; // 4bit|4bit pixel packing
|
||||
public uint Version
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[0x04..]);
|
||||
set => WriteUInt32LittleEndian(Data[0x04..], value);
|
||||
}
|
||||
|
||||
public readonly byte[] Data;
|
||||
public string DesignName
|
||||
{
|
||||
get => StringUtil.GetString(Data, 0x10, 20);
|
||||
set => StringUtil.GetBytes(value, 20).CopyTo(Data[0x10..]);
|
||||
}
|
||||
|
||||
public DesignPattern(byte[] data) => Data = data;
|
||||
public uint TownID
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[PersonalOffset..]);
|
||||
set => WriteUInt32LittleEndian(Data[PersonalOffset..], value);
|
||||
}
|
||||
|
||||
public uint Hash
|
||||
public string TownName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x04, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data[(PersonalOffset + 0x04)..]);
|
||||
}
|
||||
|
||||
public Span<byte> GetTownIdentity() => Data.Slice(PersonalOffset + 0x00, 4 + 20);
|
||||
|
||||
public uint PlayerID
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[(PersonalOffset + 0x1C)..]);
|
||||
set => WriteUInt32LittleEndian(Data[(PersonalOffset + 0x1C)..], value);
|
||||
}
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x20, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data[(PersonalOffset + 0x20)..]);
|
||||
}
|
||||
|
||||
public Span<byte> GetPlayerIdentity() => Data.Slice(PersonalOffset + 0x1C, 4 + 20);
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the color choice (1-15) for the pixel at the given <see cref="index"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">Pixel index</param>
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, 0x00);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, 0x00);
|
||||
var ofs = PixelDataOffset + (index / 2);
|
||||
var val = Data[ofs];
|
||||
return (index & 1) == 0 ? (val & 0x0F) : (val >> 4);
|
||||
}
|
||||
|
||||
public uint Version
|
||||
set
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, 0x04);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, 0x04);
|
||||
}
|
||||
|
||||
public string DesignName
|
||||
{
|
||||
get => StringUtil.GetString(Data, 0x10, 20);
|
||||
set => StringUtil.GetBytes(value, 20).CopyTo(Data, 0x10);
|
||||
}
|
||||
|
||||
public uint TownID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, PersonalOffset);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, PersonalOffset);
|
||||
}
|
||||
|
||||
public string TownName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x04, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data, PersonalOffset + 0x04);
|
||||
}
|
||||
|
||||
public byte[] GetTownIdentity() => Data.Slice(PersonalOffset + 0x00, 4 + 20);
|
||||
|
||||
public uint PlayerID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, PersonalOffset + 0x1C);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, PersonalOffset + 0x1C);
|
||||
}
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x20, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data, PersonalOffset + 0x20);
|
||||
}
|
||||
|
||||
public byte[] GetPlayerIdentity() => Data.Slice(PersonalOffset + 0x1C, 4 + 20);
|
||||
|
||||
/// <summary>
|
||||
/// Gets/Sets the color choice (1-15) for the pixel at the given <see cref="index"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">Pixel index</param>
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2);
|
||||
var val = Data[ofs];
|
||||
return (index & 1) == 0 ? (val & 0x0F) : (val >> 4);
|
||||
}
|
||||
set
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2);
|
||||
var val = Data[ofs];
|
||||
var update = ((index & 1) == 0)
|
||||
? (val & 0xF0) | (value & 0xF)
|
||||
: (value & 0xF) << 4 | (val & 0xF);
|
||||
Data[ofs] = (byte)update;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetPixelIndex(int x, int y) => (y * Height) + x;
|
||||
|
||||
public int GetPixel(int x, int y)
|
||||
{
|
||||
if ((uint)x >= Width)
|
||||
throw new ArgumentException($"Argument out of range (0-{Width})", nameof(x));
|
||||
if ((uint)y >= Height)
|
||||
throw new ArgumentException($"Argument out of range (0-{Height})", nameof(y));
|
||||
|
||||
var index = GetPixelIndex(x, y);
|
||||
return this[index];
|
||||
}
|
||||
|
||||
public static int GetColorOffset(int index)
|
||||
{
|
||||
if ((uint)index >= PaletteColorCount)
|
||||
throw new ArgumentException($"Argument out of range (0-{PaletteColorCount})", nameof(index));
|
||||
return PaletteDataStart + (index * PaletteColorSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a new array with unpacked 32bit pixel data.
|
||||
/// </summary>
|
||||
public byte[] GetBitmap()
|
||||
{
|
||||
byte[] data = new byte[4 * Width * Height];
|
||||
for (int i = 0; i < PixelCount; i++)
|
||||
{
|
||||
var choice = this[i];
|
||||
if (choice == PaletteColorCount)
|
||||
continue; // transparent?
|
||||
var palette = GetColorOffset(choice);
|
||||
var ofs = i * 4;
|
||||
data[ofs + 2] = Data[palette + 0];
|
||||
data[ofs + 1] = Data[palette + 1];
|
||||
data[ofs + 0] = Data[palette + 2];
|
||||
data[ofs + 3] = 0xFF; // opaque
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a raw slice of data containing the 24bit color pixels.
|
||||
/// </summary>
|
||||
public byte[] GetPaletteBitmap()
|
||||
{
|
||||
var result = new byte[3 * PaletteColorCount];
|
||||
for (int i = 0; i < PaletteColorCount; i++)
|
||||
{
|
||||
var ofs = PaletteDataStart + (i * 3);
|
||||
result[(i * 3) + 2] = Data[ofs + 0];
|
||||
result[(i * 3) + 1] = Data[ofs + 1];
|
||||
result[(i * 3) + 0] = Data[ofs + 2];
|
||||
}
|
||||
return result;
|
||||
var ofs = PixelDataOffset + (index / 2);
|
||||
var val = Data[ofs];
|
||||
var update = ((index & 1) == 0)
|
||||
? (val & 0xF0) | (value & 0xF)
|
||||
: ((value & 0xF) << 4) | (val & 0xF);
|
||||
Data[ofs] = (byte)update;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetPixelIndex(int x, int y) => (y * Height) + x;
|
||||
|
||||
public int GetPixel(int x, int y)
|
||||
{
|
||||
if ((uint)x >= Width)
|
||||
throw new ArgumentException($"Argument out of range (0-{Width})", nameof(x));
|
||||
if ((uint)y >= Height)
|
||||
throw new ArgumentException($"Argument out of range (0-{Height})", nameof(y));
|
||||
|
||||
var index = GetPixelIndex(x, y);
|
||||
return this[index];
|
||||
}
|
||||
|
||||
public static int GetColorOffset(int index)
|
||||
{
|
||||
if ((uint)index >= PaletteColorCount)
|
||||
throw new ArgumentException($"Argument out of range (0-{PaletteColorCount})", nameof(index));
|
||||
return PaletteDataStart + (index * PaletteColorSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a new array with unpacked 32bit pixel data.
|
||||
/// </summary>
|
||||
public byte[] GetBitmap()
|
||||
{
|
||||
byte[] data = new byte[4 * Width * Height];
|
||||
for (int i = 0; i < PixelCount; i++)
|
||||
{
|
||||
var choice = this[i];
|
||||
if (choice == PaletteColorCount)
|
||||
continue; // transparent?
|
||||
var palette = GetColorOffset(choice);
|
||||
var ofs = i * 4;
|
||||
data[ofs + 2] = Data[palette + 0];
|
||||
data[ofs + 1] = Data[palette + 1];
|
||||
data[ofs + 0] = Data[palette + 2];
|
||||
data[ofs + 3] = 0xFF; // opaque
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a raw slice of data containing the 24bit color pixels.
|
||||
/// </summary>
|
||||
public byte[] GetPaletteBitmap()
|
||||
{
|
||||
var result = new byte[3 * PaletteColorCount];
|
||||
for (int i = 0; i < PaletteColorCount; i++)
|
||||
{
|
||||
var ofs = PaletteDataStart + (i * 3);
|
||||
result[(i * 3) + 2] = Data[ofs + 0];
|
||||
result[(i * 3) + 1] = Data[ofs + 1];
|
||||
result[(i * 3) + 0] = Data[ofs + 2];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +1,149 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Advanced design pattern with 4 sheets arranged in a square.
|
||||
/// </summary>
|
||||
public class DesignPatternPRO : IVillagerOrigin
|
||||
{
|
||||
/// <summary>
|
||||
/// Advanced design pattern with 4 sheets arranged in a square.
|
||||
/// </summary>
|
||||
public class DesignPatternPRO : IVillagerOrigin
|
||||
public const int Width = 32;
|
||||
public const int Height = 32;
|
||||
|
||||
public const int SIZE = 0x8A8; // 3 bytes unused at end
|
||||
public const int SheetCount = 4;
|
||||
private const int PersonalOffset = 0x38;
|
||||
private const int PaletteDataStart = 0x78;
|
||||
public const int PaletteColorCount = 15; // y not 16???
|
||||
private const int PaletteColorSize = 3; // R, G, B
|
||||
private const int PixelDataOffset = PaletteDataStart + (PaletteColorCount * PaletteColorSize); // 0xA5
|
||||
private const int PixelCount = 0x400; // Width * Height
|
||||
private const int SheetDataSize = PixelCount / 2; // 4bit|4bit pixel packing
|
||||
|
||||
public readonly Memory<byte> Raw;
|
||||
public Span<byte> Data => Raw.Span;
|
||||
|
||||
public DesignPatternPRO(Memory<byte> data) => Raw = data;
|
||||
|
||||
public uint Hash
|
||||
{
|
||||
public const int Width = 32;
|
||||
public const int Height = 32;
|
||||
|
||||
public const int SIZE = 0x8A8; // 3 bytes unused at end
|
||||
public const int SheetCount = 4;
|
||||
private const int PersonalOffset = 0x38;
|
||||
private const int PaletteDataStart = 0x78;
|
||||
public const int PaletteColorCount = 15; // y not 16???
|
||||
private const int PaletteColorSize = 3; // R, G, B
|
||||
private const int PixelDataOffset = PaletteDataStart + (PaletteColorCount * PaletteColorSize); // 0xA5
|
||||
private const int PixelCount = 0x400; // Width * Height
|
||||
private const int SheetDataSize = PixelCount / 2; // 4bit|4bit pixel packing
|
||||
|
||||
public readonly byte[] Data;
|
||||
|
||||
public DesignPatternPRO(byte[] data) => Data = data;
|
||||
|
||||
public uint Hash
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, 0x00);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, 0x00);
|
||||
}
|
||||
|
||||
public uint Version
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, 0x04);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, 0x04);
|
||||
}
|
||||
|
||||
public string DesignName
|
||||
{
|
||||
get => StringUtil.GetString(Data, 0x10, 20);
|
||||
set => StringUtil.GetBytes(value, 20).CopyTo(Data, 0x10);
|
||||
}
|
||||
|
||||
public uint TownID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, PersonalOffset);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, PersonalOffset);
|
||||
}
|
||||
|
||||
public string TownName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x04, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data, PersonalOffset + 0x04);
|
||||
}
|
||||
|
||||
public byte[] GetTownIdentity() => Data.Slice(PersonalOffset + 0x00, 4 + 20);
|
||||
|
||||
public uint PlayerID
|
||||
{
|
||||
get => BitConverter.ToUInt32(Data, PersonalOffset + 0x1C);
|
||||
set => BitConverter.GetBytes(value).CopyTo(Data, PersonalOffset + 0x1C);
|
||||
}
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x20, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data, PersonalOffset + 0x20);
|
||||
}
|
||||
|
||||
public byte[] GetPlayerIdentity() => Data.Slice(PersonalOffset + 0x1C, 4 + 20);
|
||||
|
||||
public void SetPixelAtIndex(int sheet, int index, int value)
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2) + (sheet * SheetDataSize);
|
||||
var val = Data[ofs];
|
||||
var update = ((index & 1) == 0)
|
||||
? (val & 0xF0) | (value & 0xF)
|
||||
: (value & 0xF) << 4 | (val & 0xF);
|
||||
Data[ofs] = (byte) update;
|
||||
}
|
||||
|
||||
private int GetPixelAtIndex(int sheet, int index)
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2) + (sheet * SheetDataSize);
|
||||
var val = Data[ofs];
|
||||
return (index & 1) == 0 ? (val & 0x0F) : (val >> 4);
|
||||
}
|
||||
|
||||
public static int GetPixelIndex(int sheet, int x, int y) => (sheet * SheetDataSize) + (y * Height) + x;
|
||||
|
||||
public int GetPixel(int sheet, int x, int y)
|
||||
{
|
||||
if ((uint)x >= Width)
|
||||
throw new ArgumentException($"Argument out of range (0-{Width})", nameof(x));
|
||||
if ((uint)y >= Height)
|
||||
throw new ArgumentException($"Argument out of range (0-{Height})", nameof(y));
|
||||
|
||||
var index = GetPixelIndex(sheet, x, y);
|
||||
return GetPixelAtIndex(sheet, index);
|
||||
}
|
||||
|
||||
public static int GetColorOffset(int index)
|
||||
{
|
||||
if ((uint)index >= PaletteColorCount)
|
||||
throw new ArgumentException($"Argument out of range (0-{PaletteColorCount})", nameof(index));
|
||||
return PaletteDataStart + (index * PaletteColorSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a new array with unpacked 32bit pixel data.
|
||||
/// </summary>
|
||||
public byte[] GetBitmap(int sheet)
|
||||
{
|
||||
byte[] data = new byte[4 * Width * Height];
|
||||
for (int i = 0; i < PixelCount; i++)
|
||||
{
|
||||
var choice = GetPixelAtIndex(sheet, i);
|
||||
if (choice == PaletteColorCount)
|
||||
continue; // transparent?
|
||||
var palette = GetColorOffset(choice);
|
||||
var ofs = i * 4;
|
||||
data[ofs + 2] = Data[palette + 0];
|
||||
data[ofs + 1] = Data[palette + 1];
|
||||
data[ofs + 0] = Data[palette + 2];
|
||||
data[ofs + 3] = 0xFF; // opaque
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a raw slice of data containing the 24bit color pixels.
|
||||
/// </summary>
|
||||
public byte[] GetPaletteBitmap()
|
||||
{
|
||||
var result = new byte[3 * PaletteColorCount];
|
||||
for (int i = 0; i < PaletteColorCount; i++)
|
||||
{
|
||||
var ofs = PaletteDataStart + (i * 3);
|
||||
result[(i * 3) + 2] = Data[ofs + 0];
|
||||
result[(i * 3) + 1] = Data[ofs + 1];
|
||||
result[(i * 3) + 0] = Data[ofs + 2];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
get => ReadUInt32LittleEndian(Data);
|
||||
set => WriteUInt32LittleEndian(Data, value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint Version
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[0x04..]);
|
||||
set => WriteUInt32LittleEndian(Data[0x04..], value);
|
||||
}
|
||||
|
||||
public string DesignName
|
||||
{
|
||||
get => StringUtil.GetString(Data, 0x10, 20);
|
||||
set => StringUtil.GetBytes(value, 20).CopyTo(Data[0x10..]);
|
||||
}
|
||||
|
||||
public uint TownID
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[PersonalOffset..]);
|
||||
set => WriteUInt32LittleEndian(Data[PersonalOffset..], value);
|
||||
}
|
||||
|
||||
public string TownName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x04, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data[(PersonalOffset + 0x04)..]);
|
||||
}
|
||||
|
||||
public Span<byte> GetTownIdentity() => Data.Slice(PersonalOffset + 0x00, 4 + 20);
|
||||
|
||||
public uint PlayerID
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data[(PersonalOffset + 0x1C)..]);
|
||||
set => WriteUInt32LittleEndian(Data[(PersonalOffset + 0x1C)..], value);
|
||||
}
|
||||
|
||||
public string PlayerName
|
||||
{
|
||||
get => StringUtil.GetString(Data, PersonalOffset + 0x20, 10);
|
||||
set => StringUtil.GetBytes(value, 10).CopyTo(Data[(PersonalOffset + 0x20)..]);
|
||||
}
|
||||
|
||||
public Span<byte> GetPlayerIdentity() => Data.Slice(PersonalOffset + 0x1C, 4 + 20);
|
||||
|
||||
public void SetPixelAtIndex(int sheet, int index, int value)
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2) + (sheet * SheetDataSize);
|
||||
var val = Data[ofs];
|
||||
var update = ((index & 1) == 0)
|
||||
? (val & 0xF0) | (value & 0xF)
|
||||
: (value & 0xF) << 4 | (val & 0xF);
|
||||
Data[ofs] = (byte) update;
|
||||
}
|
||||
|
||||
private int GetPixelAtIndex(int sheet, int index)
|
||||
{
|
||||
var ofs = PixelDataOffset + (index / 2) + (sheet * SheetDataSize);
|
||||
var val = Data[ofs];
|
||||
return (index & 1) == 0 ? (val & 0x0F) : (val >> 4);
|
||||
}
|
||||
|
||||
public static int GetPixelIndex(int sheet, int x, int y) => (sheet * SheetDataSize) + (y * Height) + x;
|
||||
|
||||
public int GetPixel(int sheet, int x, int y)
|
||||
{
|
||||
if ((uint)x >= Width)
|
||||
throw new ArgumentException($"Argument out of range (0-{Width})", nameof(x));
|
||||
if ((uint)y >= Height)
|
||||
throw new ArgumentException($"Argument out of range (0-{Height})", nameof(y));
|
||||
|
||||
var index = GetPixelIndex(sheet, x, y);
|
||||
return GetPixelAtIndex(sheet, index);
|
||||
}
|
||||
|
||||
public static int GetColorOffset(int index)
|
||||
{
|
||||
if ((uint)index >= PaletteColorCount)
|
||||
throw new ArgumentException($"Argument out of range (0-{PaletteColorCount})", nameof(index));
|
||||
return PaletteDataStart + (index * PaletteColorSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a new array with unpacked 32bit pixel data.
|
||||
/// </summary>
|
||||
public byte[] GetBitmap(int sheet)
|
||||
{
|
||||
byte[] data = new byte[4 * Width * Height];
|
||||
for (int i = 0; i < PixelCount; i++)
|
||||
{
|
||||
var choice = GetPixelAtIndex(sheet, i);
|
||||
if (choice == PaletteColorCount)
|
||||
continue; // transparent?
|
||||
var palette = GetColorOffset(choice);
|
||||
var ofs = i * 4;
|
||||
data[ofs + 2] = Data[palette + 0];
|
||||
data[ofs + 1] = Data[palette + 1];
|
||||
data[ofs + 0] = Data[palette + 2];
|
||||
data[ofs + 3] = 0xFF; // opaque
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a raw slice of data containing the 24bit color pixels.
|
||||
/// </summary>
|
||||
public byte[] GetPaletteBitmap()
|
||||
{
|
||||
var result = new byte[3 * PaletteColorCount];
|
||||
for (int i = 0; i < PaletteColorCount; i++)
|
||||
{
|
||||
var ofs = PaletteDataStart + (i * 3);
|
||||
result[(i * 3) + 2] = Data[ofs + 0];
|
||||
result[(i * 3) + 1] = Data[ofs + 1];
|
||||
result[(i * 3) + 0] = Data[ofs + 2];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,279 +2,278 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for dumping decrypted game files.
|
||||
/// </summary>
|
||||
/// <seealso cref="GameFileLoader"/>
|
||||
public static class GameFileDumper
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for dumping decrypted game files.
|
||||
/// Dumps a copy of the <see cref="sav"/>'s files in their decrypted state to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="GameFileLoader"/>
|
||||
public static class GameFileDumper
|
||||
/// <param name="sav">Save Data to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this HorizonSave sav, string path)
|
||||
{
|
||||
/// <summary>
|
||||
/// Dumps a copy of the <see cref="sav"/>'s files in their decrypted state to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this HorizonSave sav, string path)
|
||||
sav.Main.Dump(path);
|
||||
foreach (var p in sav.Players)
|
||||
{
|
||||
sav.Main.Dump(path);
|
||||
foreach (var p in sav.Players)
|
||||
{
|
||||
var dir = Path.Combine(path, p.DirectoryName);
|
||||
p.Dump(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps a copy of the <see cref="player"/>'s files in their decrypted state to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="player">Save Data to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this Player player, string path)
|
||||
{
|
||||
foreach (var pair in player)
|
||||
pair.Dump(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps a copy of the <see cref="pair"/>'s files in their decrypted state to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="pair">Save Data to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this EncryptedFilePair pair, string path)
|
||||
{
|
||||
Dump(path, pair.Data, pair.NameData);
|
||||
}
|
||||
|
||||
private static void Dump(string path, byte[] data, string name)
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
var file = Path.Combine(path, name);
|
||||
File.WriteAllBytes(file, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villager houses to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="houses"></param>
|
||||
/// <param name="players"></param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void DumpPlayerHouses(this IReadOnlyList<IPlayerHouse> houses, IReadOnlyList<Player> players, string path)
|
||||
{
|
||||
for (int i = 0; i < houses.Count; i++)
|
||||
{
|
||||
var filename = i < players.Count ? players[i].Personal.PlayerName : $"House {i}";
|
||||
houses[i].Dump(filename, path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villager houses to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void DumpPlayerHouses(this HorizonSave sav, string path)
|
||||
{
|
||||
var count = Math.Min(sav.Players.Length, MainSaveOffsets.PlayerCount);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var p = sav.Players[i];
|
||||
var h = sav.Main.GetPlayerHouse(i);
|
||||
h.Dump(path, p.Personal);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this IPlayerHouse h, string path, IVillagerOrigin p) => h.Dump(p.PlayerName, path);
|
||||
|
||||
private static void Dump(this IPlayerHouse h, string player, string path)
|
||||
{
|
||||
var dest = Path.Combine(path, $"{player}.{h.Extension}");
|
||||
var data = h.Write();
|
||||
File.WriteAllBytes(dest, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villager houses to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void DumpVillagerHouses(this MainSave sav, string path)
|
||||
{
|
||||
for (int i = 0; i < MainSaveOffsets.VillagerCount; i++)
|
||||
{
|
||||
var v = sav.GetVillager(i);
|
||||
var h = sav.GetVillagerHouse(i);
|
||||
h.Dump(path, v);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this IVillagerHouse h, string path, IVillager v)
|
||||
{
|
||||
var name = GameInfo.Strings.GetVillager(v.InternalName);
|
||||
var dest = Path.Combine(path, $"{name}.{h.Extension}");
|
||||
var data = h.Write();
|
||||
File.WriteAllBytes(dest, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villagers to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="villagers">Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this IEnumerable<IVillager> villagers, string path)
|
||||
{
|
||||
foreach (var v in villagers)
|
||||
v.Dump(path);
|
||||
}
|
||||
|
||||
private static void Dump(this IVillager v, string path)
|
||||
{
|
||||
var name = GameInfo.Strings.GetVillager(v.InternalName);
|
||||
var dest = Path.Combine(path, $"{name}.{v.Extension}");
|
||||
File.WriteAllBytes(dest, v.Write());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void DumpDesigns(this MainSave sav, string path, bool indexed)
|
||||
{
|
||||
for (int i = 0; i < sav.Offsets.PatternCount; i++)
|
||||
{
|
||||
var dp = sav.GetDesign(i);
|
||||
dp.Dump(path, i, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void Dump(this IReadOnlyList<DesignPattern> patterns, string path, bool indexed)
|
||||
{
|
||||
for (var index = 0; index < patterns.Count; index++)
|
||||
{
|
||||
var dp = patterns[index];
|
||||
dp.Dump(path, index, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this DesignPattern dp, string path, int index, bool indexed)
|
||||
{
|
||||
var name = dp.DesignName;
|
||||
string fn = indexed
|
||||
? $"{index:00} - {name}.nhd"
|
||||
: $"{name}.nhd";
|
||||
fn = StringUtil.CleanFileName(fn);
|
||||
var dest = Path.Combine(path, fn);
|
||||
File.WriteAllBytes(dest, dp.Data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all designs from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
/// <param name="changeOrigins">Change origins of Patterns</param>
|
||||
public static void Load(this DesignPattern[] patterns, string path, bool changeOrigins)
|
||||
{
|
||||
if (patterns.Length == 0)
|
||||
return;
|
||||
|
||||
var files = Directory.GetFiles(path, "*.nhd", SearchOption.TopDirectoryOnly);
|
||||
int ctr = 0;
|
||||
foreach (var f in files)
|
||||
{
|
||||
var fi = new FileInfo(f);
|
||||
if (fi.Length != DesignPattern.SIZE)
|
||||
continue;
|
||||
|
||||
var data = File.ReadAllBytes(f);
|
||||
var p = new DesignPattern(data);
|
||||
if (changeOrigins)
|
||||
p.ChangeOrigins(patterns[ctr], data);
|
||||
patterns[ctr] = p;
|
||||
if (++ctr >= patterns.Length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void DumpDesignsPRO(this MainSave sav, string path, bool indexed)
|
||||
{
|
||||
for (int i = 0; i < sav.Offsets.PatternCount; i++)
|
||||
{
|
||||
var dp = sav.GetDesignPRO(i);
|
||||
dp.Dump(path, i, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void Dump(this IReadOnlyList<DesignPatternPRO> patterns, string path, bool indexed)
|
||||
{
|
||||
for (var index = 0; index < patterns.Count; index++)
|
||||
{
|
||||
var dp = patterns[index];
|
||||
dp.Dump(path, index, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all designs from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
/// <param name="changeOrigins">Change origins of Patterns</param>
|
||||
/// <param name="sortAlpha">Sort the files by file name instead of depending on the Operating System's return order</param>
|
||||
public static void Load(this DesignPatternPRO[] patterns, string path, bool changeOrigins, bool sortAlpha = true)
|
||||
{
|
||||
if (patterns.Length == 0)
|
||||
return;
|
||||
|
||||
var files = Directory.GetFiles(path, "*.nhpd", SearchOption.TopDirectoryOnly);
|
||||
if (sortAlpha)
|
||||
Array.Sort(files);
|
||||
int ctr = 0;
|
||||
foreach (var f in files)
|
||||
{
|
||||
var fi = new FileInfo(f);
|
||||
if (fi.Length != DesignPatternPRO.SIZE)
|
||||
continue;
|
||||
|
||||
var data = File.ReadAllBytes(f);
|
||||
var p = new DesignPatternPRO(data);
|
||||
if (changeOrigins)
|
||||
p.ChangeOrigins(patterns[ctr], data);
|
||||
patterns[ctr] = p;
|
||||
if (++ctr >= patterns.Length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this DesignPatternPRO dp, string path, int index, bool indexed)
|
||||
{
|
||||
var name = dp.DesignName;
|
||||
string fn = indexed
|
||||
? $"{index:00} - {name}.nhpd"
|
||||
: $"{name}.nhpd";
|
||||
fn = StringUtil.CleanFileName(fn);
|
||||
var dest = Path.Combine(path, fn);
|
||||
File.WriteAllBytes(dest, dp.Data);
|
||||
var dir = Path.Combine(path, p.DirectoryName);
|
||||
p.Dump(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps a copy of the <see cref="player"/>'s files in their decrypted state to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="player">Save Data to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this Player player, string path)
|
||||
{
|
||||
foreach (var pair in player)
|
||||
pair.Dump(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps a copy of the <see cref="pair"/>'s files in their decrypted state to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="pair">Save Data to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this EncryptedFilePair pair, string path)
|
||||
{
|
||||
Dump(path, pair.Data, pair.NameData);
|
||||
}
|
||||
|
||||
private static void Dump(string path, ReadOnlySpan<byte> data, string name)
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
var file = Path.Combine(path, name);
|
||||
File.WriteAllBytes(file, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villager houses to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="houses"></param>
|
||||
/// <param name="players"></param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void DumpPlayerHouses(this IReadOnlyList<IPlayerHouse> houses, IReadOnlyList<Player> players, string path)
|
||||
{
|
||||
for (int i = 0; i < houses.Count; i++)
|
||||
{
|
||||
var filename = i < players.Count ? players[i].Personal.PlayerName : $"House {i}";
|
||||
houses[i].Dump(filename, path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villager houses to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void DumpPlayerHouses(this HorizonSave sav, string path)
|
||||
{
|
||||
var count = Math.Min(sav.Players.Length, MainSaveOffsets.PlayerCount);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var p = sav.Players[i];
|
||||
var h = sav.Main.GetPlayerHouse(i);
|
||||
h.Dump(path, p.Personal);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this IPlayerHouse h, string path, Personal p) => h.Dump(p.PlayerName, path);
|
||||
|
||||
private static void Dump(this IPlayerHouse h, string player, string path)
|
||||
{
|
||||
var dest = Path.Combine(path, $"{player}.{h.Extension}");
|
||||
var data = h.Write();
|
||||
File.WriteAllBytes(dest, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villager houses to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void DumpVillagerHouses(this MainSave sav, string path)
|
||||
{
|
||||
for (int i = 0; i < MainSaveOffsets.VillagerCount; i++)
|
||||
{
|
||||
var v = sav.GetVillager(i);
|
||||
var h = sav.GetVillagerHouse(i);
|
||||
h.Dump(path, v);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this IVillagerHouse h, string path, IVillager v)
|
||||
{
|
||||
var name = GameInfo.Strings.GetVillager(v.InternalName);
|
||||
var dest = Path.Combine(path, $"{name}.{h.Extension}");
|
||||
var data = h.Write();
|
||||
File.WriteAllBytes(dest, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all villagers to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="villagers">Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
public static void Dump(this IEnumerable<IVillager> villagers, string path)
|
||||
{
|
||||
foreach (var v in villagers)
|
||||
v.Dump(path);
|
||||
}
|
||||
|
||||
private static void Dump(this IVillager v, string path)
|
||||
{
|
||||
var name = GameInfo.Strings.GetVillager(v.InternalName);
|
||||
var dest = Path.Combine(path, $"{name}.{v.Extension}");
|
||||
File.WriteAllBytes(dest, v.Write());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void DumpDesigns(this MainSave sav, string path, bool indexed)
|
||||
{
|
||||
for (int i = 0; i < sav.Offsets.PatternCount; i++)
|
||||
{
|
||||
var dp = sav.GetDesign(i);
|
||||
dp.Dump(path, i, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void Dump(this IReadOnlyList<DesignPattern> patterns, string path, bool indexed)
|
||||
{
|
||||
for (var index = 0; index < patterns.Count; index++)
|
||||
{
|
||||
var dp = patterns[index];
|
||||
dp.Dump(path, index, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this DesignPattern dp, string path, int index, bool indexed)
|
||||
{
|
||||
var name = dp.DesignName;
|
||||
string fn = indexed
|
||||
? $"{index:00} - {name}.nhd"
|
||||
: $"{name}.nhd";
|
||||
fn = StringUtil.CleanFileName(fn);
|
||||
var dest = Path.Combine(path, fn);
|
||||
File.WriteAllBytes(dest, dp.Data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all designs from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
/// <param name="changeOrigins">Change origins of Patterns</param>
|
||||
public static void Load(this DesignPattern[] patterns, string path, bool changeOrigins)
|
||||
{
|
||||
if (patterns.Length == 0)
|
||||
return;
|
||||
|
||||
var files = Directory.GetFiles(path, "*.nhd", SearchOption.TopDirectoryOnly);
|
||||
int ctr = 0;
|
||||
foreach (var f in files)
|
||||
{
|
||||
var fi = new FileInfo(f);
|
||||
if (fi.Length != DesignPattern.SIZE)
|
||||
continue;
|
||||
|
||||
var data = File.ReadAllBytes(f);
|
||||
var p = new DesignPattern(data);
|
||||
if (changeOrigins)
|
||||
p.ChangeOrigins(patterns[ctr], data);
|
||||
patterns[ctr] = p;
|
||||
if (++ctr >= patterns.Length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to dump from</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void DumpDesignsPRO(this MainSave sav, string path, bool indexed)
|
||||
{
|
||||
for (int i = 0; i < sav.Offsets.PatternCount; i++)
|
||||
{
|
||||
var dp = sav.GetDesignPRO(i);
|
||||
dp.Dump(path, i, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumps all designs to the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to dump</param>
|
||||
/// <param name="path">Path to dump to</param>
|
||||
/// <param name="indexed">Export file names prepended with design index</param>
|
||||
public static void Dump(this IReadOnlyList<DesignPatternPRO> patterns, string path, bool indexed)
|
||||
{
|
||||
for (var index = 0; index < patterns.Count; index++)
|
||||
{
|
||||
var dp = patterns[index];
|
||||
dp.Dump(path, index, indexed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all designs from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="patterns">Patterns to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
/// <param name="changeOrigins">Change origins of Patterns</param>
|
||||
/// <param name="sortAlpha">Sort the files by file name instead of depending on the Operating System's return order</param>
|
||||
public static void Load(this DesignPatternPRO[] patterns, string path, bool changeOrigins, bool sortAlpha = true)
|
||||
{
|
||||
if (patterns.Length == 0)
|
||||
return;
|
||||
|
||||
var files = Directory.GetFiles(path, "*.nhpd", SearchOption.TopDirectoryOnly);
|
||||
if (sortAlpha)
|
||||
Array.Sort(files);
|
||||
int ctr = 0;
|
||||
foreach (var f in files)
|
||||
{
|
||||
var fi = new FileInfo(f);
|
||||
if (fi.Length != DesignPatternPRO.SIZE)
|
||||
continue;
|
||||
|
||||
var data = File.ReadAllBytes(f);
|
||||
var p = new DesignPatternPRO(data);
|
||||
if (changeOrigins)
|
||||
p.ChangeOrigins(patterns[ctr], data);
|
||||
patterns[ctr] = p;
|
||||
if (++ctr >= patterns.Length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Dump(this DesignPatternPRO dp, string path, int index, bool indexed)
|
||||
{
|
||||
var name = dp.DesignName;
|
||||
string fn = indexed
|
||||
? $"{index:00} - {name}.nhpd"
|
||||
: $"{name}.nhpd";
|
||||
fn = StringUtil.CleanFileName(fn);
|
||||
var dest = Path.Combine(path, fn);
|
||||
File.WriteAllBytes(dest, dp.Data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +1,63 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Logic for loading decrypted game files.
|
||||
/// </summary>
|
||||
/// <seealso cref="GameFileDumper"/>
|
||||
public static class GameFileLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for loading decrypted game files.
|
||||
/// Loads a copy of the <see cref="sav"/>'s files in their decrypted state from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="GameFileDumper"/>
|
||||
public static class GameFileLoader
|
||||
/// <param name="sav">Save Data to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
public static void Load(this HorizonSave sav, string path)
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads a copy of the <see cref="sav"/>'s files in their decrypted state from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">Save Data to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
public static void Load(this HorizonSave sav, string path)
|
||||
sav.Main.Load(path);
|
||||
foreach (var p in sav.Players)
|
||||
{
|
||||
sav.Main.Load(path);
|
||||
foreach (var p in sav.Players)
|
||||
{
|
||||
var dir = Path.Combine(path, p.DirectoryName);
|
||||
p.Load(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a copy of the <see cref="player"/>'s files in their decrypted state from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="player">Save Data to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
public static void Load(this Player player, string path)
|
||||
{
|
||||
foreach (var pair in player)
|
||||
pair.Load(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the <see cref="pair"/>'s files in their decrypted state from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="pair">Save Data to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
public static void Load(this EncryptedFilePair pair, string path)
|
||||
{
|
||||
Load(path, pair.Data, pair.NameData);
|
||||
}
|
||||
|
||||
private static void Load(string path, byte[] data, string name)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
return;
|
||||
|
||||
var file = Path.Combine(path, name);
|
||||
if (!File.Exists(file))
|
||||
return;
|
||||
|
||||
var import = File.ReadAllBytes(file);
|
||||
if (data.Length != import.Length)
|
||||
return;
|
||||
|
||||
import.CopyTo(data, 0);
|
||||
var dir = Path.Combine(path, p.DirectoryName);
|
||||
p.Load(dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a copy of the <see cref="player"/>'s files in their decrypted state from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="player">Save Data to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
public static void Load(this Player player, string path)
|
||||
{
|
||||
foreach (var pair in player)
|
||||
pair.Load(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the <see cref="pair"/>'s files in their decrypted state from the requested <see cref="path"/>.
|
||||
/// </summary>
|
||||
/// <param name="pair">Save Data to load</param>
|
||||
/// <param name="path">Path to load from</param>
|
||||
public static void Load(this EncryptedFilePair pair, string path)
|
||||
{
|
||||
Load(path, pair.Data, pair.NameData);
|
||||
}
|
||||
|
||||
private static void Load(string path, Span<byte> data, string name)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
return;
|
||||
|
||||
var file = Path.Combine(path, name);
|
||||
if (!File.Exists(file))
|
||||
return;
|
||||
|
||||
var import = File.ReadAllBytes(file);
|
||||
if (data.Length != import.Length)
|
||||
return;
|
||||
|
||||
import.CopyTo(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,442 +1,454 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public static class GameLists
|
||||
{
|
||||
public static class GameLists
|
||||
{
|
||||
public static readonly IReadOnlyList<ushort> Fruits = new ushort[]
|
||||
{
|
||||
2213, // apple
|
||||
2287, // Cherry
|
||||
2214, // Orange
|
||||
2286, // Peach
|
||||
2285, // Pear
|
||||
};
|
||||
public static ReadOnlySpan<ushort> Fruits =>
|
||||
[
|
||||
2213, // apple
|
||||
2287, // Cherry
|
||||
2214, // Orange
|
||||
2286, // Peach
|
||||
2285 // Pear
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<ushort> Bugs = new ushort[]
|
||||
{
|
||||
00582, // brown cicada
|
||||
00583, // tiger butterfly
|
||||
00584, // Rajah Brooke's birdwing
|
||||
00585, // red dragonfly
|
||||
00586, // Queen Alexandra's birdwing
|
||||
00587, // pondskater
|
||||
00588, // ant
|
||||
00590, // pill bug
|
||||
00591, // wharf roach
|
||||
00592, // moth
|
||||
00594, // diving beetle
|
||||
00595, // darner dragonfly
|
||||
00596, // goliath beetle
|
||||
00597, // fly
|
||||
00598, // orchid mantis
|
||||
00599, // tiger beetle
|
||||
00600, // horned hercules
|
||||
00601, // evening cicada
|
||||
00602, // cyclommatus stag
|
||||
00603, // firefly
|
||||
00604, // dung beetle
|
||||
00605, // rice grasshopper
|
||||
00606, // mosquito
|
||||
00607, // mantis
|
||||
00608, // stinkbug
|
||||
00609, // citrus long-horned beetle
|
||||
00610, // peacock butterfly
|
||||
00611, // snail
|
||||
00612, // horned dynastid
|
||||
00613, // grasshopper
|
||||
00614, // earth-boring dung beetle
|
||||
00615, // horned atlas
|
||||
00616, // walking leaf
|
||||
00617, // cricket
|
||||
00618, // giant cicada
|
||||
00619, // spider
|
||||
00620, // agrias butterfly
|
||||
00621, // robust cicada
|
||||
00622, // bagworm
|
||||
00623, // honeybee
|
||||
00624, // miyama stag
|
||||
00625, // yellow butterfly
|
||||
00626, // common butterfly
|
||||
00627, // emperor butterfly
|
||||
00628, // centipede
|
||||
00630, // walking stick
|
||||
00631, // rainbow stag
|
||||
00632, // saw stag
|
||||
00633, // flea
|
||||
00634, // mole cricket
|
||||
00635, // banded dragonfly
|
||||
00636, // monarch butterfly
|
||||
00637, // giant stag
|
||||
00638, // golden stag
|
||||
00639, // scarab beetle
|
||||
00640, // scorpion
|
||||
00641, // cicada shell
|
||||
00642, // bell cricket
|
||||
00643, // wasp
|
||||
00644, // long locust
|
||||
00645, // jewel beetle
|
||||
00646, // tarantula
|
||||
00647, // ladybug
|
||||
00648, // migratory locust
|
||||
00649, // walker cicada
|
||||
00650, // violin beetle
|
||||
00651, // hermit crab
|
||||
00652, // Atlas moth
|
||||
00653, // horned elephant
|
||||
03477, // common bluebottle
|
||||
03478, // paper kite butterfly
|
||||
03479, // great purple emperor
|
||||
03480, // drone beetle
|
||||
03482, // giraffe stag
|
||||
03483, // man-faced stink bug
|
||||
03484, // Madagascan sunset moth
|
||||
03485, // blue weevil beetle
|
||||
03487, // rosalia batesi beetle
|
||||
03539, // snowflake
|
||||
03540, // large snowflake
|
||||
04702, // Wisp spirit piece
|
||||
05157, // giant water bug
|
||||
05339, // damselfly
|
||||
05859, // cherry-blossom petal
|
||||
07374, // maple leaf
|
||||
};
|
||||
public static ReadOnlySpan<ushort> Bugs =>
|
||||
[
|
||||
00582, // brown cicada
|
||||
00583, // tiger butterfly
|
||||
00584, // Rajah Brooke's birdwing
|
||||
00585, // red dragonfly
|
||||
00586, // Queen Alexandra's birdwing
|
||||
00587, // pondskater
|
||||
00588, // ant
|
||||
00590, // pill bug
|
||||
00591, // wharf roach
|
||||
00592, // moth
|
||||
00594, // diving beetle
|
||||
00595, // darner dragonfly
|
||||
00596, // goliath beetle
|
||||
00597, // fly
|
||||
00598, // orchid mantis
|
||||
00599, // tiger beetle
|
||||
00600, // horned hercules
|
||||
00601, // evening cicada
|
||||
00602, // cyclommatus stag
|
||||
00603, // firefly
|
||||
00604, // dung beetle
|
||||
00605, // rice grasshopper
|
||||
00606, // mosquito
|
||||
00607, // mantis
|
||||
00608, // stinkbug
|
||||
00609, // citrus long-horned beetle
|
||||
00610, // peacock butterfly
|
||||
00611, // snail
|
||||
00612, // horned dynastid
|
||||
00613, // grasshopper
|
||||
00614, // earth-boring dung beetle
|
||||
00615, // horned atlas
|
||||
00616, // walking leaf
|
||||
00617, // cricket
|
||||
00618, // giant cicada
|
||||
00619, // spider
|
||||
00620, // agrias butterfly
|
||||
00621, // robust cicada
|
||||
00622, // bagworm
|
||||
00623, // honeybee
|
||||
00624, // miyama stag
|
||||
00625, // yellow butterfly
|
||||
00626, // common butterfly
|
||||
00627, // emperor butterfly
|
||||
00628, // centipede
|
||||
00630, // walking stick
|
||||
00631, // rainbow stag
|
||||
00632, // saw stag
|
||||
00633, // flea
|
||||
00634, // mole cricket
|
||||
00635, // banded dragonfly
|
||||
00636, // monarch butterfly
|
||||
00637, // giant stag
|
||||
00638, // golden stag
|
||||
00639, // scarab beetle
|
||||
00640, // scorpion
|
||||
00641, // cicada shell
|
||||
00642, // bell cricket
|
||||
00643, // wasp
|
||||
00644, // long locust
|
||||
00645, // jewel beetle
|
||||
00646, // tarantula
|
||||
00647, // ladybug
|
||||
00648, // migratory locust
|
||||
00649, // walker cicada
|
||||
00650, // violin beetle
|
||||
00651, // hermit crab
|
||||
00652, // Atlas moth
|
||||
00653, // horned elephant
|
||||
03477, // common bluebottle
|
||||
03478, // paper kite butterfly
|
||||
03479, // great purple emperor
|
||||
03480, // drone beetle
|
||||
03482, // giraffe stag
|
||||
03483, // man-faced stink bug
|
||||
03484, // Madagascan sunset moth
|
||||
03485, // blue weevil beetle
|
||||
03487, // rosalia batesi beetle
|
||||
03539, // snowflake
|
||||
03540, // large snowflake
|
||||
04702, // Wisp spirit piece
|
||||
05157, // giant water bug
|
||||
05339, // damselfly
|
||||
05859, // cherry-blossom petal
|
||||
07374 // maple leaf
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<ushort> Fish = new ushort[]
|
||||
{
|
||||
00328, // crucian carp
|
||||
00329, // goldfish
|
||||
02215, // bitterling
|
||||
02216, // pale chub
|
||||
02217, // dace
|
||||
02219, // carp
|
||||
02220, // koi
|
||||
02221, // pop-eyed goldfish
|
||||
02222, // killifish
|
||||
02223, // crawfish
|
||||
02224, // soft-shelled turtle
|
||||
02225, // tadpole
|
||||
02226, // frog
|
||||
02227, // freshwater goby
|
||||
02228, // loach
|
||||
02229, // catfish
|
||||
02231, // giant snakehead
|
||||
02232, // bluegill
|
||||
02233, // yellow perch
|
||||
02234, // black bass
|
||||
02235, // pike
|
||||
02236, // pond smelt
|
||||
02237, // sweetfish
|
||||
02238, // cherry salmon
|
||||
02239, // char
|
||||
02241, // stringfish
|
||||
02242, // salmon
|
||||
02243, // king salmon
|
||||
02244, // mitten crab
|
||||
02245, // guppy
|
||||
02246, // nibble fish
|
||||
02247, // angelfish
|
||||
02248, // neon tetra
|
||||
02249, // piranha
|
||||
02250, // arowana
|
||||
02251, // dorado
|
||||
02252, // gar
|
||||
02253, // arapaima
|
||||
02254, // saddled bichir
|
||||
02255, // sea butterfly
|
||||
02256, // sea horse
|
||||
02257, // clown fish
|
||||
02258, // surgeonfish
|
||||
02259, // butterfly fish
|
||||
02260, // Napoleonfish
|
||||
02261, // zebra turkeyfish
|
||||
02262, // blowfish
|
||||
02263, // puffer fish
|
||||
02264, // horse mackerel
|
||||
02265, // barred knifejaw
|
||||
02266, // sea bass
|
||||
02267, // red snapper
|
||||
02268, // dab
|
||||
02269, // olive flounder
|
||||
02270, // squid
|
||||
02271, // moray eel
|
||||
02272, // ribbon eel
|
||||
02273, // football fish
|
||||
02274, // tuna
|
||||
02275, // blue marlin
|
||||
02276, // giant trevally
|
||||
02277, // ray
|
||||
02278, // ocean sunfish
|
||||
02279, // hammerhead shark
|
||||
02280, // great white shark
|
||||
02281, // saw shark
|
||||
02282, // whale shark
|
||||
02283, // oarfish
|
||||
02284, // coelacanth
|
||||
02502, // stone
|
||||
03466, // empty can
|
||||
03469, // boot
|
||||
03470, // old tire
|
||||
04189, // sturgeon
|
||||
04190, // tilapia
|
||||
04191, // betta
|
||||
04192, // snapping turtle
|
||||
04193, // golden trout
|
||||
04194, // rainbowfish
|
||||
04201, // anchovy
|
||||
04202, // mahi-mahi
|
||||
04203, // suckerfish
|
||||
04204, // barreleye
|
||||
05254, // ranchu goldfish
|
||||
12514, // water egg
|
||||
};
|
||||
public static ReadOnlySpan<ushort> Fish =>
|
||||
[
|
||||
00328, // crucian carp
|
||||
00329, // goldfish
|
||||
02215, // bitterling
|
||||
02216, // pale chub
|
||||
02217, // dace
|
||||
02219, // carp
|
||||
02220, // koi
|
||||
02221, // pop-eyed goldfish
|
||||
02222, // killifish
|
||||
02223, // crawfish
|
||||
02224, // soft-shelled turtle
|
||||
02225, // tadpole
|
||||
02226, // frog
|
||||
02227, // freshwater goby
|
||||
02228, // loach
|
||||
02229, // catfish
|
||||
02231, // giant snakehead
|
||||
02232, // bluegill
|
||||
02233, // yellow perch
|
||||
02234, // black bass
|
||||
02235, // pike
|
||||
02236, // pond smelt
|
||||
02237, // sweetfish
|
||||
02238, // cherry salmon
|
||||
02239, // char
|
||||
02241, // stringfish
|
||||
02242, // salmon
|
||||
02243, // king salmon
|
||||
02244, // mitten crab
|
||||
02245, // guppy
|
||||
02246, // nibble fish
|
||||
02247, // angelfish
|
||||
02248, // neon tetra
|
||||
02249, // piranha
|
||||
02250, // arowana
|
||||
02251, // dorado
|
||||
02252, // gar
|
||||
02253, // arapaima
|
||||
02254, // saddled bichir
|
||||
02255, // sea butterfly
|
||||
02256, // sea horse
|
||||
02257, // clown fish
|
||||
02258, // surgeonfish
|
||||
02259, // butterfly fish
|
||||
02260, // Napoleonfish
|
||||
02261, // zebra turkeyfish
|
||||
02262, // blowfish
|
||||
02263, // puffer fish
|
||||
02264, // horse mackerel
|
||||
02265, // barred knifejaw
|
||||
02266, // sea bass
|
||||
02267, // red snapper
|
||||
02268, // dab
|
||||
02269, // olive flounder
|
||||
02270, // squid
|
||||
02271, // moray eel
|
||||
02272, // ribbon eel
|
||||
02273, // football fish
|
||||
02274, // tuna
|
||||
02275, // blue marlin
|
||||
02276, // giant trevally
|
||||
02277, // ray
|
||||
02278, // ocean sunfish
|
||||
02279, // hammerhead shark
|
||||
02280, // great white shark
|
||||
02281, // saw shark
|
||||
02282, // whale shark
|
||||
02283, // oarfish
|
||||
02284, // coelacanth
|
||||
02502, // stone
|
||||
03466, // empty can
|
||||
03469, // boot
|
||||
03470, // old tire
|
||||
04189, // sturgeon
|
||||
04190, // tilapia
|
||||
04191, // betta
|
||||
04192, // snapping turtle
|
||||
04193, // golden trout
|
||||
04194, // rainbowfish
|
||||
04201, // anchovy
|
||||
04202, // mahi-mahi
|
||||
04203, // suckerfish
|
||||
04204, // barreleye
|
||||
05254, // ranchu goldfish
|
||||
12514 // water egg
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<ushort> Fossils = new ushort[]
|
||||
{
|
||||
00169, // ankylo skull
|
||||
00170, // ankylo torso
|
||||
00171, // ankylo tail
|
||||
00177, // archelon skull
|
||||
00178, // archelon tail
|
||||
00180, // megacero skull
|
||||
00181, // megacero torso
|
||||
00182, // megacero tail
|
||||
00184, // dimetrodon skull
|
||||
00185, // dimetrodon torso
|
||||
00188, // iguanodon skull
|
||||
00189, // iguanodon torso
|
||||
00190, // iguanodon tail
|
||||
00192, // ophthalmo skull
|
||||
00193, // ophthalmo torso
|
||||
00195, // mammoth skull
|
||||
00196, // mammoth torso
|
||||
00198, // pachy skull
|
||||
00199, // pachy tail
|
||||
00202, // parasaur skull
|
||||
00203, // parasaur torso
|
||||
00204, // parasaur tail
|
||||
00206, // ptera body
|
||||
00207, // right ptera wing
|
||||
00208, // left ptera wing
|
||||
00210, // deinony torso
|
||||
00211, // deinony tail
|
||||
00213, // sabertooth skull
|
||||
00214, // sabertooth tail
|
||||
00216, // diplo skull
|
||||
00217, // diplo neck
|
||||
00218, // diplo chest
|
||||
00219, // diplo pelvis
|
||||
00220, // diplo tail
|
||||
00222, // spino skull
|
||||
00223, // spino torso
|
||||
00224, // spino tail
|
||||
00226, // stego skull
|
||||
00227, // stego torso
|
||||
00228, // stego tail
|
||||
00234, // plesio skull
|
||||
00235, // plesio torso
|
||||
00236, // plesio tail
|
||||
00238, // T. rex skull
|
||||
00239, // T. rex torso
|
||||
00240, // T. rex tail
|
||||
00242, // tricera skull
|
||||
00243, // tricera torso
|
||||
00244, // tricera tail
|
||||
00294, // amber
|
||||
00295, // ammonite
|
||||
00296, // coprolite
|
||||
00298, // archaeopteryx
|
||||
00300, // dinosaur track
|
||||
00301, // australopith
|
||||
00302, // shark-tooth pattern
|
||||
00303, // trilobite
|
||||
04651, // anomalocaris
|
||||
04658, // right megalo side
|
||||
04659, // left megalo side
|
||||
04660, // dunkleosteus
|
||||
04662, // myllokunmingia
|
||||
04663, // eusthenopteron
|
||||
04664, // acanthostega
|
||||
04665, // juramaia
|
||||
04688, // brachio skull
|
||||
04689, // brachio chest
|
||||
04690, // brachio pelvis
|
||||
04691, // brachio tail
|
||||
04697, // quetzal torso
|
||||
04698, // right quetzal wing
|
||||
04699, // left quetzal wing
|
||||
07251, // diplo tail tip
|
||||
};
|
||||
public static ReadOnlySpan<ushort> Fossils =>
|
||||
[
|
||||
00169, // ankylo skull
|
||||
00170, // ankylo torso
|
||||
00171, // ankylo tail
|
||||
00177, // archelon skull
|
||||
00178, // archelon tail
|
||||
00180, // megacero skull
|
||||
00181, // megacero torso
|
||||
00182, // megacero tail
|
||||
00184, // dimetrodon skull
|
||||
00185, // dimetrodon torso
|
||||
00188, // iguanodon skull
|
||||
00189, // iguanodon torso
|
||||
00190, // iguanodon tail
|
||||
00192, // ophthalmo skull
|
||||
00193, // ophthalmo torso
|
||||
00195, // mammoth skull
|
||||
00196, // mammoth torso
|
||||
00198, // pachy skull
|
||||
00199, // pachy tail
|
||||
00202, // parasaur skull
|
||||
00203, // parasaur torso
|
||||
00204, // parasaur tail
|
||||
00206, // ptera body
|
||||
00207, // right ptera wing
|
||||
00208, // left ptera wing
|
||||
00210, // deinony torso
|
||||
00211, // deinony tail
|
||||
00213, // sabertooth skull
|
||||
00214, // sabertooth tail
|
||||
00216, // diplo skull
|
||||
00217, // diplo neck
|
||||
00218, // diplo chest
|
||||
00219, // diplo pelvis
|
||||
00220, // diplo tail
|
||||
00222, // spino skull
|
||||
00223, // spino torso
|
||||
00224, // spino tail
|
||||
00226, // stego skull
|
||||
00227, // stego torso
|
||||
00228, // stego tail
|
||||
00234, // plesio skull
|
||||
00235, // plesio torso
|
||||
00236, // plesio tail
|
||||
00238, // T. rex skull
|
||||
00239, // T. rex torso
|
||||
00240, // T. rex tail
|
||||
00242, // tricera skull
|
||||
00243, // tricera torso
|
||||
00244, // tricera tail
|
||||
00294, // amber
|
||||
00295, // ammonite
|
||||
00296, // coprolite
|
||||
00298, // archaeopteryx
|
||||
00300, // dinosaur track
|
||||
00301, // australopith
|
||||
00302, // shark-tooth pattern
|
||||
00303, // trilobite
|
||||
04651, // anomalocaris
|
||||
04658, // right megalo side
|
||||
04659, // left megalo side
|
||||
04660, // dunkleosteus
|
||||
04662, // myllokunmingia
|
||||
04663, // eusthenopteron
|
||||
04664, // acanthostega
|
||||
04665, // juramaia
|
||||
04688, // brachio skull
|
||||
04689, // brachio chest
|
||||
04690, // brachio pelvis
|
||||
04691, // brachio tail
|
||||
04697, // quetzal torso
|
||||
04698, // right quetzal wing
|
||||
04699, // left quetzal wing
|
||||
07251 // diplo tail tip
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<ushort> Art = new ushort[]
|
||||
{
|
||||
00002, // scenic painting
|
||||
00005, // graceful painting (forgery)
|
||||
00006, // graceful painting
|
||||
00009, // quaint painting (forgery)
|
||||
00010, // quaint painting
|
||||
00013, // basic painting (forgery)
|
||||
00014, // basic painting
|
||||
00017, // famous painting (forgery)
|
||||
00018, // famous painting
|
||||
00020, // perfect painting
|
||||
00023, // serene painting (forgery)
|
||||
00024, // serene painting
|
||||
00027, // wistful painting (forgery)
|
||||
00028, // wistful painting
|
||||
00031, // moving painting (forgery)
|
||||
00032, // moving painting
|
||||
00034, // warm painting
|
||||
00038, // dynamic painting
|
||||
00041, // jolly painting (forgery)
|
||||
00042, // jolly painting
|
||||
00044, // common painting
|
||||
00046, // proper painting
|
||||
00048, // nice painting
|
||||
00050, // flowery painting
|
||||
00052, // moody painting
|
||||
00055, // amazing painting (forgery)
|
||||
00056, // amazing painting
|
||||
00065, // scary painting (forgery)
|
||||
00066, // scary painting
|
||||
00068, // worthy painting
|
||||
00071, // solemn painting (forgery)
|
||||
00072, // solemn painting
|
||||
00075, // wild painting right half (forgery)
|
||||
00076, // wild painting right half
|
||||
00078, // calm painting
|
||||
01331, // motherly statue
|
||||
01332, // motherly statue (forgery)
|
||||
01333, // gallant statue
|
||||
01334, // gallant statue (forgery)
|
||||
01335, // robust statue
|
||||
01336, // robust statue (forgery)
|
||||
01337, // ancient statue
|
||||
01338, // ancient statue (forgery)
|
||||
01339, // great statue
|
||||
01341, // beautiful statue
|
||||
01342, // beautiful statue (forgery)
|
||||
01343, // mystic statue
|
||||
01344, // mystic statue (forgery)
|
||||
01345, // valiant statue
|
||||
01346, // valiant statue (forgery)
|
||||
12533, // rock-head statue
|
||||
12534, // rock-head statue (forgery)
|
||||
12535, // informative statue
|
||||
12536, // informative statue (forgery)
|
||||
12537, // tremendous statue
|
||||
12538, // tremendous statue (forgery)
|
||||
12539, // warrior statue
|
||||
12540, // warrior statue (forgery)
|
||||
12541, // familiar statue
|
||||
12570, // wild painting left half
|
||||
12571, // wild painting left half (forgery)
|
||||
12618, // twinkling painting
|
||||
12619, // academic painting
|
||||
12620, // academic painting (forgery)
|
||||
12621, // sinking painting
|
||||
12622, // detailed painting
|
||||
12623, // detailed painting (forgery)
|
||||
12624, // glowing painting
|
||||
12625, // mysterious painting
|
||||
12629, // scenic painting (forgery)
|
||||
};
|
||||
public static ReadOnlySpan<ushort> Art =>
|
||||
[
|
||||
00002, // scenic painting
|
||||
00005, // graceful painting (forgery)
|
||||
00006, // graceful painting
|
||||
00009, // quaint painting (forgery)
|
||||
00010, // quaint painting
|
||||
00013, // basic painting (forgery)
|
||||
00014, // basic painting
|
||||
00017, // famous painting (forgery)
|
||||
00018, // famous painting
|
||||
00020, // perfect painting
|
||||
00023, // serene painting (forgery)
|
||||
00024, // serene painting
|
||||
00027, // wistful painting (forgery)
|
||||
00028, // wistful painting
|
||||
00031, // moving painting (forgery)
|
||||
00032, // moving painting
|
||||
00034, // warm painting
|
||||
00038, // dynamic painting
|
||||
00041, // jolly painting (forgery)
|
||||
00042, // jolly painting
|
||||
00044, // common painting
|
||||
00046, // proper painting
|
||||
00048, // nice painting
|
||||
00050, // flowery painting
|
||||
00052, // moody painting
|
||||
00055, // amazing painting (forgery)
|
||||
00056, // amazing painting
|
||||
00065, // scary painting (forgery)
|
||||
00066, // scary painting
|
||||
00068, // worthy painting
|
||||
00071, // solemn painting (forgery)
|
||||
00072, // solemn painting
|
||||
00075, // wild painting right half (forgery)
|
||||
00076, // wild painting right half
|
||||
00078, // calm painting
|
||||
01331, // motherly statue
|
||||
01332, // motherly statue (forgery)
|
||||
01333, // gallant statue
|
||||
01334, // gallant statue (forgery)
|
||||
01335, // robust statue
|
||||
01336, // robust statue (forgery)
|
||||
01337, // ancient statue
|
||||
01338, // ancient statue (forgery)
|
||||
01339, // great statue
|
||||
01341, // beautiful statue
|
||||
01342, // beautiful statue (forgery)
|
||||
01343, // mystic statue
|
||||
01344, // mystic statue (forgery)
|
||||
01345, // valiant statue
|
||||
01346, // valiant statue (forgery)
|
||||
12533, // rock-head statue
|
||||
12534, // rock-head statue (forgery)
|
||||
12535, // informative statue
|
||||
12536, // informative statue (forgery)
|
||||
12537, // tremendous statue
|
||||
12538, // tremendous statue (forgery)
|
||||
12539, // warrior statue
|
||||
12540, // warrior statue (forgery)
|
||||
12541, // familiar statue
|
||||
12570, // wild painting left half
|
||||
12571, // wild painting left half (forgery)
|
||||
12618, // twinkling painting
|
||||
12619, // academic painting
|
||||
12620, // academic painting (forgery)
|
||||
12621, // sinking painting
|
||||
12622, // detailed painting
|
||||
12623, // detailed painting (forgery)
|
||||
12624, // glowing painting
|
||||
12625, // mysterious painting
|
||||
12629 // scenic painting (forgery)
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<ushort> Dive = new ushort[]
|
||||
{
|
||||
02620, // seaweed
|
||||
02830, // sea grapes
|
||||
02831, // sea urchin
|
||||
02832, // acorn barnacle
|
||||
02833, // oyster
|
||||
02834, // turban shell
|
||||
02835, // abalone
|
||||
02838, // pearl oyster
|
||||
02839, // scallop
|
||||
02840, // sea anemone
|
||||
02841, // sea star
|
||||
02842, // sea cucumber
|
||||
02843, // sea slug
|
||||
02844, // flatworm
|
||||
02845, // mantis shrimp
|
||||
02846, // sweet shrimp
|
||||
02847, // tiger prawn
|
||||
02848, // spiny lobster
|
||||
02849, // lobster
|
||||
02850, // snow crab
|
||||
02852, // red king crab
|
||||
02853, // spider crab
|
||||
02854, // octopus
|
||||
02855, // spotted garden eel
|
||||
02856, // chambered nautilus
|
||||
02857, // horseshoe crab
|
||||
02858, // giant isopod
|
||||
06920, // firefly squid
|
||||
07191, // gazami crab
|
||||
07203, // vampire squid
|
||||
07214, // gigas giant clam
|
||||
07228, // sea pineapple
|
||||
07245, // moon jellyfish
|
||||
07252, // umbrella octopus
|
||||
07267, // slate pencil urchin
|
||||
07278, // whelk
|
||||
07303, // sea pig
|
||||
07308, // Dungeness crab
|
||||
07318, // Venus' flower basket
|
||||
07411, // mussel
|
||||
};
|
||||
public static ReadOnlySpan<ushort> Dive =>
|
||||
[
|
||||
02620, // seaweed
|
||||
02830, // sea grapes
|
||||
02831, // sea urchin
|
||||
02832, // acorn barnacle
|
||||
02833, // oyster
|
||||
02834, // turban shell
|
||||
02835, // abalone
|
||||
02838, // pearl oyster
|
||||
02839, // scallop
|
||||
02840, // sea anemone
|
||||
02841, // sea star
|
||||
02842, // sea cucumber
|
||||
02843, // sea slug
|
||||
02844, // flatworm
|
||||
02845, // mantis shrimp
|
||||
02846, // sweet shrimp
|
||||
02847, // tiger prawn
|
||||
02848, // spiny lobster
|
||||
02849, // lobster
|
||||
02850, // snow crab
|
||||
02852, // red king crab
|
||||
02853, // spider crab
|
||||
02854, // octopus
|
||||
02855, // spotted garden eel
|
||||
02856, // chambered nautilus
|
||||
02857, // horseshoe crab
|
||||
02858, // giant isopod
|
||||
06920, // firefly squid
|
||||
07191, // gazami crab
|
||||
07203, // vampire squid
|
||||
07214, // gigas giant clam
|
||||
07228, // sea pineapple
|
||||
07245, // moon jellyfish
|
||||
07252, // umbrella octopus
|
||||
07267, // slate pencil urchin
|
||||
07278, // whelk
|
||||
07303, // sea pig
|
||||
07308, // Dungeness crab
|
||||
07318, // Venus' flower basket
|
||||
07411 // mussel
|
||||
];
|
||||
|
||||
public static readonly HashSet<ushort> Shells = new()
|
||||
{
|
||||
1374, // sea snail
|
||||
1375, // venus comb
|
||||
1376, // conch
|
||||
// 2 unused 1377, 1378
|
||||
1379, // sand dollar
|
||||
1380, // coral
|
||||
1381, // giant clam
|
||||
1382, // cowrie
|
||||
public static ReadOnlySpan<ushort> Shells =>
|
||||
[
|
||||
1374, // sea snail
|
||||
1375, // venus comb
|
||||
1376, // conch
|
||||
// 2 unused 1377, 1378
|
||||
1379, // sand dollar
|
||||
1380, // coral
|
||||
1381, // giant clam
|
||||
1382, // cowrie
|
||||
|
||||
5982, // summer shell
|
||||
12968, // pearl
|
||||
};
|
||||
5982, // summer shell
|
||||
12968 // pearl
|
||||
];
|
||||
|
||||
public static readonly HashSet<ushort> Terraforming = new()
|
||||
{
|
||||
3075, // path construction permit
|
||||
3247, // waterscaping permit
|
||||
8773, // stone path permit
|
||||
8774, // brick path permit
|
||||
8775, // dark dirt path permit
|
||||
8776, // arched tile path permit
|
||||
8777, // sand path permit
|
||||
8778, // terra-cotta tile permit
|
||||
8779, // wooden path permit
|
||||
8780, // waterscaping permit
|
||||
8781, // cliff construction permit
|
||||
9771, // custom design path permit
|
||||
};
|
||||
public static ReadOnlySpan<ushort> Terraforming =>
|
||||
[
|
||||
3075, // path construction permit
|
||||
3247, // waterscaping permit
|
||||
8773, // stone path permit
|
||||
8774, // brick path permit
|
||||
8775, // dark dirt path permit
|
||||
8776, // arched tile path permit
|
||||
8777, // sand path permit
|
||||
8778, // terra-cotta tile permit
|
||||
8779, // wooden path permit
|
||||
8780, // waterscaping permit
|
||||
8781, // cliff construction permit
|
||||
9771 // custom design path permit
|
||||
];
|
||||
|
||||
public static readonly HashSet<ushort> NoCheckReceived = new(Terraforming)
|
||||
{
|
||||
Item.DIYRecipe,
|
||||
public static ReadOnlySpan<ushort> NoCheckReceived =>
|
||||
[
|
||||
3075, // path construction permit
|
||||
3247, // waterscaping permit
|
||||
8773, // stone path permit
|
||||
8774, // brick path permit
|
||||
8775, // dark dirt path permit
|
||||
8776, // arched tile path permit
|
||||
8777, // sand path permit
|
||||
8778, // terra-cotta tile permit
|
||||
8779, // wooden path permit
|
||||
8780, // waterscaping permit
|
||||
8781, // cliff construction permit
|
||||
9771, // custom design path permit
|
||||
|
||||
9046, // Vaulting Pole Recipe
|
||||
9047, // Flimsy Shovel Recipe
|
||||
9048, // Flimsy Watering Can Recipe
|
||||
9049, // Top 8 Pop Hairstyles
|
||||
9050, // Top 8 Cool Hairstyles
|
||||
9051, // Top 8 Stylish Hair Colors
|
||||
Item.DIYRecipe,
|
||||
|
||||
9221, // Pretty Good Tools Recipes
|
||||
9046, // Vaulting Pole Recipe
|
||||
9047, // Flimsy Shovel Recipe
|
||||
9048, // Flimsy Watering Can Recipe
|
||||
9049, // Top 8 Pop Hairstyles
|
||||
9050, // Top 8 Cool Hairstyles
|
||||
9051, // Top 8 Stylish Hair Colors
|
||||
|
||||
10309, // Slingshot Recipe
|
||||
9221, // Pretty Good Tools Recipes
|
||||
|
||||
11140, // Ultimate Pocket Stuffing
|
||||
10309, // Slingshot Recipe
|
||||
|
||||
12294, // Flimsy Axe Recipe
|
||||
11140, // Ultimate Pocket Stuffing
|
||||
|
||||
12327, // Ladder Recipe
|
||||
};
|
||||
}
|
||||
}
|
||||
12294, // Flimsy Axe Recipe
|
||||
|
||||
12327 // Ladder Recipe
|
||||
];
|
||||
}
|
||||
|
|
@ -1,55 +1,54 @@
|
|||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Details for field items -- items with <see cref="Item.ItemId"/> at or above 60,000.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These details are extracted from FgMainParam.bcsv
|
||||
/// </remarks>
|
||||
public class FieldItemDefinition : INamedValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Details for field items -- items with <see cref="Item.ItemId"/> at or above 60,000.
|
||||
/// Item ID
|
||||
/// </summary>
|
||||
public ushort Index { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal Name of the Item
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Item ID that the player receives if the item is dug up.
|
||||
/// </summary>
|
||||
/// <remarks>Is <see cref="Item.NONE"/> if it cannot be dug up.</remarks>
|
||||
public readonly ushort Dig;
|
||||
|
||||
/// <summary>
|
||||
/// Item ID that the player receives if the item is dug up.
|
||||
/// </summary>
|
||||
/// <remarks>Is <see cref="Item.NONE"/> if it cannot be picked up.</remarks>
|
||||
public readonly ushort Pick;
|
||||
|
||||
/// <summary>
|
||||
/// Classification of item.
|
||||
/// </summary>
|
||||
public readonly FieldItemKind Kind;
|
||||
|
||||
public FieldItemDefinition(ushort id, ushort dig, ushort pick, string name, FieldItemKind kind)
|
||||
{
|
||||
Index = id;
|
||||
Dig = dig;
|
||||
Pick = pick;
|
||||
Name = name;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the field item is picked up, this is the held item ID.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These details are extracted from FgMainParam.bcsv
|
||||
/// If the item cannot be picked up, the <see cref="Index"/> is returned as a fallback rather than <see cref="Item.NONE"/>.
|
||||
/// </remarks>
|
||||
public class FieldItemDefinition : INamedValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Item ID
|
||||
/// </summary>
|
||||
public ushort Index { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal Name of the Item
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Item ID that the player receives if the item is dug up.
|
||||
/// </summary>
|
||||
/// <remarks>Is <see cref="Item.NONE"/> if it cannot be dug up.</remarks>
|
||||
public readonly ushort Dig;
|
||||
|
||||
/// <summary>
|
||||
/// Item ID that the player receives if the item is dug up.
|
||||
/// </summary>
|
||||
/// <remarks>Is <see cref="Item.NONE"/> if it cannot be picked up.</remarks>
|
||||
public readonly ushort Pick;
|
||||
|
||||
/// <summary>
|
||||
/// Classification of item.
|
||||
/// </summary>
|
||||
public readonly FieldItemKind Kind;
|
||||
|
||||
public FieldItemDefinition(ushort id, ushort dig, ushort pick, string name, FieldItemKind kind)
|
||||
{
|
||||
Index = id;
|
||||
Dig = dig;
|
||||
Pick = pick;
|
||||
Name = name;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the field item is picked up, this is the held item ID.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the item cannot be picked up, the <see cref="Index"/> is returned as a fallback rather than <see cref="Item.NONE"/>.
|
||||
/// </remarks>
|
||||
public ushort HeldItemId => Pick != Item.NONE ? Pick : Dig != Item.NONE ? Dig : Index;
|
||||
}
|
||||
}
|
||||
public ushort HeldItemId => Pick != Item.NONE ? Pick : Dig != Item.NONE ? Dig : Index;
|
||||
}
|
||||
|
|
@ -1,108 +1,110 @@
|
|||
using static NHSE.Core.FieldItemKind;
|
||||
|
||||
namespace NHSE.Core
|
||||
namespace NHSE.Core;
|
||||
|
||||
public enum FieldItemKind : byte
|
||||
{
|
||||
public enum FieldItemKind : byte
|
||||
{
|
||||
FenceBamboo,
|
||||
FenceBarbedWire,
|
||||
FenceChinese,
|
||||
FenceConcreteBlock,
|
||||
FenceCorrugatedIron,
|
||||
FenceCrossedBamboo,
|
||||
FenceDriedStraw,
|
||||
FenceEasterEgg,
|
||||
FenceGardenPegRope,
|
||||
FenceHalloween,
|
||||
FenceHorizontalLog,
|
||||
FenceHorizontalWood,
|
||||
FenceIce,
|
||||
FenceIkegaki,
|
||||
FenceIronAndStone,
|
||||
FenceJapanese,
|
||||
FenceJuneBride,
|
||||
FenceLattice,
|
||||
FenceLatticeBig,
|
||||
FenceLog,
|
||||
FenceLogWall,
|
||||
FenceMermaid,
|
||||
FencePark,
|
||||
FencePegRope,
|
||||
FenceSandProtection,
|
||||
FenceSharply,
|
||||
FenceSteel,
|
||||
FenceStone,
|
||||
FenceVerticalWood,
|
||||
FenceWallRenga,
|
||||
FenceWoodWhite,
|
||||
LadderKitA,
|
||||
LadderKitB,
|
||||
LadderKitC,
|
||||
LadderKitD,
|
||||
PltBushAzalea,
|
||||
PltBushCamellia,
|
||||
PltBushHibiscus,
|
||||
PltBushHolly,
|
||||
PltBushHydrangea,
|
||||
PltBushOsmanthus,
|
||||
PltBushPlumeria,
|
||||
PltFlwAnemone,
|
||||
PltFlwCosmos,
|
||||
PltFlwHyacinth,
|
||||
PltFlwLily,
|
||||
PltFlwMum,
|
||||
PltFlwPansy,
|
||||
PltFlwRose,
|
||||
PltFlwRoseGold,
|
||||
PltFlwTulip,
|
||||
PltFlwYuri,
|
||||
PltTreeBamboo,
|
||||
PltTreeCedar,
|
||||
PltTreeCedarDeco,
|
||||
PltTreeOak,
|
||||
PltTreePalm,
|
||||
PltVgtCarrot,
|
||||
PltVgtPotato,
|
||||
PltVgtPumpkin,
|
||||
PltVgtSugarcane,
|
||||
PltVgtTomato,
|
||||
PltVgtWheat,
|
||||
PltVine,
|
||||
PltWeedAut0,
|
||||
PltWeedAut1,
|
||||
PltWeedAut2,
|
||||
PltWeedLight,
|
||||
PltWeedSmr,
|
||||
PltWeedSpr,
|
||||
PltWeedWin0,
|
||||
PltWeedWin1,
|
||||
StoneA,
|
||||
StoneB,
|
||||
StoneC,
|
||||
StoneD,
|
||||
StoneE,
|
||||
UnitIconHole,
|
||||
}
|
||||
FenceBamboo,
|
||||
FenceBarbedWire,
|
||||
FenceChinese,
|
||||
FenceConcreteBlock,
|
||||
FenceCorrugatedIron,
|
||||
FenceCrossedBamboo,
|
||||
FenceDriedStraw,
|
||||
FenceEasterEgg,
|
||||
FenceGardenPegRope,
|
||||
FenceHalloween,
|
||||
FenceHorizontalLog,
|
||||
FenceHorizontalWood,
|
||||
FenceIce,
|
||||
FenceIkegaki,
|
||||
FenceIronAndStone,
|
||||
FenceJapanese,
|
||||
FenceJuneBride,
|
||||
FenceLattice,
|
||||
FenceLatticeBig,
|
||||
FenceLog,
|
||||
FenceLogWall,
|
||||
FenceMermaid,
|
||||
FencePark,
|
||||
FencePegRope,
|
||||
FenceSandProtection,
|
||||
FenceSharply,
|
||||
FenceSteel,
|
||||
FenceStone,
|
||||
FenceVerticalWood,
|
||||
FenceWallRenga,
|
||||
FenceWoodWhite,
|
||||
LadderKitA,
|
||||
LadderKitB,
|
||||
LadderKitC,
|
||||
LadderKitD,
|
||||
PltBushAzalea,
|
||||
PltBushCamellia,
|
||||
PltBushHibiscus,
|
||||
PltBushHolly,
|
||||
PltBushHydrangea,
|
||||
PltBushOsmanthus,
|
||||
PltBushPlumeria,
|
||||
PltFlwAnemone,
|
||||
PltFlwCosmos,
|
||||
PltFlwHyacinth,
|
||||
PltFlwLily,
|
||||
PltFlwMum,
|
||||
PltFlwPansy,
|
||||
PltFlwRose,
|
||||
PltFlwRoseGold,
|
||||
PltFlwTulip,
|
||||
PltFlwYuri,
|
||||
PltTreeBamboo,
|
||||
PltTreeCedar,
|
||||
PltTreeCedarDeco,
|
||||
PltTreeOak,
|
||||
PltTreePalm,
|
||||
PltVgtCarrot,
|
||||
PltVgtPotato,
|
||||
PltVgtPumpkin,
|
||||
PltVgtSugarcane,
|
||||
PltVgtTomato,
|
||||
PltVgtWheat,
|
||||
PltVine,
|
||||
PltWeedAut0,
|
||||
PltWeedAut1,
|
||||
PltWeedAut2,
|
||||
PltWeedLight,
|
||||
PltWeedSmr,
|
||||
PltWeedSpr,
|
||||
PltWeedWin0,
|
||||
PltWeedWin1,
|
||||
StoneA,
|
||||
StoneB,
|
||||
StoneC,
|
||||
StoneD,
|
||||
StoneE,
|
||||
UnitIconHole,
|
||||
}
|
||||
|
||||
public static class FieldItemKindExtensions
|
||||
public static class FieldItemKindExtensions
|
||||
{
|
||||
extension(FieldItemKind type)
|
||||
{
|
||||
public static bool IsWeed(this FieldItemKind type) => type is >= PltWeedAut0 and <= PltWeedWin1;
|
||||
public static bool IsPlant(this FieldItemKind type) => type is >= PltFlwAnemone and <= PltWeedWin1;
|
||||
public static bool IsFence(this FieldItemKind type) => type is >= FenceBamboo and <= FenceWoodWhite;
|
||||
public static bool IsBush(this FieldItemKind type) => type is >= PltBushAzalea and <= PltBushOsmanthus;
|
||||
public static bool IsFlower(this FieldItemKind type) => type is >= PltFlwAnemone and <= PltFlwYuri;
|
||||
public static bool IsTree(this FieldItemKind type) => type is >= PltTreeBamboo and <= PltTreePalm;
|
||||
public static bool IsStone(this FieldItemKind type) => type is >= StoneA and <= StoneE;
|
||||
public bool IsWeed => type is (>= PltWeedAut0 and <= PltWeedWin1);
|
||||
public bool IsPlant => type is (>= PltFlwAnemone and <= PltWeedWin1);
|
||||
public bool IsFence => type is (>= FenceBamboo and <= FenceWoodWhite);
|
||||
public bool IsBush => type is (>= PltBushAzalea and <= PltBushOsmanthus);
|
||||
public bool IsFlower => type is (>= PltFlwAnemone and <= PltFlwYuri);
|
||||
public bool IsTree => type is (>= PltTreeBamboo and <= PltTreePalm);
|
||||
public bool IsStone => type is (>= StoneA and <= StoneE);
|
||||
|
||||
public static ItemKind ToItemKind(this FieldItemKind type)
|
||||
public ItemKind ToItemKind()
|
||||
{
|
||||
if (type.IsTree())
|
||||
if (type.IsTree)
|
||||
return ItemKind.Kind_Tree;
|
||||
if (type.IsFlower())
|
||||
if (type.IsFlower)
|
||||
return ItemKind.Kind_Flower;
|
||||
if (type.IsWeed())
|
||||
if (type.IsWeed)
|
||||
return ItemKind.Kind_Weed;
|
||||
return ItemKind.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user