Compare commits
5 commits
94979907b1
...
dbc52ec28f
Author | SHA1 | Date | |
---|---|---|---|
dbc52ec28f | |||
11c089d408 | |||
6b280b2668 | |||
213269521d | |||
b6ad795383 |
11 changed files with 1205 additions and 510 deletions
|
@ -114,12 +114,15 @@ add_executable(MassBuilderSaveTool WIN32
|
||||||
SaveTool/SaveTool.cpp
|
SaveTool/SaveTool.cpp
|
||||||
SaveTool/SaveTool_drawAbout.cpp
|
SaveTool/SaveTool_drawAbout.cpp
|
||||||
SaveTool/SaveTool_drawMainMenu.cpp
|
SaveTool/SaveTool_drawMainMenu.cpp
|
||||||
|
SaveTool/SaveTool_FileWatcher.cpp
|
||||||
|
SaveTool/SaveTool_Initialisation.cpp
|
||||||
SaveTool/SaveTool_MainManager.cpp
|
SaveTool/SaveTool_MainManager.cpp
|
||||||
SaveTool/SaveTool_MassViewer.cpp
|
SaveTool/SaveTool_MassViewer.cpp
|
||||||
SaveTool/SaveTool_MassViewer_Frame.cpp
|
SaveTool/SaveTool_MassViewer_Frame.cpp
|
||||||
SaveTool/SaveTool_MassViewer_Armour.cpp
|
SaveTool/SaveTool_MassViewer_Armour.cpp
|
||||||
SaveTool/SaveTool_MassViewer_Weapons.cpp
|
SaveTool/SaveTool_MassViewer_Weapons.cpp
|
||||||
SaveTool/SaveTool_ProfileManager.cpp
|
SaveTool/SaveTool_ProfileManager.cpp
|
||||||
|
SaveTool/SaveTool_UpdateChecker.cpp
|
||||||
ProfileManager/ProfileManager.h
|
ProfileManager/ProfileManager.h
|
||||||
ProfileManager/ProfileManager.cpp
|
ProfileManager/ProfileManager.cpp
|
||||||
Profile/Profile.h
|
Profile/Profile.h
|
||||||
|
|
|
@ -27,7 +27,7 @@ using namespace Containers::Literals;
|
||||||
using namespace Magnum;
|
using namespace Magnum;
|
||||||
|
|
||||||
static const std::map<Int, Containers::StringView> accessories {
|
static const std::map<Int, Containers::StringView> accessories {
|
||||||
// Primitives
|
// region Primitives
|
||||||
{1, "Cube (S)"_s},
|
{1, "Cube (S)"_s},
|
||||||
{2, "Pentagon (S)"_s},
|
{2, "Pentagon (S)"_s},
|
||||||
{3, "Hexagon (S)"_s},
|
{3, "Hexagon (S)"_s},
|
||||||
|
@ -48,6 +48,11 @@ static const std::map<Int, Containers::StringView> accessories {
|
||||||
{18, "Decal Pad 03 (S)"_s},
|
{18, "Decal Pad 03 (S)"_s},
|
||||||
{19, "Decal Pad 04 (S)"_s},
|
{19, "Decal Pad 04 (S)"_s},
|
||||||
{20, "Decal Pad 05 (S)"_s},
|
{20, "Decal Pad 05 (S)"_s},
|
||||||
|
{21, "Triangle (S)"_s},
|
||||||
|
{22, "ThinStar (S)"_s},
|
||||||
|
{23, "Star (S)"_s},
|
||||||
|
{24, "SixSideStar (S)"_s},
|
||||||
|
{25, "Asterisk (S)"_s},
|
||||||
|
|
||||||
{51, "SquBevel (S)"_s},
|
{51, "SquBevel (S)"_s},
|
||||||
{52, "TriBevel (S)"_s},
|
{52, "TriBevel (S)"_s},
|
||||||
|
@ -70,9 +75,556 @@ static const std::map<Int, Containers::StringView> accessories {
|
||||||
{69, "CofEmboss (S)"_s},
|
{69, "CofEmboss (S)"_s},
|
||||||
{70, "JevEmboss (S)"_s},
|
{70, "JevEmboss (S)"_s},
|
||||||
|
|
||||||
// Armours
|
{101, "Flat Hex Pin (S)"_s},
|
||||||
|
{102, "Cross Circle Pin (S)"_s},
|
||||||
|
{103, "Flat Circle Pin (S)"_s},
|
||||||
|
{104, "Hex Circle Pin (S)"_s},
|
||||||
|
{105, "Circle Button Pin (S)"_s},
|
||||||
|
{106, "Hexagon Pin (S)"_s},
|
||||||
|
{107, "Cross Square Pin (S)"_s},
|
||||||
|
{108, "Flat Square Pin (S)"_s},
|
||||||
|
{109, "Quad Corner Pin (S)"_s},
|
||||||
|
{110, "Bi Corner Pin (S)"_s},
|
||||||
|
{111, "Circle Pin (S)"_s},
|
||||||
|
{112, "Flat End Pin (S)"_s},
|
||||||
|
{113, "Flat Cut Pin (S)"_s},
|
||||||
|
{114, "Radial Pin (S)"_s},
|
||||||
|
{115, "Diamiter Pin (S)"_s},
|
||||||
|
|
||||||
// Components
|
{151, "TriPoint (S)"_s},
|
||||||
|
{152, "SquPoint (S)"_s},
|
||||||
|
{153, "PenPoint (S)"_s},
|
||||||
|
{154, "HexPoint (S)"_s},
|
||||||
|
{155, "CycPoint (S)"_s},
|
||||||
|
{156, "Bevel SquCutPoint (S)"_s},
|
||||||
|
{157, "Bevel HexCutPoint (S)"_s},
|
||||||
|
{158, "Bevel HexPoint (S)"_s},
|
||||||
|
{159, "Bevel CycCutPoint (S)"_s},
|
||||||
|
{160, "Bevel CycPoint (S)"_s},
|
||||||
|
|
||||||
// Connectors
|
{201, "Shaped Edge 01 (M)"_s},
|
||||||
|
{202, "Shaped Edge 02 (M)"_s},
|
||||||
|
{203, "Shaped Edge 03 (M)"_s},
|
||||||
|
{204, "Shaped Edge 04 (M)"_s},
|
||||||
|
{205, "Shaped Edge 05 (M)"_s},
|
||||||
|
{206, "Shaped Edge 06 (M)"_s},
|
||||||
|
{207, "Shaped Edge 07 (M)"_s},
|
||||||
|
{208, "Shaped Edge 08 (M)"_s},
|
||||||
|
{209, "Shaped Edge 09 (M)"_s},
|
||||||
|
{210, "Shaped Edge 10 (M)"_s},
|
||||||
|
{211, "Shaped Edge 11 (M)"_s},
|
||||||
|
{212, "Shaped Edge 12 (M)"_s},
|
||||||
|
{213, "Shaped Edge 13 (M)"_s},
|
||||||
|
{214, "Shaped Edge 14 (M)"_s},
|
||||||
|
{215, "Shaped Edge 15 (M)"_s},
|
||||||
|
{216, "Shaped Edge 16 (M)"_s},
|
||||||
|
{217, "Shaped Edge 17 (M)"_s},
|
||||||
|
{218, "Shaped Edge 18 (M)"_s},
|
||||||
|
{219, "Shaped Edge 19 (M)"_s},
|
||||||
|
{220, "Shaped Edge 20 (M)"_s},
|
||||||
|
|
||||||
|
{251, "Fish Tail 01 (M)"_s},
|
||||||
|
{252, "Fish Tail 02 (M)"_s},
|
||||||
|
{253, "Fish Tail 03 (M)"_s},
|
||||||
|
{254, "Fish Tail 04 (M)"_s},
|
||||||
|
{255, "Fish Tail 05 (M)"_s},
|
||||||
|
{256, "Based Separator 01 (M)"_s},
|
||||||
|
{257, "Based Separator 02 (M)"_s},
|
||||||
|
{258, "Based Separator 03 (M)"_s},
|
||||||
|
{259, "Based Separator 04 (M)"_s},
|
||||||
|
{260, "Based Separator 05 (M)"_s},
|
||||||
|
{261, "Based Separator 06 (M)"_s},
|
||||||
|
{262, "Based Separator 07 (M)"_s},
|
||||||
|
{263, "Based Separator 08 (M)"_s},
|
||||||
|
{264, "Based Separator 09 (M)"_s},
|
||||||
|
{265, "Based Separator 10 (M)"_s},
|
||||||
|
|
||||||
|
{301, "Rectangular Box 01 (M)"_s},
|
||||||
|
{302, "Rectangular Box 02 (M)"_s},
|
||||||
|
{303, "Rectangular Box 03 (M)"_s},
|
||||||
|
{304, "Rectangular Box 04 (M)"_s},
|
||||||
|
{305, "Rectangular Box 05 (M)"_s},
|
||||||
|
{306, "CofBox 01 (M)"_s},
|
||||||
|
{307, "CofBox 02 (M)"_s},
|
||||||
|
{308, "CofBox 03 (M)"_s},
|
||||||
|
{309, "CofBox 04 (M)"_s},
|
||||||
|
{310, "CofBox 05 (M)"_s},
|
||||||
|
{311, "Triangular Box 01 (M)"_s},
|
||||||
|
{312, "Triangular Box 02 (M)"_s},
|
||||||
|
{313, "Triangular Box 03 (M)"_s},
|
||||||
|
{314, "Triangular Box 04 (M)"_s},
|
||||||
|
{315, "Triangular Box 05 (M)"_s},
|
||||||
|
{316, "Diagonal Box A01 (M)"_s},
|
||||||
|
{317, "Diagonal Box A02 (M)"_s},
|
||||||
|
{318, "Diagonal Box A03 (M)"_s},
|
||||||
|
{319, "Diagonal Box A04 (M)"_s},
|
||||||
|
{320, "Diagonal Box A05 (M)"_s},
|
||||||
|
{321, "Diagonal Box B01 (M)"_s},
|
||||||
|
{322, "Diagonal Box B02 (M)"_s},
|
||||||
|
{323, "Diagonal Box B03 (M)"_s},
|
||||||
|
{324, "Diagonal Box B04 (M)"_s},
|
||||||
|
{325, "Diagonal Box B05 (M)"_s},
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Armours
|
||||||
|
{1001, "Short Layer 01 (M)"_s},
|
||||||
|
{1002, "Short Layer 02 (M)"_s},
|
||||||
|
{1003, "Short Layer 03 (M)"_s},
|
||||||
|
{1004, "Short Layer 04 (M)"_s},
|
||||||
|
{1005, "Short Layer 05 (M)"_s},
|
||||||
|
{1006, "Long Layer 01 (M)"_s},
|
||||||
|
{1007, "Long Layer 02 (M)"_s},
|
||||||
|
{1008, "Long Layer 03 (M)"_s},
|
||||||
|
{1009, "Long Layer 04 (M)"_s},
|
||||||
|
{1010, "Long Layer 05 (M)"_s},
|
||||||
|
{1011, "Diagonal Long Layer 01 (M)"_s},
|
||||||
|
{1012, "Diagonal Long Layer 02 (M)"_s},
|
||||||
|
{1013, "Diagonal Long Layer 03 (M)"_s},
|
||||||
|
{1014, "Diagonal Long Layer 04 (M)"_s},
|
||||||
|
{1015, "Diagonal Long Layer 05 (M)"_s},
|
||||||
|
|
||||||
|
{1051, "Sloped Layer 01 (M)"_s},
|
||||||
|
{1052, "Sloped Layer 02 (M)"_s},
|
||||||
|
{1053, "Sloped Layer 03 (M)"_s},
|
||||||
|
{1054, "Sloped Layer 04 (M)"_s},
|
||||||
|
{1055, "Sloped Layer 05 (M)"_s},
|
||||||
|
{1056, "Sloped Layer 06 (M)"_s},
|
||||||
|
{1057, "Sloped Layer 07 (M)"_s},
|
||||||
|
{1058, "Sloped Layer 08 (M)"_s},
|
||||||
|
{1059, "Sloped Layer 09 (M)"_s},
|
||||||
|
{1060, "Sloped Layer 10 (M)"_s},
|
||||||
|
{1061, "Sloped Layer 11 (M)"_s},
|
||||||
|
{1062, "Sloped Layer 12 (M)"_s},
|
||||||
|
{1063, "Sloped Layer 13 (M)"_s},
|
||||||
|
{1064, "Sloped Layer 14 (M)"_s},
|
||||||
|
{1065, "Sloped Layer 15 (M)"_s},
|
||||||
|
|
||||||
|
{1101, "Raised Center 01 (M)"_s},
|
||||||
|
{1102, "Raised Center 02 (M)"_s},
|
||||||
|
{1103, "Raised Center 03 (M)"_s},
|
||||||
|
{1104, "Raised Center 04 (M)"_s},
|
||||||
|
{1105, "Raised Center 05 (M)"_s},
|
||||||
|
{1106, "Raised Block 01 (M)"_s},
|
||||||
|
{1107, "Raised Block 02 (M)"_s},
|
||||||
|
{1108, "Raised Block 03 (M)"_s},
|
||||||
|
{1109, "Raised Pointed (M)"_s},
|
||||||
|
{1110, "Raised Cover (M)"_s},
|
||||||
|
{1111, "Raised Slant 01 (M)"_s},
|
||||||
|
{1112, "Raised Slant 02 (M)"_s},
|
||||||
|
{1113, "Raised Slant 03 (M)"_s},
|
||||||
|
{1114, "Raised Slant 04 (M)"_s},
|
||||||
|
{1115, "Raised Slant 05 (M)"_s},
|
||||||
|
|
||||||
|
{1151, "Wide Patch 01 (L)"_s},
|
||||||
|
{1152, "Wide Patch 02 (L)"_s},
|
||||||
|
{1153, "Wide Patch 03 (L)"_s},
|
||||||
|
{1154, "Wide Patch 04 (L)"_s},
|
||||||
|
{1155, "Wide Patch 05 (L)"_s},
|
||||||
|
|
||||||
|
{1201, "Pointed Armour 01 (L)"_s},
|
||||||
|
{1202, "Pointed Armour 02 (L)"_s},
|
||||||
|
{1203, "Pointed Armour 03 (L)"_s},
|
||||||
|
{1204, "Pointed Armour 04 (L)"_s},
|
||||||
|
{1205, "Pointed Armour 05 (L)"_s},
|
||||||
|
{1206, "Pointed Armour 06 (L)"_s},
|
||||||
|
{1207, "Pointed Armour 07 (L)"_s},
|
||||||
|
{1208, "Pointed Armour 08 (L)"_s},
|
||||||
|
{1209, "Pointed Armour 09 (L)"_s},
|
||||||
|
{1210, "Pointed Armour 10 (L)"_s},
|
||||||
|
{1211, "Pointed Armour 11 (L)"_s},
|
||||||
|
{1212, "Pointed Armour 12 (L)"_s},
|
||||||
|
{1213, "Pointed Armour 13 (L)"_s},
|
||||||
|
{1214, "Pointed Armour 14 (L)"_s},
|
||||||
|
{1215, "Pointed Armour 15 (L)"_s},
|
||||||
|
|
||||||
|
{1251, "E Limb Cover 01 (L)"_s},
|
||||||
|
{1252, "E Limb Cover 02 (L)"_s},
|
||||||
|
{1253, "E Limb Cover 03 (L)"_s},
|
||||||
|
{1254, "E Limb Cover 04 (L)"_s},
|
||||||
|
{1255, "E Limb Cover 05 (L)"_s},
|
||||||
|
{1256, "E Limb Cover 06 (L)"_s},
|
||||||
|
{1257, "E Limb Cover 07 (L)"_s},
|
||||||
|
{1258, "E Limb Cover 08 (L)"_s},
|
||||||
|
{1259, "E Limb Cover 09 (L)"_s},
|
||||||
|
{1260, "E Limb Cover 10 (L)"_s},
|
||||||
|
|
||||||
|
{1301, "C Limb Cover 01 (L)"_s},
|
||||||
|
{1302, "C Limb Cover 02 (L)"_s},
|
||||||
|
{1303, "C Limb Cover 03 (L)"_s},
|
||||||
|
{1304, "C Limb Cover 04 (L)"_s},
|
||||||
|
{1305, "C Limb Cover 05 (L)"_s},
|
||||||
|
{1306, "C Limb Cover 06 (L)"_s},
|
||||||
|
{1307, "C Limb Cover 07 (L)"_s},
|
||||||
|
{1308, "C Limb Cover 08 (L)"_s},
|
||||||
|
{1309, "C Limb Cover 09 (L)"_s},
|
||||||
|
{1310, "C Limb Cover 10 (L)"_s},
|
||||||
|
{1311, "C Limb Cover 11 (L)"_s},
|
||||||
|
{1312, "C Limb Cover 12 (L)"_s},
|
||||||
|
{1313, "C Limb Cover 13 (L)"_s},
|
||||||
|
{1314, "C Limb Cover 14 (L)"_s},
|
||||||
|
{1315, "C Limb Cover 15 (L)"_s},
|
||||||
|
{1316, "C Limb Cover 16 (L)"_s},
|
||||||
|
{1317, "C Limb Cover 17 (L)"_s},
|
||||||
|
{1318, "C Limb Cover 18 (L)"_s},
|
||||||
|
{1319, "C Limb Cover 19 (L)"_s},
|
||||||
|
{1320, "C Limb Cover 20 (L)"_s},
|
||||||
|
|
||||||
|
{1351, "P Limb Cover 01 (XL)"_s},
|
||||||
|
{1352, "P Limb Cover 02 (XL)"_s},
|
||||||
|
{1353, "P Limb Cover 03 (XL)"_s},
|
||||||
|
{1354, "P Limb Cover 04 (XL)"_s},
|
||||||
|
{1355, "P Limb Cover 05 (XL)"_s},
|
||||||
|
|
||||||
|
{1401, "Flat Cover 01 (XL)"_s},
|
||||||
|
{1402, "Flat Cover 02 (XL)"_s},
|
||||||
|
{1403, "Flat Cover 03 (XL)"_s},
|
||||||
|
{1404, "Flat Cover 04 (XL)"_s},
|
||||||
|
{1405, "Flat Cover 05 (XL)"_s},
|
||||||
|
{1406, "Flat Cover 06 (XL)"_s},
|
||||||
|
{1407, "Flat Cover 07 (XL)"_s},
|
||||||
|
{1408, "Flat Cover 08 (XL)"_s},
|
||||||
|
{1409, "Flat Cover 09 (XL)"_s},
|
||||||
|
{1410, "Flat Cover 10 (XL)"_s},
|
||||||
|
|
||||||
|
{1451, "L Side Opening 01 (XL)"_s},
|
||||||
|
{1452, "L Side Opening 02 (XL)"_s},
|
||||||
|
{1453, "L Side Opening 03 (XL)"_s},
|
||||||
|
{1454, "L Side Opening 04 (XL)"_s},
|
||||||
|
{1455, "L Side Opening 05 (XL)"_s},
|
||||||
|
{1456, "L Side Opening 06 (XL)"_s},
|
||||||
|
{1457, "L Side Opening 07 (XL)"_s},
|
||||||
|
{1458, "L Side Opening 08 (XL)"_s},
|
||||||
|
{1459, "L Side Opening 09 (XL)"_s},
|
||||||
|
{1460, "L Side Opening 10 (XL)"_s},
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Components
|
||||||
|
{2001, "Disc Padding 01 (M)"_s},
|
||||||
|
{2002, "Disc Padding 02 (M)"_s},
|
||||||
|
{2003, "Disc Padding 03 (M)"_s},
|
||||||
|
{2004, "Disc Padding 04 (M)"_s},
|
||||||
|
{2005, "Disc Padding 05 (M)"_s},
|
||||||
|
{2006, "Thin Padding 01 (M)"_s},
|
||||||
|
{2007, "Thin Padding 02 (M)"_s},
|
||||||
|
{2008, "Thin Padding 03 (M)"_s},
|
||||||
|
{2009, "Thin Padding 04 (M)"_s},
|
||||||
|
{2010, "Thin Padding 05 (M)"_s},
|
||||||
|
{2011, "Thick Padding 01 (M)"_s},
|
||||||
|
{2012, "Thick Padding 02 (M)"_s},
|
||||||
|
{2013, "Thick Padding 03 (M)"_s},
|
||||||
|
{2014, "Thick Padding 04 (M)"_s},
|
||||||
|
{2015, "Thick Padding 05 (M)"_s},
|
||||||
|
{2016, "Thick Padding 06 (M)"_s},
|
||||||
|
{2017, "Thick Padding 07 (M)"_s},
|
||||||
|
{2018, "Thick Padding 08 (M)"_s},
|
||||||
|
{2019, "Thick Padding 09 (M)"_s},
|
||||||
|
{2020, "Thick Padding 10 (M)"_s},
|
||||||
|
{2021, "CSide Padding 01 (M)"_s},
|
||||||
|
{2022, "CSide Padding 02 (M)"_s},
|
||||||
|
{2023, "CSide Padding 03 (M)"_s},
|
||||||
|
{2024, "CSide Padding 04 (M)"_s},
|
||||||
|
{2025, "CSide Padding 05 (M)"_s},
|
||||||
|
|
||||||
|
{2051, "Container 01 (L)"_s},
|
||||||
|
{2052, "Container 02 (L)"_s},
|
||||||
|
{2053, "Container 03 (L)"_s},
|
||||||
|
{2054, "Container 04 (L)"_s},
|
||||||
|
{2055, "Container 05 (L)"_s},
|
||||||
|
|
||||||
|
{2101, "Plating 01 (L)"_s},
|
||||||
|
{2102, "Plating 02 (L)"_s},
|
||||||
|
{2103, "Plating 03 (L)"_s},
|
||||||
|
{2104, "Plating 04 (L)"_s},
|
||||||
|
{2105, "Plating 05 (L)"_s},
|
||||||
|
|
||||||
|
{2151, "Complex Base 01 (L)"_s},
|
||||||
|
{2152, "Complex Base 02 (L)"_s},
|
||||||
|
{2153, "Complex Base 03 (L)"_s},
|
||||||
|
{2154, "Complex Base 04 (L)"_s},
|
||||||
|
{2155, "Complex Base 05 (L)"_s},
|
||||||
|
{2156, "Complex Base 06 (L)"_s},
|
||||||
|
{2157, "Complex Base 07 (L)"_s},
|
||||||
|
{2158, "Complex Base 08 (L)"_s},
|
||||||
|
{2159, "Complex Base 09 (L)"_s},
|
||||||
|
{2160, "Complex Base 10 (L)"_s},
|
||||||
|
|
||||||
|
{2201, "Long Base 01 (XL)"_s},
|
||||||
|
{2202, "Long Base 02 (XL)"_s},
|
||||||
|
{2203, "Long Base 03 (XL)"_s},
|
||||||
|
{2204, "Long Base 04 (XL)"_s},
|
||||||
|
{2205, "Long Base 05 (XL)"_s},
|
||||||
|
|
||||||
|
{2251, "Straight Wing 01 (XL)"_s},
|
||||||
|
{2252, "Straight Wing 02 (XL)"_s},
|
||||||
|
{2253, "Straight Wing 03 (XL)"_s},
|
||||||
|
{2254, "Straight Wing 04 (XL)"_s},
|
||||||
|
{2255, "Straight Wing 05 (XL)"_s},
|
||||||
|
{2256, "Straight Wing 06 (XL)"_s},
|
||||||
|
{2257, "Straight Wing 07 (XL)"_s},
|
||||||
|
{2258, "Straight Wing 08 (XL)"_s},
|
||||||
|
{2259, "Straight Wing 09 (XL)"_s},
|
||||||
|
{2260, "Straight Wing 10 (XL)"_s},
|
||||||
|
|
||||||
|
{2301, "Triangular Wing 01 (XL)"_s},
|
||||||
|
{2302, "Triangular Wing 02 (XL)"_s},
|
||||||
|
{2303, "Triangular Wing 03 (XL)"_s},
|
||||||
|
{2304, "Triangular Wing 04 (XL)"_s},
|
||||||
|
{2305, "Triangular Wing 05 (XL)"_s},
|
||||||
|
{2306, "Triangular Wing 06 (XL)"_s},
|
||||||
|
{2307, "Triangular Wing 07 (XL)"_s},
|
||||||
|
{2308, "Triangular Wing 08 (XL)"_s},
|
||||||
|
{2309, "Triangular Wing 09 (XL)"_s},
|
||||||
|
{2310, "Triangular Wing 10 (XL)"_s},
|
||||||
|
{2311, "Triangular Wing 11 (L)"_s},
|
||||||
|
{2312, "Triangular Wing 12 (L)"_s},
|
||||||
|
{2313, "Triangular Wing 13 (L)"_s},
|
||||||
|
{2314, "Triangular Wing 14 (L)"_s},
|
||||||
|
{2315, "Triangular Wing 15 (L)"_s},
|
||||||
|
|
||||||
|
{2351, "Complex Wing 01 (XL)"_s},
|
||||||
|
{2352, "Complex Wing 02 (XL)"_s},
|
||||||
|
{2353, "Complex Wing 03 (XL)"_s},
|
||||||
|
{2354, "Complex Wing 04 (XL)"_s},
|
||||||
|
{2355, "Complex Wing 05 (XL)"_s},
|
||||||
|
{2356, "Complex Wing 06 (L)"_s},
|
||||||
|
{2357, "Complex Wing 07 (L)"_s},
|
||||||
|
{2358, "Complex Wing 08 (L)"_s},
|
||||||
|
{2359, "Complex Wing 09 (L)"_s},
|
||||||
|
{2360, "Complex Wing 10 (L)"_s},
|
||||||
|
|
||||||
|
{2401, "Blade 01 (XL)"_s},
|
||||||
|
{2402, "Blade 02 (XL)"_s},
|
||||||
|
{2403, "Blade 03 (XL)"_s},
|
||||||
|
{2404, "Blade 04 (XL)"_s},
|
||||||
|
{2405, "Blade 05 (XL)"_s},
|
||||||
|
{2406, "Blade 06 (XL)"_s},
|
||||||
|
{2407, "Blade 07 (XL)"_s},
|
||||||
|
{2408, "Blade 08 (XL)"_s},
|
||||||
|
{2409, "Blade 09 (XL)"_s},
|
||||||
|
{2410, "Blade 10 (XL)"_s},
|
||||||
|
|
||||||
|
{2426, "Curved Blade 01 (XL)"_s},
|
||||||
|
{2427, "Curved Blade 02 (XL)"_s},
|
||||||
|
{2428, "Curved Blade 03 (XL)"_s},
|
||||||
|
{2429, "Curved Blade 04 (XL)"_s},
|
||||||
|
{2430, "Curved Blade 05 (XL)"_s},
|
||||||
|
|
||||||
|
{2451, "Horn 01 (M)"_s},
|
||||||
|
{2452, "Horn 02 (M)"_s},
|
||||||
|
{2453, "Horn 03 (M)"_s},
|
||||||
|
{2454, "Horn 04 (M)"_s},
|
||||||
|
{2455, "Horn 05 (M)"_s},
|
||||||
|
{2456, "Horn 06 (M)"_s},
|
||||||
|
{2457, "Horn 07 (M)"_s},
|
||||||
|
{2458, "Horn 08 (M)"_s},
|
||||||
|
{2459, "Horn 09 (M)"_s},
|
||||||
|
{2460, "Horn 10 (M)"_s},
|
||||||
|
{2461, "Horn 11 (M)"_s},
|
||||||
|
{2462, "Horn 12 (M)"_s},
|
||||||
|
{2463, "Horn 13 (M)"_s},
|
||||||
|
{2464, "Horn 14 (M)"_s},
|
||||||
|
{2465, "Horn 15 (M)"_s},
|
||||||
|
|
||||||
|
{2471, "Mask (M)"_s},
|
||||||
|
{2472, "Droplet (M)"_s},
|
||||||
|
{2473, "Thigh (M)"_s},
|
||||||
|
{2474, "LegS (M)"_s},
|
||||||
|
{2475, "LegTH (M)"_s},
|
||||||
|
{2476, "Plume 01 (M)"_s},
|
||||||
|
{2477, "Plume 02 (M)"_s},
|
||||||
|
{2478, "Plume 03 (M)"_s},
|
||||||
|
{2479, "Plume 04 (M)"_s},
|
||||||
|
{2480, "Plume 05 (M)"_s},
|
||||||
|
|
||||||
|
{2491, "Tail 01 (XL)"_s},
|
||||||
|
{2492, "Tail 02 (XL)"_s},
|
||||||
|
{2493, "Tail 03 (XL)"_s},
|
||||||
|
{2494, "Tail 04 (XL)"_s},
|
||||||
|
{2495, "Tail 05 (XL)"_s},
|
||||||
|
|
||||||
|
{2501, "Finger 01 (M)"_s},
|
||||||
|
{2502, "Finger 02 (M)"_s},
|
||||||
|
{2503, "Finger 03 (M)"_s},
|
||||||
|
{2504, "Finger 04 (M)"_s},
|
||||||
|
{2505, "Finger 05 (M)"_s},
|
||||||
|
|
||||||
|
{2521, "Fabric 01 (XL)"_s},
|
||||||
|
{2522, "Fabric 02 (XL)"_s},
|
||||||
|
{2523, "Fabric 03 (XL)"_s},
|
||||||
|
{2524, "Fabric 04 (XL)"_s},
|
||||||
|
{2525, "Fabric 05 (XL)"_s},
|
||||||
|
|
||||||
|
{2551, "Energy Barrel 01 (XL)"_s},
|
||||||
|
{2552, "Energy Barrel 02 (XL)"_s},
|
||||||
|
{2553, "Energy Barrel 03 (XL)"_s},
|
||||||
|
{2554, "Energy Barrel 04 (XL)"_s},
|
||||||
|
{2555, "Energy Barrel 05 (XL)"_s},
|
||||||
|
|
||||||
|
{2601, "L Bullet Barrel 01 (XL)"_s},
|
||||||
|
{2602, "L Bullet Barrel 02 (XL)"_s},
|
||||||
|
{2603, "L Bullet Barrel 03 (XL)"_s},
|
||||||
|
{2604, "L Bullet Barrel 04 (XL)"_s},
|
||||||
|
{2605, "L Bullet Barrel 05 (XL)"_s},
|
||||||
|
{2606, "S Bullet Barrel 01 (XL)"_s},
|
||||||
|
{2607, "S Bullet Barrel 02 (XL)"_s},
|
||||||
|
{2608, "S Bullet Barrel 03 (XL)"_s},
|
||||||
|
{2609, "S Bullet Barrel 04 (XL)"_s},
|
||||||
|
{2610, "S Bullet Barrel 05 (XL)"_s},
|
||||||
|
|
||||||
|
{2651, "Cylinder Scope 01 (M)"_s},
|
||||||
|
{2652, "Cylinder Scope 02 (M)"_s},
|
||||||
|
{2653, "Cylinder Scope 03 (M)"_s},
|
||||||
|
{2654, "Cylinder Scope 04 (M)"_s},
|
||||||
|
{2655, "Cylinder Scope 05 (M)"_s},
|
||||||
|
{2656, "Elec Scope 01 (M)"_s},
|
||||||
|
{2657, "Elec Scope 02 (M)"_s},
|
||||||
|
{2658, "Elec Scope 03 (M)"_s},
|
||||||
|
{2659, "Elec Scope 04 (M)"_s},
|
||||||
|
{2660, "Elec Scope 05 (M)"_s},
|
||||||
|
{2661, "Mark Scope 01 (S)"_s},
|
||||||
|
{2662, "Mark Scope 02 (S)"_s},
|
||||||
|
{2663, "Mark Scope 03 (S)"_s},
|
||||||
|
{2664, "Mark Scope 04 (S)"_s},
|
||||||
|
{2665, "Mark Scope 05 (S)"_s},
|
||||||
|
|
||||||
|
{2701, "S Single Weaponry (M)"_s},
|
||||||
|
{2702, "S Packed Weaponry 01 (M)"_s},
|
||||||
|
{2703, "S Packed Weaponry 02 (M)"_s},
|
||||||
|
{2704, "S Packed Weaponry 03 (M)"_s},
|
||||||
|
{2705, "S Packed Weaponry 04 (M)"_s},
|
||||||
|
{2706, "L Single Weaponry (XL)"_s},
|
||||||
|
{2707, "L Packed Weaponry 01 (XL)"_s},
|
||||||
|
{2708, "L Packed Weaponry 02 (XL)"_s},
|
||||||
|
{2709, "L Packed Weaponry 03 (XL)"_s},
|
||||||
|
{2710, "L Packed Weaponry 04 (XL)"_s},
|
||||||
|
{2711, "Atk Single Weaponry (XL)"_s},
|
||||||
|
{2712, "Atk Packed Weaponry 01 (XL)"_s},
|
||||||
|
{2713, "Atk Packed Weaponry 02 (XL)"_s},
|
||||||
|
{2714, "Atk Packed Weaponry 03 (XL)"_s},
|
||||||
|
{2715, "Atk Packed Weaponry 04 (XL)"_s},
|
||||||
|
|
||||||
|
{2751, "Vent 01 (M)"_s},
|
||||||
|
{2752, "Vent 02 (M)"_s},
|
||||||
|
{2753, "Vent 03 (M)"_s},
|
||||||
|
{2754, "Vent 04 (M)"_s},
|
||||||
|
{2755, "Vent 05 (M)"_s},
|
||||||
|
{2756, "Vent 06 (M)"_s},
|
||||||
|
{2757, "Vent 07 (M)"_s},
|
||||||
|
{2758, "Vent 08 (M)"_s},
|
||||||
|
{2759, "Vent 09 (M)"_s},
|
||||||
|
{2760, "Vent 10 (M)"_s},
|
||||||
|
|
||||||
|
{2901, "Complex Construct 01 (L)"_s},
|
||||||
|
{2902, "Complex Construct 02 (L)"_s},
|
||||||
|
{2903, "Complex Construct 03 (L)"_s},
|
||||||
|
{2904, "Complex Construct 04 (L)"_s},
|
||||||
|
{2905, "Complex Construct 05 (L)"_s},
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Connectors
|
||||||
|
{3001, "Circular Vent 01 (M)"_s},
|
||||||
|
{3002, "Circular Vent 02 (M)"_s},
|
||||||
|
{3003, "Circular Vent 03 (M)"_s},
|
||||||
|
{3004, "Circular Vent 04 (M)"_s},
|
||||||
|
{3005, "Circular Vent 05 (M)"_s},
|
||||||
|
{3006, "Circular Vent 06 (M)"_s},
|
||||||
|
{3007, "Circular Vent 07 (M)"_s},
|
||||||
|
{3008, "Circular Vent 08 (M)"_s},
|
||||||
|
{3009, "Circular Vent 09 (M)"_s},
|
||||||
|
{3010, "Circular Vent 10 (M)"_s},
|
||||||
|
{3011, "Circular Vent 11 (M)"_s},
|
||||||
|
{3012, "Circular Vent 12 (M)"_s},
|
||||||
|
{3013, "Circular Vent 13 (M)"_s},
|
||||||
|
{3014, "Circular Vent 14 (M)"_s},
|
||||||
|
{3015, "Circular Vent 15 (M)"_s},
|
||||||
|
|
||||||
|
{3051, "Reactor 01 (L)"_s},
|
||||||
|
{3052, "Reactor 02 (L)"_s},
|
||||||
|
{3053, "Reactor 03 (L)"_s},
|
||||||
|
{3054, "Reactor 04 (L)"_s},
|
||||||
|
{3055, "Reactor 05 (L)"_s},
|
||||||
|
|
||||||
|
{3101, "Connecting Tube 01 (XL)"_s},
|
||||||
|
{3102, "Connecting Tube 02 (XL)"_s},
|
||||||
|
{3103, "Connecting Tube 03 (XL)"_s},
|
||||||
|
{3104, "Connecting Tube 04 (XL)"_s},
|
||||||
|
{3105, "Connecting Tube 05 (XL)"_s},
|
||||||
|
|
||||||
|
{3151, "Latch 01 (M)"_s},
|
||||||
|
{3152, "Latch 02 (M)"_s},
|
||||||
|
{3153, "Latch 03 (M)"_s},
|
||||||
|
{3154, "Latch 04 (M)"_s},
|
||||||
|
{3155, "Latch 05 (M)"_s},
|
||||||
|
{3156, "Latch 06 (M)"_s},
|
||||||
|
{3157, "Latch 07 (M)"_s},
|
||||||
|
{3158, "Latch 08 (M)"_s},
|
||||||
|
{3159, "Latch 09 (M)"_s},
|
||||||
|
{3160, "Latch 10 (M)"_s},
|
||||||
|
{3161, "Latch 11 (M)"_s},
|
||||||
|
{3162, "Latch 12 (M)"_s},
|
||||||
|
{3163, "Latch 13 (M)"_s},
|
||||||
|
{3164, "Latch 14 (M)"_s},
|
||||||
|
{3165, "Latch 15 (M)"_s},
|
||||||
|
|
||||||
|
{3201, "Short Connector 01 (M)"_s},
|
||||||
|
{3202, "Short Connector 02 (M)"_s},
|
||||||
|
{3203, "Short Connector 03 (M)"_s},
|
||||||
|
{3204, "Short Connector 04 (M)"_s},
|
||||||
|
{3205, "Short Connector 05 (M)"_s},
|
||||||
|
{3206, "Antenna 01 (S)"_s},
|
||||||
|
{3207, "Antenna 02 (S)"_s},
|
||||||
|
{3208, "Antenna 03 (S)"_s},
|
||||||
|
{3209, "Antenna 04 (S)"_s},
|
||||||
|
{3210, "Antenna 05 (S)"_s},
|
||||||
|
|
||||||
|
{3226, "Long Connector 01 (XL)"_s},
|
||||||
|
{3227, "Long Connector 02 (XL)"_s},
|
||||||
|
{3228, "Long Connector 03 (XL)"_s},
|
||||||
|
{3229, "Long Connector 04 (XL)"_s},
|
||||||
|
{3230, "Long Connector 05 (XL)"_s},
|
||||||
|
{3231, "Long Connector 06 (XL)"_s},
|
||||||
|
{3232, "Long Connector 07 (XL)"_s},
|
||||||
|
{3233, "Long Connector 08 (XL)"_s},
|
||||||
|
{3234, "Long Connector 09 (XL)"_s},
|
||||||
|
{3235, "Long Connector 10 (XL)"_s},
|
||||||
|
|
||||||
|
{3251, "Complex Connector 01 (XL)"_s},
|
||||||
|
{3252, "Complex Connector 02 (XL)"_s},
|
||||||
|
{3253, "Complex Connector 03 (XL)"_s},
|
||||||
|
{3254, "Complex Connector 04 (XL)"_s},
|
||||||
|
{3255, "Complex Connector 05 (XL)"_s},
|
||||||
|
|
||||||
|
{3301, "Tube Line 01 (L)"_s},
|
||||||
|
{3302, "Tube Line 02 (L)"_s},
|
||||||
|
{3303, "Tube Line 03 (L)"_s},
|
||||||
|
{3304, "Tube Line 04 (XL)"_s},
|
||||||
|
{3305, "Tube Line 05 (XL)"_s},
|
||||||
|
{3306, "Tube Line 06 (M)"_s},
|
||||||
|
{3307, "Tube Line 07 (M)"_s},
|
||||||
|
{3308, "Tube Line 08 (M)"_s},
|
||||||
|
{3309, "Tube Line 09 (L)"_s},
|
||||||
|
{3310, "Tube Line 10 (L)"_s},
|
||||||
|
|
||||||
|
{3351, "Radar Plate 01 (M)"_s},
|
||||||
|
{3352, "Radar Plate 02 (M)"_s},
|
||||||
|
{3353, "Radar Plate 03 (M)"_s},
|
||||||
|
{3354, "Radar Plate 04 (M)"_s},
|
||||||
|
{3355, "Radar Plate 05 (M)"_s},
|
||||||
|
{3356, "Radar Pod 01 (M)"_s},
|
||||||
|
{3357, "Radar Pod 02 (M)"_s},
|
||||||
|
{3358, "Radar Pod 03 (M)"_s},
|
||||||
|
{3359, "Radar Pod 04 (M)"_s},
|
||||||
|
{3360, "Radar Pod 05 (M)"_s},
|
||||||
|
|
||||||
|
{3401, "Tri Pod 01 (M)"_s},
|
||||||
|
{3402, "Tri Pod 02 (M)"_s},
|
||||||
|
{3403, "Tri Pod 03 (M)"_s},
|
||||||
|
{3404, "Tri Pod 04 (M)"_s},
|
||||||
|
{3405, "Tri Pod 05 (M)"_s},
|
||||||
|
{3406, "Signal Pod 01 (M)"_s},
|
||||||
|
{3407, "Signal Pod 02 (M)"_s},
|
||||||
|
{3408, "Signal Pod 03 (M)"_s},
|
||||||
|
{3409, "Signal Pod 04 (M)"_s},
|
||||||
|
{3410, "Signal Pod 05 (M)"_s},
|
||||||
|
// endregion
|
||||||
};
|
};
|
||||||
|
|
|
@ -106,6 +106,10 @@ void Mass::refreshValues() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_mass->saveType() != "/Game/Core/Save/bpSaveGameUnit.bpSaveGameUnit_C"_s) {
|
||||||
|
Utility::Error{} << _filename << "is not a valid unit save.";
|
||||||
|
}
|
||||||
|
|
||||||
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
auto unit_data = _mass->at<GenericStructProperty>("UnitData"_s);
|
||||||
if(!unit_data) {
|
if(!unit_data) {
|
||||||
Utility::Error{} << "Couldn't find unit data in" << _filename;
|
Utility::Error{} << "Couldn't find unit data in" << _filename;
|
||||||
|
|
|
@ -106,6 +106,10 @@ void Profile::refreshValues() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_profile.saveType() != "/Game/Core/Save/bpSaveGameProfile.bpSaveGameProfile_C"_s) {
|
||||||
|
Utility::Error{} << _filename << "is not a valid profile save.";
|
||||||
|
}
|
||||||
|
|
||||||
auto name_prop = _profile.at<StringProperty>("CompanyName"_s);
|
auto name_prop = _profile.at<StringProperty>("CompanyName"_s);
|
||||||
if(!name_prop) {
|
if(!name_prop) {
|
||||||
_lastError = "No company name in "_s + _filename;
|
_lastError = "No company name in "_s + _filename;
|
||||||
|
|
|
@ -16,13 +16,7 @@
|
||||||
|
|
||||||
#include "SaveTool.h"
|
#include "SaveTool.h"
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <Corrade/Containers/Pair.h>
|
|
||||||
#include <Corrade/Containers/ScopeGuard.h>
|
#include <Corrade/Containers/ScopeGuard.h>
|
||||||
#include <Corrade/Utility/Format.h>
|
|
||||||
#include <Corrade/Utility/Path.h>
|
|
||||||
#include <Corrade/Utility/String.h>
|
|
||||||
#include <Corrade/Utility/Unicode.h>
|
#include <Corrade/Utility/Unicode.h>
|
||||||
|
|
||||||
#include <Magnum/GL/DebugOutput.h>
|
#include <Magnum/GL/DebugOutput.h>
|
||||||
|
@ -35,15 +29,10 @@
|
||||||
|
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#include <windef.h>
|
|
||||||
#include <winuser.h>
|
|
||||||
#include <processthreadsapi.h>
|
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <shlobj.h>
|
|
||||||
#include <wtsapi32.h>
|
#include <wtsapi32.h>
|
||||||
|
|
||||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
|
||||||
|
|
||||||
using namespace Containers::Literals;
|
using namespace Containers::Literals;
|
||||||
|
|
||||||
|
@ -204,39 +193,6 @@ SaveTool::~SaveTool() {
|
||||||
Utility::Debug{} << "Exiting...";
|
Utility::Debug{} << "Exiting...";
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveTool::handleFileAction(efsw::WatchID watch_id,
|
|
||||||
const std::string&,
|
|
||||||
const std::string& filename,
|
|
||||||
efsw::Action action,
|
|
||||||
std::string old_filename)
|
|
||||||
{
|
|
||||||
SDL_Event event;
|
|
||||||
SDL_zero(event);
|
|
||||||
event.type = _fileEventId;
|
|
||||||
|
|
||||||
if(watch_id == _watchIDs[StagingDir] && Utility::String::endsWith(filename, ".sav")) {
|
|
||||||
event.user.code = StagedUpdate;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Utility::String::endsWith(filename, "Config.sav")) {
|
|
||||||
return;
|
|
||||||
} // TODO: actually do something when config files will finally be handled
|
|
||||||
|
|
||||||
if(!Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.user.code = action;
|
|
||||||
event.user.data1 = Containers::String{Containers::AllocatedInit, filename.c_str()}.release();
|
|
||||||
if(action == efsw::Actions::Moved) {
|
|
||||||
event.user.data2 = Containers::String{Containers::AllocatedInit, old_filename.c_str()}.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::drawEvent() {
|
void SaveTool::drawEvent() {
|
||||||
#ifdef SAVETOOL_DEBUG_BUILD
|
#ifdef SAVETOOL_DEBUG_BUILD
|
||||||
tweak.update();
|
tweak.update();
|
||||||
|
@ -299,406 +255,6 @@ void SaveTool::anyEvent(SDL_Event& event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveTool::initEvent(SDL_Event& event) {
|
|
||||||
_initThread.join();
|
|
||||||
|
|
||||||
switch(event.user.code) {
|
|
||||||
case InitSuccess:
|
|
||||||
_uiState = UiState::ProfileManager;
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
break;
|
|
||||||
case ProfileManagerFailure:
|
|
||||||
Utility::Error{} << "Error initialising ProfileManager:" << _profileManager->lastError();
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising ProfileManager", _profileManager->lastError().data(), window());
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::updateCheckEvent(SDL_Event& event) {
|
|
||||||
_updateThread.join();
|
|
||||||
|
|
||||||
if(event.user.code == CurlInitFailed) {
|
|
||||||
_queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(event.user.code == CurlError) {
|
|
||||||
Containers::String error{static_cast<char*>(event.user.data2), CURL_ERROR_SIZE, nullptr};
|
|
||||||
_queue.addToast(Toast::Type::Error, error, std::chrono::milliseconds{5000});
|
|
||||||
_queue.addToast(Toast::Type::Error, static_cast<char*>(event.user.data1), std::chrono::milliseconds{5000});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(event.user.code == CurlTimeout) {
|
|
||||||
_queue.addToast(Toast::Type::Error, "The request timed out."_s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(event.user.code != 200) {
|
|
||||||
_queue.addToast(Toast::Type::Error, Utility::format("The request failed with error code {}", event.user.code));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Version {
|
|
||||||
explicit Version(Containers::StringView str) {
|
|
||||||
std::size_t start_point = 0;
|
|
||||||
|
|
||||||
if(str[0] == 'v') {
|
|
||||||
start_point++;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto components = Containers::StringView{str.data() + start_point}.split('.');
|
|
||||||
|
|
||||||
major = std::strtol(components[0].data(), nullptr, 10);
|
|
||||||
minor = std::strtol(components[1].data(), nullptr, 10);
|
|
||||||
patch = std::strtol(components[2].data(), nullptr, 10);
|
|
||||||
|
|
||||||
fullVersion = major * 10000 + minor * 100 + patch;
|
|
||||||
|
|
||||||
if(str.hasSuffix("-pre")) {
|
|
||||||
prerelease = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Int fullVersion;
|
|
||||||
Int major = 0;
|
|
||||||
Int minor = 0;
|
|
||||||
Int patch = 0;
|
|
||||||
bool prerelease = false;
|
|
||||||
|
|
||||||
bool operator==(const Version& other) const {
|
|
||||||
return fullVersion == other.fullVersion;
|
|
||||||
}
|
|
||||||
bool operator>(const Version& other) const {
|
|
||||||
if((fullVersion > other.fullVersion) ||
|
|
||||||
(fullVersion == other.fullVersion && prerelease == false && other.prerelease == true))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
operator Containers::String() const {
|
|
||||||
return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : "");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Version current_ver{SAVETOOL_VERSION};
|
|
||||||
|
|
||||||
Containers::String response{static_cast<char*>(event.user.data1), strlen(static_cast<char*>(event.user.data1)), nullptr};
|
|
||||||
auto components = response.split('\n');
|
|
||||||
|
|
||||||
Version latest_ver{components.front()};
|
|
||||||
|
|
||||||
if(latest_ver > current_ver) {
|
|
||||||
_queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information."_s,
|
|
||||||
std::chrono::milliseconds{5000});
|
|
||||||
_updateAvailable = true;
|
|
||||||
_latestVersion = latest_ver;
|
|
||||||
_releaseLink = Utility::format("https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases/tag/v{}", components.front());
|
|
||||||
_downloadLink = components.back();
|
|
||||||
}
|
|
||||||
else if(latest_ver == current_ver || (current_ver > latest_ver && current_ver.prerelease == true)) {
|
|
||||||
_queue.addToast(Toast::Type::Success, "The application is already up to date."_s);
|
|
||||||
}
|
|
||||||
else if(current_ver > latest_ver && current_ver.prerelease == false) {
|
|
||||||
_queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"_s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::fileUpdateEvent(SDL_Event& event) {
|
|
||||||
if(event.user.code == StagedUpdate) {
|
|
||||||
_massManager->refreshStagedMasses();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String filename{static_cast<char*>(event.user.data1), std::strlen(static_cast<char*>(event.user.data1)), nullptr};
|
|
||||||
Containers::String old_filename;
|
|
||||||
|
|
||||||
Int index = 0;
|
|
||||||
Int old_index = 0;
|
|
||||||
bool is_current_profile = filename == _currentProfile->filename();
|
|
||||||
bool is_unit = filename.hasPrefix(_currentProfile->isDemo() ? "DemoUnit"_s : "Unit"_s);
|
|
||||||
if(is_unit) {
|
|
||||||
index = ((filename[_currentProfile->isDemo() ? 8 : 4] - 0x30) * 10) +
|
|
||||||
(filename[_currentProfile->isDemo() ? 9 : 5] - 0x30);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(event.user.code == FileMoved) {
|
|
||||||
old_filename = Containers::String{static_cast<char*>(event.user.data2), std::strlen(static_cast<char*>(event.user.data2)), nullptr};
|
|
||||||
old_index = ((old_filename[_currentProfile->isDemo() ? 8 : 4] - 0x30) * 10) +
|
|
||||||
(old_filename[_currentProfile->isDemo() ? 9 : 5] - 0x30);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(event.user.code) {
|
|
||||||
case FileAdded:
|
|
||||||
if(is_unit) {
|
|
||||||
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
|
||||||
_massManager->refreshHangar(index);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_currentMass->setDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FileDeleted:
|
|
||||||
if(is_current_profile) {
|
|
||||||
_currentProfile = nullptr;
|
|
||||||
_uiState = UiState::ProfileManager;
|
|
||||||
_profileManager->refreshProfiles();
|
|
||||||
}
|
|
||||||
else if(is_unit) {
|
|
||||||
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
|
||||||
_massManager->refreshHangar(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FileModified:
|
|
||||||
if(is_current_profile) {
|
|
||||||
_currentProfile->refreshValues();
|
|
||||||
}
|
|
||||||
else if(is_unit) {
|
|
||||||
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
|
||||||
_massManager->refreshHangar(index);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(_modifiedBySaveTool && _currentMass->filename() == filename) {
|
|
||||||
auto handle = CreateFileW(Utility::Unicode::widen(Containers::StringView{filename}).data(), GENERIC_READ, 0,
|
|
||||||
nullptr, OPEN_EXISTING, 0, nullptr);
|
|
||||||
if(handle && handle != INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(handle);
|
|
||||||
_modifiedBySaveTool = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_currentMass->setDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FileMoved:
|
|
||||||
if(is_unit) {
|
|
||||||
if(old_filename.hasSuffix(".sav"_s)) {
|
|
||||||
_massManager->refreshHangar(index);
|
|
||||||
_massManager->refreshHangar(old_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_queue.addToast(Toast::Type::Warning, "Unknown file action type"_s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::initialiseConfiguration() {
|
|
||||||
Utility::Debug{} << "Reading configuration file...";
|
|
||||||
|
|
||||||
if(_conf.hasValue("cheat_mode"_s)) {
|
|
||||||
_cheatMode = _conf.value<bool>("cheat_mode"_s);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_conf.setValue("cheat_mode"_s, _cheatMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_conf.hasValue("unsafe_mode"_s)) {
|
|
||||||
_unsafeMode = _conf.value<bool>("unsafe_mode"_s);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_conf.setValue("unsafe_mode"_s, _unsafeMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_conf.hasValue("startup_update_check"_s)) {
|
|
||||||
_checkUpdatesOnStartup = _conf.value<bool>("startup_update_check"_s);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_conf.hasValue("skip_disclaimer"_s)) {
|
|
||||||
_skipDisclaimer = _conf.value<bool>("skip_disclaimer"_s);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_conf.setValue("skip_disclaimer"_s, _skipDisclaimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_conf.hasValue("frame_limit"_s)) {
|
|
||||||
std::string frame_limit = _conf.value("frame_limit"_s);
|
|
||||||
if(frame_limit == "vsync"_s) {
|
|
||||||
_framelimit = Framelimit::Vsync;
|
|
||||||
}
|
|
||||||
else if(frame_limit == "half_vsync"_s) {
|
|
||||||
_framelimit = Framelimit::HalfVsync;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_framelimit = Framelimit::FpsCap;
|
|
||||||
_fpsCap = std::stoul(frame_limit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_conf.setValue("frame_limit"_s, "vsync"_s);
|
|
||||||
}
|
|
||||||
|
|
||||||
_conf.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::initialiseGui() {
|
|
||||||
Utility::Debug{} << "Initialising ImGui...";
|
|
||||||
|
|
||||||
ImGui::CreateContext();
|
|
||||||
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf"_s);
|
|
||||||
ImFontConfig font_config;
|
|
||||||
font_config.FontDataOwnedByAtlas = false;
|
|
||||||
std::strcpy(font_config.Name, "Source Sans Pro");
|
|
||||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(reg_font.data()), reg_font.size(), 20.0f, &font_config);
|
|
||||||
|
|
||||||
auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS);
|
|
||||||
static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
|
|
||||||
ImFontConfig icon_config;
|
|
||||||
icon_config.FontDataOwnedByAtlas = false;
|
|
||||||
icon_config.MergeMode = true;
|
|
||||||
icon_config.PixelSnapH = true;
|
|
||||||
icon_config.OversampleH = icon_config.OversampleV = 1;
|
|
||||||
icon_config.GlyphMinAdvanceX = 18.0f;
|
|
||||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(icon_font.data()), icon_font.size(), 16.0f, &icon_config, icon_range);
|
|
||||||
|
|
||||||
auto brand_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAB);
|
|
||||||
static const ImWchar brand_range[] = { ICON_MIN_FAB, ICON_MAX_FAB, 0 };
|
|
||||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(brand_font.data()), brand_font.size(), 16.0f, &icon_config, brand_range);
|
|
||||||
|
|
||||||
auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf"_s);
|
|
||||||
ImVector<ImWchar> range;
|
|
||||||
ImFontGlyphRangesBuilder builder;
|
|
||||||
builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
|
|
||||||
builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences.
|
|
||||||
builder.BuildRanges(&range);
|
|
||||||
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(mono_font.data()), mono_font.size(), 18.0f, &font_config, range.Data);
|
|
||||||
|
|
||||||
_imgui = ImGuiIntegration::Context(*ImGui::GetCurrentContext(), windowSize());
|
|
||||||
|
|
||||||
io.IniFilename = nullptr;
|
|
||||||
|
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
|
||||||
|
|
||||||
style.WindowTitleAlign = {0.5f, 0.5f};
|
|
||||||
style.FrameRounding = 3.2f;
|
|
||||||
style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::initialiseManager() {
|
|
||||||
SDL_Event event;
|
|
||||||
SDL_zero(event);
|
|
||||||
event.type = _initEventId;
|
|
||||||
|
|
||||||
_profileManager.emplace(_saveDir, _backupsDir);
|
|
||||||
if(!_profileManager->ready()) {
|
|
||||||
event.user.code = ProfileManagerFailure;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.user.code = InitSuccess;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SaveTool::initialiseToolDirectories() -> bool {
|
|
||||||
Utility::Debug{} << "Initialising Save Tool directories...";
|
|
||||||
|
|
||||||
_backupsDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "backups");
|
|
||||||
_stagingDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "staging");
|
|
||||||
//_armouryDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "armoury");
|
|
||||||
//_armoursDir = Utility::Directory::join(_armouryDir, "armours");
|
|
||||||
//_weaponsDir = Utility::Directory::join(_armouryDir, "weapons");
|
|
||||||
//_stylesDir = Utility::Directory::join(_armouryDir, "styles");
|
|
||||||
|
|
||||||
if(!Utility::Path::exists(_backupsDir)) {
|
|
||||||
Utility::Debug{} << "Backups directory not found, creating...";
|
|
||||||
if(!Utility::Path::make(_backupsDir)) {
|
|
||||||
Utility::Error{} << (_lastError = "Couldn't create the backups directory.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Utility::Path::exists(_stagingDir)) {
|
|
||||||
Utility::Debug{} << "Staging directory not found, creating...";
|
|
||||||
if(!Utility::Path::make(_stagingDir)) {
|
|
||||||
Utility::Error{} << (_lastError = "Couldn't create the backups directory.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if(!Utility::Directory::exists(_armouryDir)) {
|
|
||||||
// Utility::Debug{} << "Armoury directory not found, creating...";
|
|
||||||
// if(!Utility::Path::make(_armouryDir)) {
|
|
||||||
// Utility::Error{} << (_lastError = "Couldn't create the armoury directory.");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if(!Utility::Directory::exists(_armoursDir)) {
|
|
||||||
// Utility::Debug{} << "Armours directory not found, creating...";
|
|
||||||
// if(!Utility::Path::make(_armoursDir)) {
|
|
||||||
// Utility::Error{} << (_lastError = "Couldn't create the armours directory.");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if(!Utility::Directory::exists(_weaponsDir)) {
|
|
||||||
// Utility::Debug{} << "Weapons directory not found, creating...";
|
|
||||||
// if(!Utility::Path::make(_weaponsDir)) {
|
|
||||||
// Utility::Error{} << (_lastError = "Couldn't create the weapons directory.");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if(!Utility::Directory::exists(_stylesDir)) {
|
|
||||||
// Utility::Debug{} << "Styles directory not found, creating...";
|
|
||||||
// if(!Utility::Path::make(_stylesDir)) {
|
|
||||||
// Utility::Error{} << (_lastError = "Couldn't create the styles directory.");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SaveTool::findGameDataDirectory() -> bool {
|
|
||||||
Utility::Debug{} << "Searching for the game's save directory...";
|
|
||||||
|
|
||||||
wchar_t* localappdata_path = nullptr;
|
|
||||||
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
|
|
||||||
if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK)
|
|
||||||
{
|
|
||||||
Utility::Error{} << (_lastError = "SHGetKnownFolderPath() failed in SaveTool::findGameDataDirectory()"_s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_gameDataDir = Utility::Path::join(Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder"_s);
|
|
||||||
|
|
||||||
if(!Utility::Path::exists(_gameDataDir)) {
|
|
||||||
Utility::Error{} << (_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_configDir = Utility::Path::join(_gameDataDir, "Saved/Config/WindowsNoEditor"_s);
|
|
||||||
_saveDir = Utility::Path::join(_gameDataDir, "Saved/SaveGames"_s);
|
|
||||||
_screenshotsDir = Utility::Path::join(_gameDataDir, "Saved/Screenshots/WindowsNoEditor"_s);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::initialiseMassManager() {
|
|
||||||
_massManager.emplace(_saveDir, _currentProfile->account(), _currentProfile->isDemo(), _stagingDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::initialiseFileWatcher() {
|
|
||||||
_fileWatcher.emplace();
|
|
||||||
_watchIDs[SaveDir] = _fileWatcher->addWatch(_saveDir, this, false);
|
|
||||||
_watchIDs[StagingDir] = _fileWatcher->addWatch(_stagingDir, this, false);
|
|
||||||
_fileWatcher->watch();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::drawImGui() {
|
void SaveTool::drawImGui() {
|
||||||
_imgui.newFrame();
|
_imgui.newFrame();
|
||||||
|
|
||||||
|
@ -895,55 +451,3 @@ void SaveTool::checkGameState() {
|
||||||
_gameState = GameState::Unknown;
|
_gameState = GameState::Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto writeData(char* ptr, std::size_t size, std::size_t nmemb, Containers::String* buf)-> std::size_t {
|
|
||||||
if(!ptr || !buf) return 0;
|
|
||||||
(*buf) = Utility::format("{}{}", *buf, Containers::StringView{ptr, size * nmemb});
|
|
||||||
return size * nmemb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SaveTool::checkForUpdates() {
|
|
||||||
SDL_Event event;
|
|
||||||
SDL_zero(event);
|
|
||||||
event.type = _updateEventId;
|
|
||||||
|
|
||||||
auto curl = curl_easy_init();
|
|
||||||
if(!curl) {
|
|
||||||
event.user.code = CurlInitFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(curl) {
|
|
||||||
Containers::String response_body{Containers::AllocatedInit, ""};
|
|
||||||
Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE * 2};
|
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version");
|
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data());
|
|
||||||
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L);
|
|
||||||
|
|
||||||
auto code = curl_easy_perform(curl);
|
|
||||||
|
|
||||||
if(code == CURLE_OK) {
|
|
||||||
long status = 0;
|
|
||||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
|
|
||||||
event.user.code = Int(status);
|
|
||||||
event.user.data1 = response_body.release();
|
|
||||||
}
|
|
||||||
else if(code == CURLE_OPERATION_TIMEDOUT) {
|
|
||||||
event.user.code = CurlTimeout;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
event.user.code = CurlError;
|
|
||||||
event.user.data1 = const_cast<char*>(curl_easy_strerror(code));
|
|
||||||
event.user.data2 = Containers::String{error_buffer}.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
}
|
|
||||||
|
|
139
src/SaveTool/SaveTool_FileWatcher.cpp
Normal file
139
src/SaveTool/SaveTool_FileWatcher.cpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <Corrade/Utility/String.h>
|
||||||
|
#include <Corrade/Utility/Unicode.h>
|
||||||
|
|
||||||
|
#include <fileapi.h>
|
||||||
|
#include <handleapi.h>
|
||||||
|
|
||||||
|
#include "SaveTool.h"
|
||||||
|
|
||||||
|
void SaveTool::handleFileAction(efsw::WatchID watch_id,
|
||||||
|
const std::string&,
|
||||||
|
const std::string& filename,
|
||||||
|
efsw::Action action,
|
||||||
|
std::string old_filename)
|
||||||
|
{
|
||||||
|
SDL_Event event;
|
||||||
|
SDL_zero(event);
|
||||||
|
event.type = _fileEventId;
|
||||||
|
|
||||||
|
if(watch_id == _watchIDs[StagingDir] && Utility::String::endsWith(filename, ".sav")) {
|
||||||
|
event.user.code = StagedUpdate;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Utility::String::endsWith(filename, "Config.sav")) {
|
||||||
|
return;
|
||||||
|
} // TODO: actually do something when config files will finally be handled
|
||||||
|
|
||||||
|
if(!Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.user.code = action;
|
||||||
|
event.user.data1 = Containers::String{Containers::AllocatedInit, filename.c_str()}.release();
|
||||||
|
if(action == efsw::Actions::Moved) {
|
||||||
|
event.user.data2 = Containers::String{Containers::AllocatedInit, old_filename.c_str()}.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::fileUpdateEvent(SDL_Event& event) {
|
||||||
|
if(event.user.code == StagedUpdate) {
|
||||||
|
_massManager->refreshStagedMasses();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Containers::String filename{static_cast<char*>(event.user.data1), std::strlen(static_cast<char*>(event.user.data1)), nullptr};
|
||||||
|
Containers::String old_filename;
|
||||||
|
|
||||||
|
Int index = 0;
|
||||||
|
Int old_index = 0;
|
||||||
|
bool is_current_profile = filename == _currentProfile->filename();
|
||||||
|
bool is_unit = filename.hasPrefix(_currentProfile->isDemo() ? "DemoUnit"_s : "Unit"_s);
|
||||||
|
if(is_unit) {
|
||||||
|
index = ((filename[_currentProfile->isDemo() ? 8 : 4] - 0x30) * 10) +
|
||||||
|
(filename[_currentProfile->isDemo() ? 9 : 5] - 0x30);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.user.code == FileMoved) {
|
||||||
|
old_filename = Containers::String{static_cast<char*>(event.user.data2), std::strlen(static_cast<char*>(event.user.data2)), nullptr};
|
||||||
|
old_index = ((old_filename[_currentProfile->isDemo() ? 8 : 4] - 0x30) * 10) +
|
||||||
|
(old_filename[_currentProfile->isDemo() ? 9 : 5] - 0x30);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(event.user.code) {
|
||||||
|
case FileAdded:
|
||||||
|
if(is_unit) {
|
||||||
|
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
||||||
|
_massManager->refreshHangar(index);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_currentMass->setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FileDeleted:
|
||||||
|
if(is_current_profile) {
|
||||||
|
_currentProfile = nullptr;
|
||||||
|
_uiState = UiState::ProfileManager;
|
||||||
|
_profileManager->refreshProfiles();
|
||||||
|
}
|
||||||
|
else if(is_unit) {
|
||||||
|
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
||||||
|
_massManager->refreshHangar(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FileModified:
|
||||||
|
if(is_current_profile) {
|
||||||
|
_currentProfile->refreshValues();
|
||||||
|
}
|
||||||
|
else if(is_unit) {
|
||||||
|
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
|
||||||
|
_massManager->refreshHangar(index);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(_modifiedBySaveTool && _currentMass->filename() == filename) {
|
||||||
|
auto handle = CreateFileW(Utility::Unicode::widen(Containers::StringView{filename}).data(), GENERIC_READ, 0,
|
||||||
|
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||||
|
if(handle && handle != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(handle);
|
||||||
|
_modifiedBySaveTool = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_currentMass->setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FileMoved:
|
||||||
|
if(is_unit) {
|
||||||
|
if(old_filename.hasSuffix(".sav"_s)) {
|
||||||
|
_massManager->refreshHangar(index);
|
||||||
|
_massManager->refreshHangar(old_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_queue.addToast(Toast::Type::Warning, "Unknown file action type"_s);
|
||||||
|
}
|
||||||
|
}
|
255
src/SaveTool/SaveTool_Initialisation.cpp
Normal file
255
src/SaveTool/SaveTool_Initialisation.cpp
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <Corrade/Containers/Pair.h>
|
||||||
|
#include <Corrade/Containers/ScopeGuard.h>
|
||||||
|
#include <Corrade/Utility/Path.h>
|
||||||
|
#include <Corrade/Utility/Unicode.h>
|
||||||
|
|
||||||
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||||
|
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||||
|
|
||||||
|
#include "SaveTool.h"
|
||||||
|
|
||||||
|
void SaveTool::initEvent(SDL_Event& event) {
|
||||||
|
_initThread.join();
|
||||||
|
|
||||||
|
switch(event.user.code) {
|
||||||
|
case InitSuccess:
|
||||||
|
_uiState = UiState::ProfileManager;
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
break;
|
||||||
|
case ProfileManagerFailure:
|
||||||
|
Utility::Error{} << "Error initialising ProfileManager:" << _profileManager->lastError();
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising ProfileManager", _profileManager->lastError().data(), window());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::initialiseConfiguration() {
|
||||||
|
Utility::Debug{} << "Reading configuration file...";
|
||||||
|
|
||||||
|
if(_conf.hasValue("cheat_mode"_s)) {
|
||||||
|
_cheatMode = _conf.value<bool>("cheat_mode"_s);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_conf.setValue("cheat_mode"_s, _cheatMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_conf.hasValue("unsafe_mode"_s)) {
|
||||||
|
_unsafeMode = _conf.value<bool>("unsafe_mode"_s);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_conf.setValue("unsafe_mode"_s, _unsafeMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_conf.hasValue("startup_update_check"_s)) {
|
||||||
|
_checkUpdatesOnStartup = _conf.value<bool>("startup_update_check"_s);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_conf.hasValue("skip_disclaimer"_s)) {
|
||||||
|
_skipDisclaimer = _conf.value<bool>("skip_disclaimer"_s);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_conf.setValue("skip_disclaimer"_s, _skipDisclaimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_conf.hasValue("frame_limit"_s)) {
|
||||||
|
std::string frame_limit = _conf.value("frame_limit"_s);
|
||||||
|
if(frame_limit == "vsync"_s) {
|
||||||
|
_framelimit = Framelimit::Vsync;
|
||||||
|
}
|
||||||
|
else if(frame_limit == "half_vsync"_s) {
|
||||||
|
_framelimit = Framelimit::HalfVsync;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_framelimit = Framelimit::FpsCap;
|
||||||
|
_fpsCap = std::stoul(frame_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_conf.setValue("frame_limit"_s, "vsync"_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
_conf.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::initialiseGui() {
|
||||||
|
Utility::Debug{} << "Initialising ImGui...";
|
||||||
|
|
||||||
|
ImGui::CreateContext();
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf"_s);
|
||||||
|
ImFontConfig font_config;
|
||||||
|
font_config.FontDataOwnedByAtlas = false;
|
||||||
|
std::strcpy(font_config.Name, "Source Sans Pro");
|
||||||
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(reg_font.data()), reg_font.size(), 20.0f, &font_config);
|
||||||
|
|
||||||
|
auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS);
|
||||||
|
static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
|
||||||
|
ImFontConfig icon_config;
|
||||||
|
icon_config.FontDataOwnedByAtlas = false;
|
||||||
|
icon_config.MergeMode = true;
|
||||||
|
icon_config.PixelSnapH = true;
|
||||||
|
icon_config.OversampleH = icon_config.OversampleV = 1;
|
||||||
|
icon_config.GlyphMinAdvanceX = 18.0f;
|
||||||
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(icon_font.data()), icon_font.size(), 16.0f, &icon_config, icon_range);
|
||||||
|
|
||||||
|
auto brand_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAB);
|
||||||
|
static const ImWchar brand_range[] = { ICON_MIN_FAB, ICON_MAX_FAB, 0 };
|
||||||
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(brand_font.data()), brand_font.size(), 16.0f, &icon_config, brand_range);
|
||||||
|
|
||||||
|
auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf"_s);
|
||||||
|
ImVector<ImWchar> range;
|
||||||
|
ImFontGlyphRangesBuilder builder;
|
||||||
|
builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
|
||||||
|
builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences.
|
||||||
|
builder.BuildRanges(&range);
|
||||||
|
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(mono_font.data()), mono_font.size(), 18.0f, &font_config, range.Data);
|
||||||
|
|
||||||
|
_imgui = ImGuiIntegration::Context(*ImGui::GetCurrentContext(), windowSize());
|
||||||
|
|
||||||
|
io.IniFilename = nullptr;
|
||||||
|
|
||||||
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
|
||||||
|
style.WindowTitleAlign = {0.5f, 0.5f};
|
||||||
|
style.FrameRounding = 3.2f;
|
||||||
|
style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::initialiseManager() {
|
||||||
|
SDL_Event event;
|
||||||
|
SDL_zero(event);
|
||||||
|
event.type = _initEventId;
|
||||||
|
|
||||||
|
_profileManager.emplace(_saveDir, _backupsDir);
|
||||||
|
if(!_profileManager->ready()) {
|
||||||
|
event.user.code = ProfileManagerFailure;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.user.code = InitSuccess;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SaveTool::initialiseToolDirectories() -> bool {
|
||||||
|
Utility::Debug{} << "Initialising Save Tool directories...";
|
||||||
|
|
||||||
|
_backupsDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "backups");
|
||||||
|
_stagingDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "staging");
|
||||||
|
//_armouryDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "armoury");
|
||||||
|
//_armoursDir = Utility::Directory::join(_armouryDir, "armours");
|
||||||
|
//_weaponsDir = Utility::Directory::join(_armouryDir, "weapons");
|
||||||
|
//_stylesDir = Utility::Directory::join(_armouryDir, "styles");
|
||||||
|
|
||||||
|
if(!Utility::Path::exists(_backupsDir)) {
|
||||||
|
Utility::Debug{} << "Backups directory not found, creating...";
|
||||||
|
if(!Utility::Path::make(_backupsDir)) {
|
||||||
|
Utility::Error{} << (_lastError = "Couldn't create the backups directory.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Utility::Path::exists(_stagingDir)) {
|
||||||
|
Utility::Debug{} << "Staging directory not found, creating...";
|
||||||
|
if(!Utility::Path::make(_stagingDir)) {
|
||||||
|
Utility::Error{} << (_lastError = "Couldn't create the backups directory.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if(!Utility::Directory::exists(_armouryDir)) {
|
||||||
|
// Utility::Debug{} << "Armoury directory not found, creating...";
|
||||||
|
// if(!Utility::Path::make(_armouryDir)) {
|
||||||
|
// Utility::Error{} << (_lastError = "Couldn't create the armoury directory.");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if(!Utility::Directory::exists(_armoursDir)) {
|
||||||
|
// Utility::Debug{} << "Armours directory not found, creating...";
|
||||||
|
// if(!Utility::Path::make(_armoursDir)) {
|
||||||
|
// Utility::Error{} << (_lastError = "Couldn't create the armours directory.");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if(!Utility::Directory::exists(_weaponsDir)) {
|
||||||
|
// Utility::Debug{} << "Weapons directory not found, creating...";
|
||||||
|
// if(!Utility::Path::make(_weaponsDir)) {
|
||||||
|
// Utility::Error{} << (_lastError = "Couldn't create the weapons directory.");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if(!Utility::Directory::exists(_stylesDir)) {
|
||||||
|
// Utility::Debug{} << "Styles directory not found, creating...";
|
||||||
|
// if(!Utility::Path::make(_stylesDir)) {
|
||||||
|
// Utility::Error{} << (_lastError = "Couldn't create the styles directory.");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SaveTool::findGameDataDirectory() -> bool {
|
||||||
|
Utility::Debug{} << "Searching for the game's save directory...";
|
||||||
|
|
||||||
|
wchar_t* localappdata_path = nullptr;
|
||||||
|
Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree};
|
||||||
|
if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK)
|
||||||
|
{
|
||||||
|
Utility::Error{} << (_lastError = "SHGetKnownFolderPath() failed in SaveTool::findGameDataDirectory()"_s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_gameDataDir = Utility::Path::join(Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder"_s);
|
||||||
|
|
||||||
|
if(!Utility::Path::exists(_gameDataDir)) {
|
||||||
|
Utility::Error{} << (_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_configDir = Utility::Path::join(_gameDataDir, "Saved/Config/WindowsNoEditor"_s);
|
||||||
|
_saveDir = Utility::Path::join(_gameDataDir, "Saved/SaveGames"_s);
|
||||||
|
_screenshotsDir = Utility::Path::join(_gameDataDir, "Saved/Screenshots/WindowsNoEditor"_s);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::initialiseMassManager() {
|
||||||
|
_massManager.emplace(_saveDir, _currentProfile->account(), _currentProfile->isDemo(), _stagingDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::initialiseFileWatcher() {
|
||||||
|
_fileWatcher.emplace();
|
||||||
|
_watchIDs[SaveDir] = _fileWatcher->addWatch(_saveDir, this, false);
|
||||||
|
_watchIDs[StagingDir] = _fileWatcher->addWatch(_stagingDir, this, false);
|
||||||
|
_fileWatcher->watch();
|
||||||
|
}
|
|
@ -502,10 +502,79 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<C
|
||||||
drawTooltip("WARNING: accessory mapping is a WIP.");
|
drawTooltip("WARNING: accessory mapping is a WIP.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SAVETOOL_DEBUG_BUILD
|
ImGui::SameLine();
|
||||||
ImGui::SameLine(0.0f, ImGui::GetStyle().FramePadding.x * 5.0f);
|
|
||||||
ImGui::Text("Attach index: %i", accessory.attachIndex);
|
static Int tab = 0;
|
||||||
#endif
|
if(ImGui::SmallButton("Change")) {
|
||||||
|
ImGui::OpenPopup("##AccessoryPopup");
|
||||||
|
if(accessory.id < 1000) {
|
||||||
|
tab = 0;
|
||||||
|
}
|
||||||
|
else if(accessory.id >= 3000) {
|
||||||
|
tab = 3;
|
||||||
|
}
|
||||||
|
else if(accessory.id >= 2000) {
|
||||||
|
tab = 2;
|
||||||
|
}
|
||||||
|
else if(accessory.id >= 1000) {
|
||||||
|
tab = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ImGui::BeginPopup("##AccessoryPopup")) {
|
||||||
|
Float selectable_width = 90.0f;
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f});
|
||||||
|
if(ImGui::Selectable("Primitives", tab == 0, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||||
|
tab = 0;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Selectable("Armours", tab == 1, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||||
|
tab = 1;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Selectable("Components", tab == 2, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||||
|
tab = 2;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::Selectable("Connectors", tab == 3, ImGuiSelectableFlags_DontClosePopups, {selectable_width, 0.0f})) {
|
||||||
|
tab = 3;
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if(ImGui::BeginListBox("##AccessoryListbox", {-1.0f, 0.0f})) {
|
||||||
|
for(const auto& acc : accessories) {
|
||||||
|
if(acc.first >= tab * 1000 && acc.first < ((tab + 1) * 1000)) {
|
||||||
|
if(ImGui::Selectable(acc.second.data(), acc.first == accessory.id)) {
|
||||||
|
accessory.id = acc.first;
|
||||||
|
accessory.attachIndex = 0;
|
||||||
|
}
|
||||||
|
if(acc.first == accessory.id) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndListBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(accessory.id > 0) {
|
||||||
|
ImGui::SameLine();
|
||||||
|
if(ImGui::SmallButton("Unequip")) {
|
||||||
|
accessory.id = 0;
|
||||||
|
accessory.attachIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
drawAlignedText("Styles:");
|
drawAlignedText("Styles:");
|
||||||
|
|
162
src/SaveTool/SaveTool_UpdateChecker.cpp
Normal file
162
src/SaveTool/SaveTool_UpdateChecker.cpp
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
// MassBuilderSaveTool
|
||||||
|
// Copyright (C) 2021-2022 Guillaume Jacquemin
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <Corrade/Utility/Format.h>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "SaveTool.h"
|
||||||
|
|
||||||
|
void SaveTool::updateCheckEvent(SDL_Event& event) {
|
||||||
|
_updateThread.join();
|
||||||
|
|
||||||
|
if(event.user.code == CurlInitFailed) {
|
||||||
|
_queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(event.user.code == CurlError) {
|
||||||
|
Containers::String error{static_cast<char*>(event.user.data2), CURL_ERROR_SIZE, nullptr};
|
||||||
|
_queue.addToast(Toast::Type::Error, error, std::chrono::milliseconds{5000});
|
||||||
|
_queue.addToast(Toast::Type::Error, static_cast<char*>(event.user.data1), std::chrono::milliseconds{5000});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(event.user.code == CurlTimeout) {
|
||||||
|
_queue.addToast(Toast::Type::Error, "The request timed out."_s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(event.user.code != 200) {
|
||||||
|
_queue.addToast(Toast::Type::Error, Utility::format("The request failed with error code {}", event.user.code));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Version {
|
||||||
|
explicit Version(Containers::StringView str) {
|
||||||
|
std::size_t start_point = 0;
|
||||||
|
|
||||||
|
if(str[0] == 'v') {
|
||||||
|
start_point++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto components = Containers::StringView{str.data() + start_point}.split('.');
|
||||||
|
|
||||||
|
major = std::strtol(components[0].data(), nullptr, 10);
|
||||||
|
minor = std::strtol(components[1].data(), nullptr, 10);
|
||||||
|
patch = std::strtol(components[2].data(), nullptr, 10);
|
||||||
|
|
||||||
|
fullVersion = major * 10000 + minor * 100 + patch;
|
||||||
|
|
||||||
|
if(str.hasSuffix("-pre")) {
|
||||||
|
prerelease = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Int fullVersion;
|
||||||
|
Int major = 0;
|
||||||
|
Int minor = 0;
|
||||||
|
Int patch = 0;
|
||||||
|
bool prerelease = false;
|
||||||
|
|
||||||
|
bool operator==(const Version& other) const {
|
||||||
|
return fullVersion == other.fullVersion;
|
||||||
|
}
|
||||||
|
bool operator>(const Version& other) const {
|
||||||
|
if((fullVersion > other.fullVersion) ||
|
||||||
|
(fullVersion == other.fullVersion && prerelease == false && other.prerelease == true))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operator Containers::String() const {
|
||||||
|
return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : "");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Version current_ver{SAVETOOL_VERSION};
|
||||||
|
|
||||||
|
Containers::String response{static_cast<char*>(event.user.data1), strlen(static_cast<char*>(event.user.data1)), nullptr};
|
||||||
|
auto components = response.split('\n');
|
||||||
|
|
||||||
|
Version latest_ver{components.front()};
|
||||||
|
|
||||||
|
if(latest_ver > current_ver) {
|
||||||
|
_queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information."_s,
|
||||||
|
std::chrono::milliseconds{5000});
|
||||||
|
_updateAvailable = true;
|
||||||
|
_latestVersion = latest_ver;
|
||||||
|
_releaseLink = Utility::format("https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases/tag/v{}", components.front());
|
||||||
|
_downloadLink = components.back();
|
||||||
|
}
|
||||||
|
else if(latest_ver == current_ver || (current_ver > latest_ver && current_ver.prerelease == true)) {
|
||||||
|
_queue.addToast(Toast::Type::Success, "The application is already up to date."_s);
|
||||||
|
}
|
||||||
|
else if(current_ver > latest_ver && current_ver.prerelease == false) {
|
||||||
|
_queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"_s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto writeData(char* ptr, std::size_t size, std::size_t nmemb, Containers::String* buf)-> std::size_t {
|
||||||
|
if(!ptr || !buf) return 0;
|
||||||
|
(*buf) = Utility::format("{}{}", *buf, Containers::StringView{ptr, size * nmemb});
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveTool::checkForUpdates() {
|
||||||
|
SDL_Event event;
|
||||||
|
SDL_zero(event);
|
||||||
|
event.type = _updateEventId;
|
||||||
|
|
||||||
|
auto curl = curl_easy_init();
|
||||||
|
if(!curl) {
|
||||||
|
event.user.code = CurlInitFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(curl) {
|
||||||
|
Containers::String response_body{Containers::AllocatedInit, ""};
|
||||||
|
Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE * 2};
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L);
|
||||||
|
|
||||||
|
auto code = curl_easy_perform(curl);
|
||||||
|
|
||||||
|
if(code == CURLE_OK) {
|
||||||
|
long status = 0;
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
|
||||||
|
event.user.code = Int(status);
|
||||||
|
event.user.data1 = response_body.release();
|
||||||
|
}
|
||||||
|
else if(code == CURLE_OPERATION_TIMEDOUT) {
|
||||||
|
event.user.code = CurlTimeout;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
event.user.code = CurlError;
|
||||||
|
event.user.data1 = const_cast<char*>(curl_easy_strerror(code));
|
||||||
|
event.user.data2 = Containers::String{error_buffer}.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include <Corrade/Containers/ArrayView.h>
|
|
||||||
#include <Corrade/Containers/Optional.h>
|
#include <Corrade/Containers/Optional.h>
|
||||||
|
#include <Corrade/Utility/Format.h>
|
||||||
#include <Corrade/Utility/Path.h>
|
#include <Corrade/Utility/Path.h>
|
||||||
|
|
||||||
#include "BinaryReader.h"
|
#include "BinaryReader.h"
|
||||||
|
@ -52,6 +52,10 @@ auto UESaveFile::reloadData() -> bool {
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto UESaveFile::saveType() -> Containers::StringView {
|
||||||
|
return _saveType;
|
||||||
|
}
|
||||||
|
|
||||||
void UESaveFile::appendProperty(UnrealPropertyBase::ptr prop) {
|
void UESaveFile::appendProperty(UnrealPropertyBase::ptr prop) {
|
||||||
auto none_prop = std::move(_properties.back());
|
auto none_prop = std::move(_properties.back());
|
||||||
_properties.back() = std::move(prop);
|
_properties.back() = std::move(prop);
|
||||||
|
@ -62,9 +66,6 @@ auto UESaveFile::props() -> Containers::ArrayView<UnrealPropertyBase::ptr> {
|
||||||
return _properties;
|
return _properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <Corrade/Containers/StringStl.h>
|
|
||||||
|
|
||||||
auto UESaveFile::saveToFile() -> bool {
|
auto UESaveFile::saveToFile() -> bool {
|
||||||
BinaryWriter writer{_filepath + ".tmp"_s};
|
BinaryWriter writer{_filepath + ".tmp"_s};
|
||||||
|
|
||||||
|
@ -192,7 +193,7 @@ void UESaveFile::loadData() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayReserve(_customFormatData, custom_format_data_size);
|
_customFormatData = Containers::Array<CustomFormatDataEntry>{custom_format_data_size};
|
||||||
|
|
||||||
for(UnsignedInt i = 0; i < custom_format_data_size; i++) {
|
for(UnsignedInt i = 0; i < custom_format_data_size; i++) {
|
||||||
CustomFormatDataEntry entry;
|
CustomFormatDataEntry entry;
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include <Corrade/Containers/Array.h>
|
#include <Corrade/Containers/ArrayView.h>
|
||||||
#include <Corrade/Containers/GrowableArray.h>
|
#include <Corrade/Containers/GrowableArray.h>
|
||||||
#include <Corrade/Containers/Reference.h>
|
#include <Corrade/Containers/Reference.h>
|
||||||
#include <Corrade/Containers/StaticArray.h>
|
#include <Corrade/Containers/StaticArray.h>
|
||||||
|
@ -41,6 +41,8 @@ class UESaveFile {
|
||||||
|
|
||||||
auto reloadData() -> bool;
|
auto reloadData() -> bool;
|
||||||
|
|
||||||
|
auto saveType() -> Containers::StringView;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::enable_if_t<std::is_base_of<UnrealPropertyBase, T>::value, T*>
|
std::enable_if_t<std::is_base_of<UnrealPropertyBase, T>::value, T*>
|
||||||
at(Containers::StringView name) {
|
at(Containers::StringView name) {
|
||||||
|
|
Loading…
Reference in a new issue