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_drawAbout.cpp
|
||||
SaveTool/SaveTool_drawMainMenu.cpp
|
||||
SaveTool/SaveTool_FileWatcher.cpp
|
||||
SaveTool/SaveTool_Initialisation.cpp
|
||||
SaveTool/SaveTool_MainManager.cpp
|
||||
SaveTool/SaveTool_MassViewer.cpp
|
||||
SaveTool/SaveTool_MassViewer_Frame.cpp
|
||||
SaveTool/SaveTool_MassViewer_Armour.cpp
|
||||
SaveTool/SaveTool_MassViewer_Weapons.cpp
|
||||
SaveTool/SaveTool_ProfileManager.cpp
|
||||
SaveTool/SaveTool_UpdateChecker.cpp
|
||||
ProfileManager/ProfileManager.h
|
||||
ProfileManager/ProfileManager.cpp
|
||||
Profile/Profile.h
|
||||
|
|
|
@ -27,7 +27,7 @@ using namespace Containers::Literals;
|
|||
using namespace Magnum;
|
||||
|
||||
static const std::map<Int, Containers::StringView> accessories {
|
||||
// Primitives
|
||||
// region Primitives
|
||||
{1, "Cube (S)"_s},
|
||||
{2, "Pentagon (S)"_s},
|
||||
{3, "Hexagon (S)"_s},
|
||||
|
@ -48,6 +48,11 @@ static const std::map<Int, Containers::StringView> accessories {
|
|||
{18, "Decal Pad 03 (S)"_s},
|
||||
{19, "Decal Pad 04 (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},
|
||||
{52, "TriBevel (S)"_s},
|
||||
|
@ -70,9 +75,556 @@ static const std::map<Int, Containers::StringView> accessories {
|
|||
{69, "CofEmboss (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);
|
||||
if(!unit_data) {
|
||||
Utility::Error{} << "Couldn't find unit data in" << _filename;
|
||||
|
|
|
@ -106,6 +106,10 @@ void Profile::refreshValues() {
|
|||
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);
|
||||
if(!name_prop) {
|
||||
_lastError = "No company name in "_s + _filename;
|
||||
|
|
|
@ -16,13 +16,7 @@
|
|||
|
||||
#include "SaveTool.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <Corrade/Containers/Pair.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 <Magnum/GL/DebugOutput.h>
|
||||
|
@ -35,15 +29,10 @@
|
|||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <windef.h>
|
||||
#include <winuser.h>
|
||||
#include <processthreadsapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include "../FontAwesome/IconsFontAwesome5.h"
|
||||
#include "../FontAwesome/IconsFontAwesome5Brands.h"
|
||||
|
||||
using namespace Containers::Literals;
|
||||
|
||||
|
@ -204,39 +193,6 @@ SaveTool::~SaveTool() {
|
|||
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() {
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
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() {
|
||||
_imgui.newFrame();
|
||||
|
||||
|
@ -895,55 +451,3 @@ void SaveTool::checkGameState() {
|
|||
_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.");
|
||||
}
|
||||
|
||||
#ifdef SAVETOOL_DEBUG_BUILD
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().FramePadding.x * 5.0f);
|
||||
ImGui::Text("Attach index: %i", accessory.attachIndex);
|
||||
#endif
|
||||
ImGui::SameLine();
|
||||
|
||||
static Int tab = 0;
|
||||
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();
|
||||
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
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/Optional.h>
|
||||
#include <Corrade/Utility/Format.h>
|
||||
#include <Corrade/Utility/Path.h>
|
||||
|
||||
#include "BinaryReader.h"
|
||||
|
@ -52,6 +52,10 @@ auto UESaveFile::reloadData() -> bool {
|
|||
return valid();
|
||||
}
|
||||
|
||||
auto UESaveFile::saveType() -> Containers::StringView {
|
||||
return _saveType;
|
||||
}
|
||||
|
||||
void UESaveFile::appendProperty(UnrealPropertyBase::ptr prop) {
|
||||
auto none_prop = std::move(_properties.back());
|
||||
_properties.back() = std::move(prop);
|
||||
|
@ -62,9 +66,6 @@ auto UESaveFile::props() -> Containers::ArrayView<UnrealPropertyBase::ptr> {
|
|||
return _properties;
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include <Corrade/Containers/StringStl.h>
|
||||
|
||||
auto UESaveFile::saveToFile() -> bool {
|
||||
BinaryWriter writer{_filepath + ".tmp"_s};
|
||||
|
||||
|
@ -192,7 +193,7 @@ void UESaveFile::loadData() {
|
|||
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++) {
|
||||
CustomFormatDataEntry entry;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// 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/Array.h>
|
||||
#include <Corrade/Containers/ArrayView.h>
|
||||
#include <Corrade/Containers/GrowableArray.h>
|
||||
#include <Corrade/Containers/Reference.h>
|
||||
#include <Corrade/Containers/StaticArray.h>
|
||||
|
@ -41,6 +41,8 @@ class UESaveFile {
|
|||
|
||||
auto reloadData() -> bool;
|
||||
|
||||
auto saveType() -> Containers::StringView;
|
||||
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_base_of<UnrealPropertyBase, T>::value, T*>
|
||||
at(Containers::StringView name) {
|
||||
|
|
Loading…
Reference in a new issue