mirror of
				https://github.com/hedge-dev/UnleashedRecomp.git
				synced 2025-10-30 07:11:05 +00:00 
			
		
		
		
	Implement installer with support for ISO, STFS and SVOD. Also implement XEX Patcher. (#5)
This commit is contained in:
		
							parent
							
								
									d36aa26bac
								
							
						
					
					
						commit
						3215e47279
					
				
					 40 changed files with 13171 additions and 11 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							|  | @ -7,3 +7,6 @@ | |||
| [submodule "thirdparty/ShaderRecomp"] | ||||
| 	path = thirdparty/ShaderRecomp | ||||
| 	url = https://github.com/hedge-dev/ShaderRecomp.git | ||||
| [submodule "thirdparty/libmspack"] | ||||
| 	path = thirdparty/libmspack | ||||
| 	url = https://github.com/kyz/libmspack | ||||
|  |  | |||
|  | @ -79,6 +79,30 @@ set(SWA_UI_CXX_SOURCES | |||
|     "ui/window.cpp" | ||||
| ) | ||||
| 
 | ||||
| set(SWA_INSTALL_CXX_SOURCES | ||||
|     "install/installer.cpp" | ||||
|     "install/iso_file_system.cpp" | ||||
|     "install/memory_mapped_file.cpp" | ||||
|     "install/xcontent_file_system.cpp" | ||||
|     "install/xex_patcher.cpp" | ||||
|     "install/hashes/apotos_shamar.cpp" | ||||
|     "install/hashes/chunnan.cpp" | ||||
|     "install/hashes/empire_city_adabat.cpp" | ||||
|     "install/hashes/game.cpp" | ||||
|     "install/hashes/holoska.cpp" | ||||
|     "install/hashes/mazuri.cpp" | ||||
|     "install/hashes/spagonia.cpp" | ||||
|     "install/hashes/update.cpp" | ||||
| ) | ||||
| 
 | ||||
| set(LIBMSPACK_PATH ${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack) | ||||
| 
 | ||||
| set(LIBMSPACK_C_SOURCES | ||||
|     ${LIBMSPACK_PATH}/lzxd.c | ||||
| ) | ||||
| 
 | ||||
| set_source_files_properties(${LIBMSPACK_C_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON) | ||||
| 
 | ||||
| set(SMOLV_SOURCE_DIR "${SWA_THIRDPARTY_ROOT}/ShaderRecomp/thirdparty/smol-v/source") | ||||
| 
 | ||||
| set(SWA_CXX_SOURCES | ||||
|  | @ -95,7 +119,8 @@ set(SWA_CXX_SOURCES | |||
|     ${SWA_HID_CXX_SOURCES} | ||||
|     ${SWA_PATCHES_CXX_SOURCES} | ||||
|     ${SWA_UI_CXX_SOURCES} | ||||
| 
 | ||||
|     ${SWA_INSTALL_CXX_SOURCES} | ||||
|     ${LIBMSPACK_C_SOURCES} | ||||
|     "${SMOLV_SOURCE_DIR}/smolv.cpp" | ||||
| ) | ||||
| 
 | ||||
|  | @ -127,6 +152,7 @@ find_package(Stb REQUIRED) | |||
| find_package(unofficial-concurrentqueue REQUIRED) | ||||
| find_package(imgui CONFIG REQUIRED) | ||||
| find_package(magic_enum CONFIG REQUIRED) | ||||
| find_package(unofficial-tiny-aes-c CONFIG REQUIRED) | ||||
| 
 | ||||
| file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) | ||||
| add_custom_command(TARGET UnleashedRecomp POST_BUILD | ||||
|  | @ -163,12 +189,15 @@ target_link_libraries(UnleashedRecomp PRIVATE | |||
|     Synchronization | ||||
|     imgui::imgui | ||||
|     magic_enum::magic_enum | ||||
|     unofficial::tiny-aes-c::tiny-aes-c | ||||
| ) | ||||
| 
 | ||||
| target_include_directories(UnleashedRecomp PRIVATE | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR} | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/api | ||||
|     ${SWA_THIRDPARTY_ROOT}/ddspp  | ||||
|     ${SWA_THIRDPARTY_ROOT}/ddspp | ||||
|     ${SWA_THIRDPARTY_ROOT}/TinySHA1 | ||||
|     ${LIBMSPACK_PATH} | ||||
|     ${Stb_INCLUDE_DIR} | ||||
|     ${SMOLV_SOURCE_DIR} | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										65
									
								
								UnleashedRecomp/install/directory_file_system.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								UnleashedRecomp/install/directory_file_system.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <filesystem> | ||||
| 
 | ||||
| #include "virtual_file_system.h" | ||||
| 
 | ||||
| struct DirectoryFileSystem : VirtualFileSystem | ||||
| { | ||||
|     std::filesystem::path directoryPath; | ||||
| 
 | ||||
|     DirectoryFileSystem(const std::filesystem::path &directoryPath) | ||||
|     { | ||||
|         this->directoryPath = directoryPath; | ||||
|     } | ||||
| 
 | ||||
|     bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override | ||||
|     { | ||||
|         std::ifstream fileStream(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str()))), std::ios::binary); | ||||
|         if (fileStream.is_open()) | ||||
|         { | ||||
|             fileStream.read((char *)(fileData), fileDataMaxByteCount); | ||||
|             return !fileStream.bad(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     size_t getSize(const std::string &path) const override | ||||
|     { | ||||
|         std::error_code ec; | ||||
|         size_t fileSize = std::filesystem::file_size(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str()))), ec); | ||||
|         if (!ec) | ||||
|         { | ||||
|             return fileSize; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool exists(const std::string &path) const override | ||||
|     { | ||||
|         if (path.empty()) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return std::filesystem::exists(directoryPath / std::filesystem::path(std::u8string_view((const char8_t *)(path.c_str())))); | ||||
|     } | ||||
| 
 | ||||
|     static std::unique_ptr<VirtualFileSystem> create(const std::filesystem::path &directoryPath) | ||||
|     { | ||||
|         if (std::filesystem::exists(directoryPath)) | ||||
|         { | ||||
|             return std::make_unique<DirectoryFileSystem>(directoryPath); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return nullptr; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										297
									
								
								UnleashedRecomp/install/hashes/apotos_shamar.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								UnleashedRecomp/install/hashes/apotos_shamar.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,297 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t ApotosShamarHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> ApotosShamarFiles[]; | ||||
| extern const size_t ApotosShamarFilesSize; | ||||
| 
 | ||||
| const uint64_t ApotosShamarHashes[] = { | ||||
|     5161782899687054855ULL, | ||||
|     10848638096963692019ULL, | ||||
|     9473129523580090227ULL, | ||||
|     14507476319569850326ULL, | ||||
|     7984304958810014204ULL, | ||||
|     17285119940264119717ULL, | ||||
|     2388648365684266872ULL, | ||||
|     7072117265505496768ULL, | ||||
|     7300804046379422574ULL, | ||||
|     11024341213616898389ULL, | ||||
|     2269719192208918419ULL, | ||||
|     11277103027538800966ULL, | ||||
|     3503444980189584178ULL, | ||||
|     4165664752696059934ULL, | ||||
|     7145613245284078465ULL, | ||||
|     8956692682572097337ULL, | ||||
|     467673557758517075ULL, | ||||
|     5865394160581371462ULL, | ||||
|     2388648365684266872ULL, | ||||
|     7072117265505496768ULL, | ||||
|     4651933531844120545ULL, | ||||
|     16366432125719702966ULL, | ||||
|     6204045984421832366ULL, | ||||
|     7455071839519300924ULL, | ||||
|     1601475090088501102ULL, | ||||
|     6476812967046504881ULL, | ||||
|     10110818077789761124ULL, | ||||
|     12473493932267894529ULL, | ||||
|     192734448958995711ULL, | ||||
|     4979345902860045881ULL, | ||||
|     2388648365684266872ULL, | ||||
|     7072117265505496768ULL, | ||||
|     7354426500253318588ULL, | ||||
|     13436608285401838874ULL, | ||||
|     5455808863724849103ULL, | ||||
|     13912048957214708709ULL, | ||||
|     3577115447410096717ULL, | ||||
|     9167108726914458134ULL, | ||||
|     269064862329040576ULL, | ||||
|     13733247789252130917ULL, | ||||
|     1908075175783995052ULL, | ||||
|     17569164706309240857ULL, | ||||
|     2687257361293581496ULL, | ||||
|     15703575149297583018ULL, | ||||
|     13596487015352587574ULL, | ||||
|     15226982905233731417ULL, | ||||
|     7318040371083150448ULL, | ||||
|     14626638887488090768ULL, | ||||
|     1919723919369387380ULL, | ||||
|     17373239717886557228ULL, | ||||
|     8218928162239233105ULL, | ||||
|     12366520936282200873ULL, | ||||
|     10467517237606579536ULL, | ||||
|     11504181969379809881ULL, | ||||
|     913379962765093751ULL, | ||||
|     13184977455228849096ULL, | ||||
|     994534313191316773ULL, | ||||
|     1161598282996799627ULL, | ||||
|     8028384967928659483ULL, | ||||
|     9167275059460261476ULL, | ||||
|     12329170430565474464ULL, | ||||
|     14614493918698922602ULL, | ||||
|     8835009808275053059ULL, | ||||
|     9227869334598201915ULL, | ||||
|     6637379186446622324ULL, | ||||
|     9397642730708705852ULL, | ||||
|     10630484262545617745ULL, | ||||
|     10797068556411890259ULL, | ||||
|     6361872786799807915ULL, | ||||
|     7387444947377556158ULL, | ||||
|     8230353453873378338ULL, | ||||
|     12403999963358558507ULL, | ||||
|     232254748410986870ULL, | ||||
|     13070561742440061598ULL, | ||||
|     10958136070207580182ULL, | ||||
|     15790689719855919515ULL, | ||||
|     14585362614190785186ULL, | ||||
|     17761667637871173377ULL, | ||||
|     9234510757818947078ULL, | ||||
|     15427471912569301087ULL, | ||||
|     9320397015075772917ULL, | ||||
|     5202741620127019351ULL, | ||||
|     6987027253392466959ULL, | ||||
|     5818570466966793416ULL, | ||||
|     11818189498451458264ULL, | ||||
|     8227907329904191071ULL, | ||||
|     8485734709208759762ULL, | ||||
|     7322832926430412082ULL, | ||||
|     252821791924020792ULL, | ||||
|     10750524204772481581ULL, | ||||
|     9618439767240157908ULL, | ||||
|     6136980604454707359ULL, | ||||
|     7851876072887711328ULL, | ||||
|     7126123337902320477ULL, | ||||
|     16732438051503528656ULL, | ||||
|     9626404695949647173ULL, | ||||
|     14630178898376646697ULL, | ||||
|     18071570395979705668ULL, | ||||
|     14290188031476453509ULL, | ||||
|     5082129473071351035ULL, | ||||
|     226647428045111345ULL, | ||||
|     11645732680053564481ULL, | ||||
|     5363987804539005071ULL, | ||||
|     15998860607353466394ULL, | ||||
|     9167368126469307383ULL, | ||||
|     2744550015698589133ULL, | ||||
|     10260598734212408368ULL, | ||||
|     2300802232392089767ULL, | ||||
|     10949176600743863488ULL, | ||||
|     1118205454506633106ULL, | ||||
|     5969867951434607877ULL, | ||||
|     6671314176692954687ULL, | ||||
|     14530713737956734950ULL, | ||||
|     2004571052229566869ULL, | ||||
|     9708958471851417571ULL, | ||||
|     2142992686316263474ULL, | ||||
|     7168715241575146342ULL, | ||||
|     1976917309517312226ULL, | ||||
|     18038718853485103300ULL, | ||||
|     7044835724604442413ULL, | ||||
|     15531130920102660109ULL, | ||||
|     4462669497430426931ULL, | ||||
|     13428469011561492337ULL, | ||||
|     3214238300098346592ULL, | ||||
|     16176599498653256643ULL, | ||||
|     5326927580226746490ULL, | ||||
|     15047534843332950162ULL, | ||||
|     11636822788287294319ULL, | ||||
|     12554314680841874379ULL, | ||||
|     13981752751371535335ULL, | ||||
|     12254474770223375774ULL, | ||||
|     7961887113788376965ULL, | ||||
|     5360854891214543592ULL, | ||||
|     9184741739495532010ULL, | ||||
|     1210820029035423219ULL, | ||||
|     3779694736357686174ULL, | ||||
|     7289857685129858177ULL, | ||||
|     17552766072613442781ULL, | ||||
|     878947465360421687ULL, | ||||
|     7265659051859092189ULL, | ||||
|     11427964425552083037ULL, | ||||
|     12982062630479208399ULL, | ||||
|     11441566204903265308ULL, | ||||
|     14705280727085431572ULL, | ||||
|     455364757374368469ULL, | ||||
|     5573968245771556687ULL, | ||||
|     1250381038431950088ULL, | ||||
|     2410996856079368646ULL, | ||||
|     5259209343569281884ULL, | ||||
|     8112015537375387576ULL, | ||||
|     6878422699214478955ULL, | ||||
|     8949331867975860441ULL, | ||||
|     1985690755616441830ULL, | ||||
|     9351064623690489389ULL, | ||||
|     8896435929557957139ULL, | ||||
|     11542596339951462459ULL, | ||||
|     1985690755616441830ULL, | ||||
|     9351064623690489389ULL, | ||||
|     3185071971562944151ULL, | ||||
|     9949170060646427581ULL, | ||||
|     3811554303412706475ULL, | ||||
|     13858258875735338844ULL, | ||||
|     11056011717482380676ULL, | ||||
|     16815456012040865536ULL, | ||||
|     7271530458034274660ULL, | ||||
|     13079014928873717024ULL, | ||||
|     5685165921037143623ULL, | ||||
|     18141751546323874463ULL, | ||||
|     6983633615275701371ULL, | ||||
|     13085359633765678358ULL, | ||||
|     6682199648914577543ULL, | ||||
|     16715523554730788031ULL, | ||||
|     9945450437595332438ULL, | ||||
|     14162527193937280238ULL, | ||||
|     2442142878432699094ULL, | ||||
|     9645678184877844291ULL, | ||||
|     10217667783481469232ULL, | ||||
|     12548875758281895729ULL, | ||||
| }; | ||||
| 
 | ||||
| const std::pair<const char *, uint32_t> ApotosShamarFiles[] = { | ||||
|     { "#ActD_SubMykonos_02.ar.00", 2 }, | ||||
|     { "#ActD_SubMykonos_02.arl", 2 }, | ||||
|     { "#ActD_SubMykonos_03.ar.00", 2 }, | ||||
|     { "#ActD_SubMykonos_03.arl", 2 }, | ||||
|     { "#ActD_SubMykonos_04.ar.00", 2 }, | ||||
|     { "#ActD_SubMykonos_04.arl", 2 }, | ||||
|     { "#ActD_SubMykonos_05.ar.00", 2 }, | ||||
|     { "#ActD_SubMykonos_05.arl", 2 }, | ||||
|     { "#ActD_SubMykonos_06.ar.00", 2 }, | ||||
|     { "#ActD_SubMykonos_06.arl", 2 }, | ||||
|     { "#ActD_SubPetra_02.ar.00", 2 }, | ||||
|     { "#ActD_SubPetra_02.arl", 2 }, | ||||
|     { "#ActD_SubPetra_04.ar.00", 2 }, | ||||
|     { "#ActD_SubPetra_04.arl", 2 }, | ||||
|     { "#ActN_SubMykonos_02.ar.00", 2 }, | ||||
|     { "#ActN_SubMykonos_02.arl", 2 }, | ||||
|     { "#ActN_SubMykonos_03.ar.00", 2 }, | ||||
|     { "#ActN_SubMykonos_03.arl", 2 }, | ||||
|     { "#ActN_SubMykonos_04.ar.00", 2 }, | ||||
|     { "#ActN_SubMykonos_04.arl", 2 }, | ||||
|     { "#ActN_SubPetra_02.ar.00", 2 }, | ||||
|     { "#ActN_SubPetra_02.arl", 2 }, | ||||
|     { "#ActN_SubPetra_03.ar.00", 2 }, | ||||
|     { "#ActN_SubPetra_03.arl", 2 }, | ||||
|     { "#Application.ar.00", 2 }, | ||||
|     { "#Application.arl", 2 }, | ||||
|     { "ActD_SubMykonos_02.ar.00", 2 }, | ||||
|     { "ActD_SubMykonos_02.arl", 2 }, | ||||
|     { "ActD_SubPetra_02.ar.00", 2 }, | ||||
|     { "ActD_SubPetra_02.arl", 2 }, | ||||
|     { "ActN_SubMykonos_03.ar.00", 2 }, | ||||
|     { "ActN_SubMykonos_03.arl", 2 }, | ||||
|     { "ActN_SubMykonos_04.ar.00", 2 }, | ||||
|     { "ActN_SubMykonos_04.arl", 2 }, | ||||
|     { "ActN_SubPetra_02.ar.00", 2 }, | ||||
|     { "ActN_SubPetra_02.ar.01", 2 }, | ||||
|     { "ActN_SubPetra_02.arl", 2 }, | ||||
|     { "ActN_SubPetra_03.ar.00", 2 }, | ||||
|     { "ActN_SubPetra_03.arl", 2 }, | ||||
|     { "Additional/ActD_MykonosAct1/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_MykonosAct2/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_Petra/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubMykonos_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubMykonos_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubPetra_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubPetra_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubPetra_03/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_MykonosEvil/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubMykonos_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/BossPetra/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Event_M0_06_myk/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Event_M6_01_temple/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Event_M8_16_myk/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ExStageTails1/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ExStageTails2/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_Mykonos/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_MykonosETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_MykonosETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_Mykonos_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_PetraCapital/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_PetraCapitalETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_PetraCapitalETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_PetraCapital_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_PetraLabo/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_PetraLabo_Night/Stage-Add.pfd", 1 }, | ||||
|     { "DLC.xml", 1 }, | ||||
|     { "Languages/English/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/English/WorldMap.arl", 2 }, | ||||
|     { "Languages/French/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/French/WorldMap.arl", 2 }, | ||||
|     { "Languages/German/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/German/WorldMap.arl", 2 }, | ||||
|     { "Languages/Italian/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Italian/WorldMap.arl", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.arl", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.arl", 2 }, | ||||
|     { "Packed/ActD_SubMykonos_02/Stage.pfd", 1 }, | ||||
|     { "Packed/ActD_SubPetra_02/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubPetra_02/Stage.pfd", 1 }, | ||||
|     { "WorldMap.ar.00", 2 }, | ||||
|     { "WorldMap.arl", 2 }, | ||||
|     { "work/ActD_MykonosAct1/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_MykonosAct2/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_Petra/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubMykonos_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubPetra_03/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_MykonosEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_PetraEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SubMykonos_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/Event_M8_16_myk/Terrain.prm.xml", 2 }, | ||||
|     { "work/Event_temple/Terrain.prm.xml", 2 }, | ||||
|     { "work/StaffRoll/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_Mykonos/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_MykonosETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_MykonosETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_Mykonos_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_PetraCapital/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_PetraCapitalETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_PetraCapitalETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_PetraCapital_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_PetraLabo/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_PetraLabo_Night/Terrain.prm.xml", 2 }, | ||||
| }; | ||||
| 
 | ||||
| const size_t ApotosShamarFilesSize = std::size(ApotosShamarFiles); | ||||
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/apotos_shamar.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/apotos_shamar.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t ApotosShamarHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> ApotosShamarFiles[]; | ||||
| extern const size_t ApotosShamarFilesSize; | ||||
| 
 | ||||
							
								
								
									
										211
									
								
								UnleashedRecomp/install/hashes/chunnan.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								UnleashedRecomp/install/hashes/chunnan.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,211 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t ChunnanHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> ChunnanFiles[]; | ||||
| extern const size_t ChunnanFilesSize; | ||||
| 
 | ||||
| const uint64_t ChunnanHashes[] = { | ||||
|     4916287666769501565ULL, | ||||
|     11970519140178822377ULL, | ||||
|     1036602527484778521ULL, | ||||
|     14798433086191189521ULL, | ||||
|     10646557629458609476ULL, | ||||
|     10713656744393722857ULL, | ||||
|     763540214073867667ULL, | ||||
|     3077520808973995505ULL, | ||||
|     3650022750694984496ULL, | ||||
|     17646887566993148783ULL, | ||||
|     5922433511303640285ULL, | ||||
|     6042200465914054108ULL, | ||||
|     10857350734717065794ULL, | ||||
|     14989231061973888057ULL, | ||||
|     1039624040955487627ULL, | ||||
|     14044814300419760090ULL, | ||||
|     5803112655842355018ULL, | ||||
|     6547906886493755080ULL, | ||||
|     2653940158411132556ULL, | ||||
|     6001214908824901338ULL, | ||||
|     12495882025135198599ULL, | ||||
|     12639105290841965223ULL, | ||||
|     8006441485547456063ULL, | ||||
|     17680405693218986079ULL, | ||||
|     787195009054753821ULL, | ||||
|     6894519290185704169ULL, | ||||
|     8761940786378535518ULL, | ||||
|     14409827195925710801ULL, | ||||
|     9932979175820467217ULL, | ||||
|     17354914869214236495ULL, | ||||
|     7393046528943419306ULL, | ||||
|     10318961341152276648ULL, | ||||
|     15227553401250387405ULL, | ||||
|     16697499576601945061ULL, | ||||
|     2725724513251979147ULL, | ||||
|     2959262702089257674ULL, | ||||
|     13005022849156685393ULL, | ||||
|     15761951700770049152ULL, | ||||
|     1543933356731858728ULL, | ||||
|     11918274797446795400ULL, | ||||
|     8565138620607953150ULL, | ||||
|     12303972293118122590ULL, | ||||
|     3894262225055971808ULL, | ||||
|     15748923097789202806ULL, | ||||
|     7445113579724918237ULL, | ||||
|     9352139437145177775ULL, | ||||
|     13354327698439508785ULL, | ||||
|     14395299404885352996ULL, | ||||
|     1717181541369804485ULL, | ||||
|     13580134637576898558ULL, | ||||
|     7869123835192762123ULL, | ||||
|     17965909040401498617ULL, | ||||
|     8052995111566596790ULL, | ||||
|     12914551890455320561ULL, | ||||
|     4117682086191216416ULL, | ||||
|     6338289071186062965ULL, | ||||
|     5814176097875431103ULL, | ||||
|     14826385593100095233ULL, | ||||
|     10541047035071991113ULL, | ||||
|     6190214339491074327ULL, | ||||
|     10842054931648009233ULL, | ||||
|     5939715546527939616ULL, | ||||
|     7892483490571988259ULL, | ||||
|     5706834027745974113ULL, | ||||
|     5279702511609911835ULL, | ||||
|     9429007351441093145ULL, | ||||
|     9003299904399604102ULL, | ||||
|     17387825652548649779ULL, | ||||
|     15599237468898715461ULL, | ||||
|     9636847268497273922ULL, | ||||
|     6553990919897350057ULL, | ||||
|     13702604427410293191ULL, | ||||
|     2951702790388654203ULL, | ||||
|     11840626484159722915ULL, | ||||
|     2545196762095857746ULL, | ||||
|     3245381935646466890ULL, | ||||
|     14924004818419014054ULL, | ||||
|     17307026898787526320ULL, | ||||
|     505706103741995403ULL, | ||||
|     1910775063020669898ULL, | ||||
|     7076255593390973200ULL, | ||||
|     12112449858466219362ULL, | ||||
|     14742510104650318633ULL, | ||||
|     18061657763370298501ULL, | ||||
|     481489007567700054ULL, | ||||
|     16400619344256757493ULL, | ||||
|     2276480650877074293ULL, | ||||
|     17657490758851414958ULL, | ||||
|     10375576356736793341ULL, | ||||
|     11978364073664959090ULL, | ||||
|     6962787816486957283ULL, | ||||
|     8945389641396629891ULL, | ||||
|     10333276603165380084ULL, | ||||
|     10766868630468389150ULL, | ||||
|     13638842475286731623ULL, | ||||
|     7586779683423382399ULL, | ||||
|     7959765828778244231ULL, | ||||
|     809929751227234334ULL, | ||||
|     2646002627166349152ULL, | ||||
|     11780180320854559998ULL, | ||||
|     13855197810473133678ULL, | ||||
|     5061966727887963421ULL, | ||||
|     8831329945074475835ULL, | ||||
|     1092659380955694324ULL, | ||||
|     8406669250393168781ULL, | ||||
|     1210820029035423219ULL, | ||||
|     3779694736357686174ULL, | ||||
|     8953286534157132517ULL, | ||||
|     15926591678820634559ULL, | ||||
|     6892281684169093025ULL, | ||||
|     11798154340507794264ULL, | ||||
|     3383667935658249174ULL, | ||||
|     8090374027476087947ULL, | ||||
|     1344287774476569420ULL, | ||||
|     16036817376283104799ULL, | ||||
|     15708461104025072747ULL, | ||||
|     17647438730951324648ULL, | ||||
|     3271559932170326270ULL, | ||||
|     3440032660435501456ULL, | ||||
|     15209001562045336524ULL, | ||||
|     16527226617411200894ULL, | ||||
|     16083303942902816114ULL, | ||||
|     16998804538680658636ULL, | ||||
|     211876019454739059ULL, | ||||
|     17900510319720696725ULL, | ||||
| }; | ||||
| 
 | ||||
| const std::pair<const char *, uint32_t> ChunnanFiles[] = { | ||||
|     { "#ActD_SubChina_01.ar.00", 2 }, | ||||
|     { "#ActD_SubChina_01.arl", 2 }, | ||||
|     { "#ActD_SubChina_02.ar.00", 2 }, | ||||
|     { "#ActD_SubChina_02.arl", 2 }, | ||||
|     { "#ActD_SubChina_05.ar.00", 2 }, | ||||
|     { "#ActD_SubChina_05.arl", 2 }, | ||||
|     { "#ActD_SubChina_06.ar.00", 2 }, | ||||
|     { "#ActD_SubChina_06.arl", 2 }, | ||||
|     { "#ActN_SubChina_02.ar.00", 2 }, | ||||
|     { "#ActN_SubChina_02.arl", 2 }, | ||||
|     { "#ActN_SubChina_03.ar.00", 2 }, | ||||
|     { "#ActN_SubChina_03.arl", 2 }, | ||||
|     { "#Application.ar.00", 2 }, | ||||
|     { "#Application.arl", 2 }, | ||||
|     { "#SonicActionCommon_China.ar.00", 2 }, | ||||
|     { "#SonicActionCommon_China.arl", 2 }, | ||||
|     { "ActD_SubChina_01.ar.00", 2 }, | ||||
|     { "ActD_SubChina_01.arl", 2 }, | ||||
|     { "ActD_SubChina_02.ar.00", 2 }, | ||||
|     { "ActD_SubChina_02.arl", 2 }, | ||||
|     { "ActD_SubChina_06.ar.00", 2 }, | ||||
|     { "ActD_SubChina_06.arl", 2 }, | ||||
|     { "ActN_SubChina_02.ar.00", 2 }, | ||||
|     { "ActN_SubChina_02.ar.01", 2 }, | ||||
|     { "ActN_SubChina_02.ar.02", 2 }, | ||||
|     { "ActN_SubChina_02.arl", 2 }, | ||||
|     { "ActN_SubChina_03.ar.00", 2 }, | ||||
|     { "ActN_SubChina_03.arl", 2 }, | ||||
|     { "Additional/ActD_China/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubChina_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubChina_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubChina_03/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubChina_04/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_ChinaEvil/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubChina_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubChina_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_China/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_ChinaETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_ChinaETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_China_Night/Stage-Add.pfd", 1 }, | ||||
|     { "China.dmy", 1 }, | ||||
|     { "DLC.xml", 1 }, | ||||
|     { "Languages/English/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/English/WorldMap.arl", 2 }, | ||||
|     { "Languages/French/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/French/WorldMap.arl", 2 }, | ||||
|     { "Languages/German/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/German/WorldMap.arl", 2 }, | ||||
|     { "Languages/Italian/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Italian/WorldMap.arl", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.arl", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.arl", 2 }, | ||||
|     { "Packed/ActD_SubChina_01/Stage.pfd", 1 }, | ||||
|     { "Packed/ActD_SubChina_02/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubChina_02/Stage.pfd", 1 }, | ||||
|     { "SonicActionCommon_China.ar.00", 2 }, | ||||
|     { "SonicActionCommon_China.ar.01", 2 }, | ||||
|     { "SonicActionCommon_China.arl", 2 }, | ||||
|     { "WorldMap.ar.00", 2 }, | ||||
|     { "WorldMap.arl", 2 }, | ||||
|     { "work/ActD_China/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubChina_03/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubChina_04/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_ChinaEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SubChina_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_China/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_ChinaETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_ChinaETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_China_Night/Terrain.prm.xml", 2 }, | ||||
| }; | ||||
| 
 | ||||
| const size_t ChunnanFilesSize = std::size(ChunnanFiles); | ||||
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/chunnan.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/chunnan.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t ChunnanHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> ChunnanFiles[]; | ||||
| extern const size_t ChunnanFilesSize; | ||||
| 
 | ||||
							
								
								
									
										301
									
								
								UnleashedRecomp/install/hashes/empire_city_adabat.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								UnleashedRecomp/install/hashes/empire_city_adabat.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,301 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t EmpireCityAdabatHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> EmpireCityAdabatFiles[]; | ||||
| extern const size_t EmpireCityAdabatFilesSize; | ||||
| 
 | ||||
| const uint64_t EmpireCityAdabatHashes[] = { | ||||
|     4618484507458881259ULL, | ||||
|     8182943251133517605ULL, | ||||
|     4541536790551220736ULL, | ||||
|     17406849580710840362ULL, | ||||
|     13446786864141547866ULL, | ||||
|     18394961398924760842ULL, | ||||
|     7232487182893775796ULL, | ||||
|     14381110835230961016ULL, | ||||
|     4274945889038837581ULL, | ||||
|     7808510065756945406ULL, | ||||
|     1610509086024531696ULL, | ||||
|     8803475239916562653ULL, | ||||
|     10167668386744802426ULL, | ||||
|     13956299527641656682ULL, | ||||
|     996767587458817188ULL, | ||||
|     1288788687784703445ULL, | ||||
|     1545319301186643948ULL, | ||||
|     3463553283246285904ULL, | ||||
|     2329886274870127790ULL, | ||||
|     10808429212560901966ULL, | ||||
|     1527041476422438351ULL, | ||||
|     2162146429294502965ULL, | ||||
|     15296556067580089ULL, | ||||
|     16098667829436141937ULL, | ||||
|     744838819720957427ULL, | ||||
|     7423469708976263310ULL, | ||||
|     3529776342881389937ULL, | ||||
|     4461759958287006047ULL, | ||||
|     8807875940438298613ULL, | ||||
|     16843338913535213802ULL, | ||||
|     58256850107590821ULL, | ||||
|     13358292493270923606ULL, | ||||
|     3407070953812175890ULL, | ||||
|     17745212169422172237ULL, | ||||
|     7003077550452454334ULL, | ||||
|     8088383338758466757ULL, | ||||
|     1919723919369387380ULL, | ||||
|     17373239717886557228ULL, | ||||
|     8218928162239233105ULL, | ||||
|     12366520936282200873ULL, | ||||
|     11042839363452044257ULL, | ||||
|     15356169009822064376ULL, | ||||
|     9138597922789664708ULL, | ||||
|     16547445367137541751ULL, | ||||
|     13108520045170224668ULL, | ||||
|     15310644750109062057ULL, | ||||
|     777887060161745667ULL, | ||||
|     13298530423883017862ULL, | ||||
|     10405571379268351804ULL, | ||||
|     17852400551605150551ULL, | ||||
|     8485124434733981977ULL, | ||||
|     12177189339342254810ULL, | ||||
|     3082211076406596763ULL, | ||||
|     6711541791173565389ULL, | ||||
|     1392400380375046924ULL, | ||||
|     8082351014158359034ULL, | ||||
|     9938956644170626460ULL, | ||||
|     16820751035068431553ULL, | ||||
|     6498223167578513931ULL, | ||||
|     15573567680447777044ULL, | ||||
|     818541743678319259ULL, | ||||
|     13162066552036570425ULL, | ||||
|     4470546825230084927ULL, | ||||
|     16933853415047689351ULL, | ||||
|     17047164819104329366ULL, | ||||
|     11164964391272112273ULL, | ||||
|     10214344852494979677ULL, | ||||
|     8749232225003494592ULL, | ||||
|     10588995228676795372ULL, | ||||
|     17946424022152922370ULL, | ||||
|     7684379787895597340ULL, | ||||
|     17137410643095877226ULL, | ||||
|     2892218193839563307ULL, | ||||
|     17360308384459188410ULL, | ||||
|     9618439767240157908ULL, | ||||
|     12538237685986535590ULL, | ||||
|     10783954654629912125ULL, | ||||
|     11628381209642954065ULL, | ||||
|     1492167910272475344ULL, | ||||
|     2464069474108457492ULL, | ||||
|     13481409826628489247ULL, | ||||
|     6970423075361710992ULL, | ||||
|     6267765362270814284ULL, | ||||
|     9012032776579276367ULL, | ||||
|     2744550015698589133ULL, | ||||
|     10260598734212408368ULL, | ||||
|     2300802232392089767ULL, | ||||
|     10949176600743863488ULL, | ||||
|     1118205454506633106ULL, | ||||
|     5969867951434607877ULL, | ||||
|     6671314176692954687ULL, | ||||
|     14530713737956734950ULL, | ||||
|     2004571052229566869ULL, | ||||
|     9708958471851417571ULL, | ||||
|     2142992686316263474ULL, | ||||
|     7168715241575146342ULL, | ||||
|     1976917309517312226ULL, | ||||
|     18038718853485103300ULL, | ||||
|     7044835724604442413ULL, | ||||
|     15531130920102660109ULL, | ||||
|     4462669497430426931ULL, | ||||
|     13428469011561492337ULL, | ||||
|     3214238300098346592ULL, | ||||
|     16176599498653256643ULL, | ||||
|     5326927580226746490ULL, | ||||
|     15047534843332950162ULL, | ||||
|     11636822788287294319ULL, | ||||
|     12554314680841874379ULL, | ||||
|     8706113903054062682ULL, | ||||
|     18207042628229284597ULL, | ||||
|     5213300754710184598ULL, | ||||
|     8479681542825232071ULL, | ||||
|     5360854891214543592ULL, | ||||
|     9184741739495532010ULL, | ||||
|     1210820029035423219ULL, | ||||
|     3779694736357686174ULL, | ||||
|     3837095400824054399ULL, | ||||
|     9794004182896121787ULL, | ||||
|     12515494881385509791ULL, | ||||
|     15063249629029871557ULL, | ||||
|     13061295136017828432ULL, | ||||
|     17824323001364842093ULL, | ||||
|     1014938187367296966ULL, | ||||
|     1452881609817169700ULL, | ||||
|     3555492069376698022ULL, | ||||
|     12805106068875273513ULL, | ||||
|     10312311736007919080ULL, | ||||
|     17030922443332175658ULL, | ||||
|     10695305817718672781ULL, | ||||
|     13804284662675589878ULL, | ||||
|     9089169043898385347ULL, | ||||
|     13234787173486497799ULL, | ||||
|     8604409041610624441ULL, | ||||
|     12146239465411938672ULL, | ||||
|     2721757041275179953ULL, | ||||
|     11198739556793737443ULL, | ||||
|     15712221005744556715ULL, | ||||
|     18094691449976637288ULL, | ||||
|     15547655397439064196ULL, | ||||
|     17270814844989703830ULL, | ||||
|     3140658107263566967ULL, | ||||
|     4592986651443127450ULL, | ||||
|     5456798863030720610ULL, | ||||
|     15886814922191774691ULL, | ||||
|     3949234515704675807ULL, | ||||
|     11955725616840921688ULL, | ||||
|     275537481032843773ULL, | ||||
|     9312649863415127630ULL, | ||||
|     10341982185761145488ULL, | ||||
|     11616013853856219024ULL, | ||||
|     747890237293655409ULL, | ||||
|     16207842125850922960ULL, | ||||
|     7337489688872005573ULL, | ||||
|     9028255494965930705ULL, | ||||
|     15360353024836444627ULL, | ||||
|     16078477132817971317ULL, | ||||
|     3457659339231167623ULL, | ||||
|     15718276630192910248ULL, | ||||
|     9554347366501308641ULL, | ||||
|     9964745841368142774ULL, | ||||
|     12984301129292863167ULL, | ||||
|     15594004427009479633ULL, | ||||
|     8763624932769278216ULL, | ||||
|     9531499636450705927ULL, | ||||
|     6901020296706243673ULL, | ||||
|     6939812494265815615ULL, | ||||
|     3081980174784719188ULL, | ||||
|     17328496666218534737ULL, | ||||
|     2803515411835567103ULL, | ||||
|     4886896048325757224ULL, | ||||
|     16083303942902816114ULL, | ||||
|     16998804538680658636ULL, | ||||
|     4511521600139320416ULL, | ||||
|     13420895488136723132ULL, | ||||
|     16636596484105747721ULL, | ||||
|     18203853164184025820ULL, | ||||
|     15091774185752181796ULL, | ||||
|     16492958331247638847ULL, | ||||
|     3414648039055001172ULL, | ||||
|     16364348268655095621ULL, | ||||
|     3485883307755363459ULL, | ||||
|     10861246708191214529ULL, | ||||
| }; | ||||
| 
 | ||||
| const std::pair<const char *, uint32_t> EmpireCityAdabatFiles[] = { | ||||
|     { "#ActD_SubBeach_01.ar.00", 2 }, | ||||
|     { "#ActD_SubBeach_01.arl", 2 }, | ||||
|     { "#ActD_SubBeach_03.ar.00", 2 }, | ||||
|     { "#ActD_SubBeach_03.arl", 2 }, | ||||
|     { "#ActD_SubBeach_05.ar.00", 2 }, | ||||
|     { "#ActD_SubBeach_05.arl", 2 }, | ||||
|     { "#ActD_SubNY_02.ar.00", 2 }, | ||||
|     { "#ActD_SubNY_02.arl", 2 }, | ||||
|     { "#ActD_SubNY_03.ar.00", 2 }, | ||||
|     { "#ActD_SubNY_03.arl", 2 }, | ||||
|     { "#ActN_SubBeach_02.ar.00", 2 }, | ||||
|     { "#ActN_SubBeach_02.arl", 2 }, | ||||
|     { "#ActN_SubBeach_03.ar.00", 2 }, | ||||
|     { "#ActN_SubBeach_03.arl", 2 }, | ||||
|     { "#ActN_SubNY_01.ar.00", 2 }, | ||||
|     { "#ActN_SubNY_01.arl", 2 }, | ||||
|     { "#ActN_SubNY_02.ar.00", 2 }, | ||||
|     { "#ActN_SubNY_02.arl", 2 }, | ||||
|     { "#Application.ar.00", 2 }, | ||||
|     { "#Application.arl", 2 }, | ||||
|     { "ActD_SubBeach_01.ar.00", 2 }, | ||||
|     { "ActD_SubBeach_01.arl", 2 }, | ||||
|     { "ActD_SubBeach_03.ar.00", 2 }, | ||||
|     { "ActD_SubBeach_03.arl", 2 }, | ||||
|     { "ActD_SubBeach_05.ar.00", 2 }, | ||||
|     { "ActD_SubBeach_05.arl", 2 }, | ||||
|     { "ActD_SubNY_02.ar.00", 2 }, | ||||
|     { "ActD_SubNY_02.arl", 2 }, | ||||
|     { "ActN_SubNY_01.ar.00", 2 }, | ||||
|     { "ActN_SubNY_01.ar.01", 2 }, | ||||
|     { "ActN_SubNY_01.arl", 2 }, | ||||
|     { "Additional/ActD_Beach/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_NY/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubBeach_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubBeach_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubBeach_03/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubBeach_04/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubNY_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubNY_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_BeachEvil/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_NYEvil/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubBeach_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubNY_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Event_M6_01_temple/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_NYCity/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_NYCityETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_NYCityETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_NYCity_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_SouthEastAsia/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_SouthEastAsiaETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_SouthEastAsiaETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_SouthEastAsia_Night/Stage-Add.pfd", 1 }, | ||||
|     { "DLC.xml", 1 }, | ||||
|     { "Languages/English/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/English/WorldMap.arl", 2 }, | ||||
|     { "Languages/French/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/French/WorldMap.arl", 2 }, | ||||
|     { "Languages/German/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/German/WorldMap.arl", 2 }, | ||||
|     { "Languages/Italian/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Italian/WorldMap.arl", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.arl", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.arl", 2 }, | ||||
|     { "Packed/ActD_SubBeach_01/Stage.pfd", 1 }, | ||||
|     { "Packed/ActD_SubBeach_03/Stage.pfd", 1 }, | ||||
|     { "Packed/ActD_SubNY_02/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubNY_01/Stage.pfd", 1 }, | ||||
|     { "WorldMap.ar.00", 2 }, | ||||
|     { "WorldMap.arl", 2 }, | ||||
|     { "work/ActD_Beach/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_NY/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubBeach_02/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubBeach_04/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubNY_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_BeachEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_BeachEvil/evl_sea_obj_st_waterCircle.model", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle-0000.texture", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle-0000.uv-anim", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle-0001.texture", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle-0001.uv-anim", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle-0002.texture", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle-0002.uv-anim", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle-0003.texture", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle.material", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle.texset", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_kt_MotionWater_dif.dds", 2 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_kt_MotionWater_env.dds", 2 }, | ||||
|     { "work/ActN_NYEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SubBeach_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SubNY_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/BossEggLancer/Terrain.prm.xml", 2 }, | ||||
|     { "work/Event_M4_01_egb_hideout/Terrain.prm.xml", 2 }, | ||||
|     { "work/Event_M6_01_temple/Terrain.prm.xml", 2 }, | ||||
|     { "work/Event_egb_hidout_exterior/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_NYCity/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_NYCityETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_NYCityETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_NYCity_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_SouthEastAsia/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_SouthEastAsiaETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_SouthEastAsiaETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_SouthEastAsia_Night/Terrain.prm.xml", 2 }, | ||||
| }; | ||||
| 
 | ||||
| const size_t EmpireCityAdabatFilesSize = std::size(EmpireCityAdabatFiles); | ||||
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/empire_city_adabat.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/empire_city_adabat.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t EmpireCityAdabatHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> EmpireCityAdabatFiles[]; | ||||
| extern const size_t EmpireCityAdabatFilesSize; | ||||
| 
 | ||||
							
								
								
									
										8436
									
								
								UnleashedRecomp/install/hashes/game.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8436
									
								
								UnleashedRecomp/install/hashes/game.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/game.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/game.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t GameHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> GameFiles[]; | ||||
| extern const size_t GameFilesSize; | ||||
| 
 | ||||
							
								
								
									
										198
									
								
								UnleashedRecomp/install/hashes/holoska.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								UnleashedRecomp/install/hashes/holoska.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,198 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t HoloskaHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> HoloskaFiles[]; | ||||
| extern const size_t HoloskaFilesSize; | ||||
| 
 | ||||
| const uint64_t HoloskaHashes[] = { | ||||
|     2675323900039702065ULL, | ||||
|     4277145918507575318ULL, | ||||
|     37003434483691695ULL, | ||||
|     173772618141122105ULL, | ||||
|     10014684039097108933ULL, | ||||
|     12951885659934974458ULL, | ||||
|     5420475312037398025ULL, | ||||
|     8990457325890346487ULL, | ||||
|     5828311441007099177ULL, | ||||
|     15895929894456691275ULL, | ||||
|     10944334660972947767ULL, | ||||
|     12306207387774270676ULL, | ||||
|     1133020226384470961ULL, | ||||
|     10978642704870093657ULL, | ||||
|     6571493200930941260ULL, | ||||
|     12775456849440773110ULL, | ||||
|     10993350723710351594ULL, | ||||
|     18266525158058610108ULL, | ||||
|     5224405159313498915ULL, | ||||
|     7030149124581670257ULL, | ||||
|     743509372682842317ULL, | ||||
|     6268515345337947410ULL, | ||||
|     621840898472918401ULL, | ||||
|     7793408857629121219ULL, | ||||
|     3628810224077277661ULL, | ||||
|     4735419277840395906ULL, | ||||
|     6298408288867498004ULL, | ||||
|     9672923561522739380ULL, | ||||
|     8577458973002786949ULL, | ||||
|     8580672557326042084ULL, | ||||
|     905264783309208510ULL, | ||||
|     5442721461459141559ULL, | ||||
|     5665558615477908883ULL, | ||||
|     9138114750084480028ULL, | ||||
|     10560390528404575839ULL, | ||||
|     13571388347857105531ULL, | ||||
|     759267540240315808ULL, | ||||
|     1395028858241468056ULL, | ||||
|     4101501475292075848ULL, | ||||
|     13453093997934088664ULL, | ||||
|     16436427612349862673ULL, | ||||
|     16475130472640188313ULL, | ||||
|     1009427446083976444ULL, | ||||
|     6227277608980222296ULL, | ||||
|     13005423191223021574ULL, | ||||
|     7227050263030209202ULL, | ||||
|     9777930758670277699ULL, | ||||
|     877007860174867856ULL, | ||||
|     18238103507153996510ULL, | ||||
|     17930956651100144422ULL, | ||||
|     9455059678942766083ULL, | ||||
|     11513193284629003124ULL, | ||||
|     14582773429927700438ULL, | ||||
|     7832492987792635365ULL, | ||||
|     14922683551437897222ULL, | ||||
|     2218036959448726520ULL, | ||||
|     5915675802417501794ULL, | ||||
|     4734985573199693091ULL, | ||||
|     13894625369955491525ULL, | ||||
|     10164406532505859673ULL, | ||||
|     11738098787596070522ULL, | ||||
|     2506038633852497536ULL, | ||||
|     3066872557705014675ULL, | ||||
|     13577051447279183181ULL, | ||||
|     15995491349078614893ULL, | ||||
|     7170529702240695373ULL, | ||||
|     9610481190777925774ULL, | ||||
|     9843976124448789757ULL, | ||||
|     15999881082141829716ULL, | ||||
|     1705967380110975175ULL, | ||||
|     1945492401042462005ULL, | ||||
|     12826054609787136918ULL, | ||||
|     14947608392493777252ULL, | ||||
|     8454834516025688631ULL, | ||||
|     16412196874867098033ULL, | ||||
|     1302335439956347422ULL, | ||||
|     5904634235948759380ULL, | ||||
|     837467602803878420ULL, | ||||
|     11838669303496931932ULL, | ||||
|     1026342585625625691ULL, | ||||
|     17104669198514697031ULL, | ||||
|     6226633771929098644ULL, | ||||
|     11434391196365401368ULL, | ||||
|     1825358492785453273ULL, | ||||
|     10935112159483090915ULL, | ||||
|     10368247242044046510ULL, | ||||
|     5360854891214543592ULL, | ||||
|     9184741739495532010ULL, | ||||
|     1210820029035423219ULL, | ||||
|     3779694736357686174ULL, | ||||
|     11026144482164398929ULL, | ||||
|     17624209453030784775ULL, | ||||
|     1268371966612623612ULL, | ||||
|     17620869642251638393ULL, | ||||
|     7500432003580343090ULL, | ||||
|     8925351885022805937ULL, | ||||
|     4545712040089568182ULL, | ||||
|     16087391183543862251ULL, | ||||
|     7153771647341077160ULL, | ||||
|     15835374550237816233ULL, | ||||
|     10806012310881440072ULL, | ||||
|     14061367079592692873ULL, | ||||
|     16856865969950237558ULL, | ||||
|     16918398385542560356ULL, | ||||
|     7838032685469261389ULL, | ||||
|     14087988214801845031ULL, | ||||
|     287254560976480823ULL, | ||||
|     4291829565728637795ULL, | ||||
|     1833361518702399357ULL, | ||||
|     9945701995133200218ULL, | ||||
|     12195458283103638803ULL, | ||||
|     18193324349862755987ULL, | ||||
|     3992878524104250195ULL, | ||||
|     9683778847586532057ULL, | ||||
|     640455243454193529ULL, | ||||
|     9845312495153161302ULL, | ||||
| }; | ||||
| 
 | ||||
| const std::pair<const char *, uint32_t> HoloskaFiles[] = { | ||||
|     { "#ActD_SubSnow_02.ar.00", 2 }, | ||||
|     { "#ActD_SubSnow_02.arl", 2 }, | ||||
|     { "#ActD_SubSnow_03.ar.00", 2 }, | ||||
|     { "#ActD_SubSnow_03.arl", 2 }, | ||||
|     { "#ActD_SubSnow_04.ar.00", 2 }, | ||||
|     { "#ActD_SubSnow_04.arl", 2 }, | ||||
|     { "#ActD_SubSnow_05.ar.00", 2 }, | ||||
|     { "#ActD_SubSnow_05.arl", 2 }, | ||||
|     { "#ActN_SubSnow_01.ar.00", 2 }, | ||||
|     { "#ActN_SubSnow_01.arl", 2 }, | ||||
|     { "#ActN_SubSnow_02.ar.00", 2 }, | ||||
|     { "#ActN_SubSnow_02.arl", 2 }, | ||||
|     { "#Application.ar.00", 2 }, | ||||
|     { "#Application.arl", 2 }, | ||||
|     { "ActD_SubSnow_02.ar.00", 2 }, | ||||
|     { "ActD_SubSnow_02.arl", 2 }, | ||||
|     { "ActD_SubSnow_03.ar.00", 2 }, | ||||
|     { "ActD_SubSnow_03.arl", 2 }, | ||||
|     { "ActN_SubSnow_01.ar.00", 2 }, | ||||
|     { "ActN_SubSnow_01.arl", 2 }, | ||||
|     { "ActN_SubSnow_02.ar.00", 2 }, | ||||
|     { "ActN_SubSnow_02.arl", 2 }, | ||||
|     { "Additional/ActD_Snow/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubSnow_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubSnow_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubSnow_03/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SnowEvil/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubSnow_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubSnow_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/BossDarkGaiaMoray/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_Snow/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_SnowETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_SnowETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_Snow_Night/Stage-Add.pfd", 1 }, | ||||
|     { "DLC.xml", 1 }, | ||||
|     { "Languages/English/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/English/WorldMap.arl", 2 }, | ||||
|     { "Languages/French/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/French/WorldMap.arl", 2 }, | ||||
|     { "Languages/German/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/German/WorldMap.arl", 2 }, | ||||
|     { "Languages/Italian/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Italian/WorldMap.arl", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.arl", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.arl", 2 }, | ||||
|     { "Packed/ActD_SubSnow_02/Stage.pfd", 1 }, | ||||
|     { "Packed/ActD_SubSnow_03/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubSnow_01/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubSnow_02/Stage.pfd", 1 }, | ||||
|     { "Snow.dmy", 1 }, | ||||
|     { "WorldMap.ar.00", 2 }, | ||||
|     { "WorldMap.arl", 2 }, | ||||
|     { "work/ActD_Snow/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubSnow_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SnowEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterSnowDayActionSub02.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterSnowDayActionSub03.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterSnowDayActionSub04.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterSnowDayActionSub05.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterSnowNightActionSub01.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterSnowNightActionSub02.seq.xml", 2 }, | ||||
|     { "work/Town_Snow/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_SnowETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_SnowETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_Snow_Night/Terrain.prm.xml", 2 }, | ||||
| }; | ||||
| 
 | ||||
| const size_t HoloskaFilesSize = std::size(HoloskaFiles); | ||||
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/holoska.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/holoska.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t HoloskaHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> HoloskaFiles[]; | ||||
| extern const size_t HoloskaFilesSize; | ||||
| 
 | ||||
							
								
								
									
										207
									
								
								UnleashedRecomp/install/hashes/mazuri.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								UnleashedRecomp/install/hashes/mazuri.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,207 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t MazuriHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> MazuriFiles[]; | ||||
| extern const size_t MazuriFilesSize; | ||||
| 
 | ||||
| const uint64_t MazuriHashes[] = { | ||||
|     8310122492208490409ULL, | ||||
|     14334946716403322379ULL, | ||||
|     6359932846637962320ULL, | ||||
|     15564392554394963521ULL, | ||||
|     4145580004644844748ULL, | ||||
|     5886649247807377109ULL, | ||||
|     8972853618768766550ULL, | ||||
|     16274784949553698740ULL, | ||||
|     7644922894813285241ULL, | ||||
|     16792340405196435317ULL, | ||||
|     7219363224850733712ULL, | ||||
|     14783821508665540659ULL, | ||||
|     90578118658326728ULL, | ||||
|     322485799435034401ULL, | ||||
|     11426516458306753692ULL, | ||||
|     16875540838976300804ULL, | ||||
|     691543308196576824ULL, | ||||
|     12748185913217986160ULL, | ||||
|     181497269294558465ULL, | ||||
|     13384184373605717208ULL, | ||||
|     9680733245723597610ULL, | ||||
|     11319512019915225753ULL, | ||||
|     5156260857743175296ULL, | ||||
|     17024263853193085482ULL, | ||||
|     9161916068418754052ULL, | ||||
|     15099371411684664534ULL, | ||||
|     8811039586506246550ULL, | ||||
|     13344975442165302144ULL, | ||||
|     11616101478448229001ULL, | ||||
|     16890442647900693500ULL, | ||||
|     10613805266180977971ULL, | ||||
|     12232891231073729485ULL, | ||||
|     5827659615582798219ULL, | ||||
|     10044950643067304005ULL, | ||||
|     10405571379268351804ULL, | ||||
|     17852400551605150551ULL, | ||||
|     8485124434733981977ULL, | ||||
|     12177189339342254810ULL, | ||||
|     6914067631133882133ULL, | ||||
|     8516330542230824299ULL, | ||||
|     5227234300746783898ULL, | ||||
|     11316739451124421549ULL, | ||||
|     4006017088443300321ULL, | ||||
|     8144466479720785800ULL, | ||||
|     14807147190400377590ULL, | ||||
|     17032461497328235598ULL, | ||||
|     3485364854384528759ULL, | ||||
|     12314750532364104301ULL, | ||||
|     18171772732457610158ULL, | ||||
|     15511223977167516261ULL, | ||||
|     3733997387449100578ULL, | ||||
|     3127614471276641962ULL, | ||||
|     16473237911443301576ULL, | ||||
|     8519032476298984207ULL, | ||||
|     1282659255712538566ULL, | ||||
|     9032400057270978659ULL, | ||||
|     1438891209736948095ULL, | ||||
|     9445272093697834862ULL, | ||||
|     17042589027198683899ULL, | ||||
|     17834939632591927531ULL, | ||||
|     17963312833258248676ULL, | ||||
|     11212796985379953935ULL, | ||||
|     8520618928746918706ULL, | ||||
|     12620044629892189723ULL, | ||||
|     17231920840550500769ULL, | ||||
|     14751571373966723331ULL, | ||||
|     15192962011256471386ULL, | ||||
|     5641128099468064081ULL, | ||||
|     15353897587627765076ULL, | ||||
|     9694642992346267925ULL, | ||||
|     12849306083654146943ULL, | ||||
|     12611788236218891748ULL, | ||||
|     13815021343500055451ULL, | ||||
|     17210166018872705038ULL, | ||||
|     17856279508890497700ULL, | ||||
|     15236163838001295487ULL, | ||||
|     15463039469068597027ULL, | ||||
|     3188082504873605346ULL, | ||||
|     10762266561438768212ULL, | ||||
|     842630621715284677ULL, | ||||
|     1961824913476492580ULL, | ||||
|     5152443868555296771ULL, | ||||
|     10324715709102329399ULL, | ||||
|     1763695362403970051ULL, | ||||
|     3584532033227831137ULL, | ||||
|     5851561062160893814ULL, | ||||
|     10927370304760079285ULL, | ||||
|     13011799481510971519ULL, | ||||
|     17677776816961090167ULL, | ||||
|     13923999678594617040ULL, | ||||
|     5360854891214543592ULL, | ||||
|     9184741739495532010ULL, | ||||
|     1210820029035423219ULL, | ||||
|     3779694736357686174ULL, | ||||
|     45507338756965322ULL, | ||||
|     17034560519917147503ULL, | ||||
|     615638415508415245ULL, | ||||
|     12821211946029354361ULL, | ||||
|     173839052608557733ULL, | ||||
|     6298694170641867436ULL, | ||||
|     12773486509288875531ULL, | ||||
|     12866025506444541977ULL, | ||||
|     3427216651702511068ULL, | ||||
|     8503894857294129869ULL, | ||||
|     8658756214229842885ULL, | ||||
|     12517977842558558441ULL, | ||||
|     8772357560481233720ULL, | ||||
|     9298201679300251287ULL, | ||||
|     1413776131937655221ULL, | ||||
|     16241630498833235183ULL, | ||||
|     3107620193063708762ULL, | ||||
|     18350383620420541255ULL, | ||||
|     108006630585092978ULL, | ||||
|     171415973667919914ULL, | ||||
|     1817978790908311521ULL, | ||||
|     7165219417836909758ULL, | ||||
|     5462363977947174456ULL, | ||||
|     8840489652156575872ULL, | ||||
|     10345266752933627695ULL, | ||||
|     15321193045322335042ULL, | ||||
|     471637519459967124ULL, | ||||
|     6817966383869009387ULL, | ||||
| }; | ||||
| 
 | ||||
| const std::pair<const char *, uint32_t> MazuriFiles[] = { | ||||
|     { "#ActD_SubAfrica_02.ar.00", 2 }, | ||||
|     { "#ActD_SubAfrica_02.arl", 2 }, | ||||
|     { "#ActD_SubAfrica_04.ar.00", 2 }, | ||||
|     { "#ActD_SubAfrica_04.arl", 2 }, | ||||
|     { "#ActD_SubAfrica_05.ar.00", 2 }, | ||||
|     { "#ActD_SubAfrica_05.arl", 2 }, | ||||
|     { "#ActD_SubAfrica_06.ar.00", 2 }, | ||||
|     { "#ActD_SubAfrica_06.arl", 2 }, | ||||
|     { "#ActN_SubAfrica_02.ar.00", 2 }, | ||||
|     { "#ActN_SubAfrica_02.arl", 2 }, | ||||
|     { "#ActN_SubAfrica_03.ar.00", 2 }, | ||||
|     { "#ActN_SubAfrica_03.arl", 2 }, | ||||
|     { "#Application.ar.00", 2 }, | ||||
|     { "#Application.arl", 2 }, | ||||
|     { "ActD_SubAfrica_02.ar.00", 2 }, | ||||
|     { "ActD_SubAfrica_02.ar.01", 2 }, | ||||
|     { "ActD_SubAfrica_02.arl", 2 }, | ||||
|     { "ActD_SubAfrica_06.ar.00", 2 }, | ||||
|     { "ActD_SubAfrica_06.arl", 2 }, | ||||
|     { "ActN_SubAfrica_02.ar.00", 2 }, | ||||
|     { "ActN_SubAfrica_02.arl", 2 }, | ||||
|     { "ActN_SubAfrica_03.ar.00", 2 }, | ||||
|     { "ActN_SubAfrica_03.ar.01", 2 }, | ||||
|     { "ActN_SubAfrica_03.arl", 2 }, | ||||
|     { "Additional/ActD_Africa/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubAfrica_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubAfrica_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubAfrica_03/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_AfricaEvil/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubAfrica_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubAfrica_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubAfrica_03/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/BossEggBeetle/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Event_afr_hideout/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_Africa/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_AfricaETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_AfricaETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_Africa_Night/Stage-Add.pfd", 1 }, | ||||
|     { "DLC.xml", 1 }, | ||||
|     { "Languages/English/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/English/WorldMap.arl", 2 }, | ||||
|     { "Languages/French/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/French/WorldMap.arl", 2 }, | ||||
|     { "Languages/German/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/German/WorldMap.arl", 2 }, | ||||
|     { "Languages/Italian/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Italian/WorldMap.arl", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.arl", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.arl", 2 }, | ||||
|     { "Packed/ActD_SubAfrica_02/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubAfrica_02/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubAfrica_03/Stage.pfd", 1 }, | ||||
|     { "WorldMap.ar.00", 2 }, | ||||
|     { "WorldMap.arl", 2 }, | ||||
|     { "work/ActD_Africa/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubAfrica_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubAfrica_02/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubAfrica_03/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_AfricaEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SubAfrica_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SubAfrica_02/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_SubAfrica_03/Terrain.prm.xml", 2 }, | ||||
|     { "work/BossEggBeetle/Terrain.prm.xml", 2 }, | ||||
|     { "work/Event_afr_hideout/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_Africa/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_AfricaETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_AfricaETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_Africa_Night/Terrain.prm.xml", 2 }, | ||||
| }; | ||||
| 
 | ||||
| const size_t MazuriFilesSize = std::size(MazuriFiles); | ||||
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/mazuri.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/mazuri.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t MazuriHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> MazuriFiles[]; | ||||
| extern const size_t MazuriFilesSize; | ||||
| 
 | ||||
							
								
								
									
										207
									
								
								UnleashedRecomp/install/hashes/spagonia.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								UnleashedRecomp/install/hashes/spagonia.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,207 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t SpagoniaHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> SpagoniaFiles[]; | ||||
| extern const size_t SpagoniaFilesSize; | ||||
| 
 | ||||
| const uint64_t SpagoniaHashes[] = { | ||||
|     14867694217129324738ULL, | ||||
|     17100842773888706873ULL, | ||||
|     9752656502718674446ULL, | ||||
|     15671159208413511075ULL, | ||||
|     1812603898025740639ULL, | ||||
|     12613450881360770457ULL, | ||||
|     1154935869782185816ULL, | ||||
|     8219769246658808506ULL, | ||||
|     9695895323794780829ULL, | ||||
|     15823038424497033867ULL, | ||||
|     5817217669193198611ULL, | ||||
|     6497441520538379134ULL, | ||||
|     10139383215379183246ULL, | ||||
|     13620914181664931155ULL, | ||||
|     4668974212184368466ULL, | ||||
|     10500741838109391509ULL, | ||||
|     647311874653915105ULL, | ||||
|     7661921675863949967ULL, | ||||
|     323394983647000743ULL, | ||||
|     797187618373021610ULL, | ||||
|     353294754399582215ULL, | ||||
|     16740492856758014010ULL, | ||||
|     1952614254672843308ULL, | ||||
|     2667940006400122486ULL, | ||||
|     12564343012022085590ULL, | ||||
|     16154216407056286457ULL, | ||||
|     6298408288867498004ULL, | ||||
|     9672923561522739380ULL, | ||||
|     14834906762895569303ULL, | ||||
|     16854111345280229225ULL, | ||||
|     4837640393588267349ULL, | ||||
|     16098597153130008996ULL, | ||||
|     12600320757740560262ULL, | ||||
|     12939475623775993690ULL, | ||||
|     10477103196696189363ULL, | ||||
|     12732657904265336737ULL, | ||||
|     13095277609263620069ULL, | ||||
|     16750231076154973610ULL, | ||||
|     2022042165098050912ULL, | ||||
|     17839881966578661233ULL, | ||||
|     5534774080348504073ULL, | ||||
|     14030101124033407301ULL, | ||||
|     6718729617636463209ULL, | ||||
|     12440200088588141894ULL, | ||||
|     6369118057772660528ULL, | ||||
|     7315135301096821941ULL, | ||||
|     7602114569538282692ULL, | ||||
|     11336647640667052949ULL, | ||||
|     10152111535756487098ULL, | ||||
|     5265464758157500744ULL, | ||||
|     5234945774616460434ULL, | ||||
|     10795075404967515916ULL, | ||||
|     13538969501315954190ULL, | ||||
|     17102608468773106653ULL, | ||||
|     14121317735511216399ULL, | ||||
|     5157803794135169420ULL, | ||||
|     7199840892697349122ULL, | ||||
|     1225096793739555247ULL, | ||||
|     9128945470099535129ULL, | ||||
|     4734985573199693091ULL, | ||||
|     13894625369955491525ULL, | ||||
|     10164406532505859673ULL, | ||||
|     11738098787596070522ULL, | ||||
|     2506038633852497536ULL, | ||||
|     3066872557705014675ULL, | ||||
|     13577051447279183181ULL, | ||||
|     15995491349078614893ULL, | ||||
|     7170529702240695373ULL, | ||||
|     9610481190777925774ULL, | ||||
|     9843976124448789757ULL, | ||||
|     15999881082141829716ULL, | ||||
|     1705967380110975175ULL, | ||||
|     1945492401042462005ULL, | ||||
|     12826054609787136918ULL, | ||||
|     14947608392493777252ULL, | ||||
|     8454834516025688631ULL, | ||||
|     16412196874867098033ULL, | ||||
|     1302335439956347422ULL, | ||||
|     5904634235948759380ULL, | ||||
|     837467602803878420ULL, | ||||
|     11838669303496931932ULL, | ||||
|     1026342585625625691ULL, | ||||
|     17104669198514697031ULL, | ||||
|     3709548792784456596ULL, | ||||
|     16703932146892522057ULL, | ||||
|     3247313303103704726ULL, | ||||
|     5360854891214543592ULL, | ||||
|     9184741739495532010ULL, | ||||
|     1210820029035423219ULL, | ||||
|     3779694736357686174ULL, | ||||
|     1269303531989512502ULL, | ||||
|     15044160000628794537ULL, | ||||
|     8716580408330791802ULL, | ||||
|     16789543858655671716ULL, | ||||
|     2003346667352944922ULL, | ||||
|     15313758642971562051ULL, | ||||
|     2739790431130553532ULL, | ||||
|     6783670011455890702ULL, | ||||
|     697852188952975609ULL, | ||||
|     17185281492289182605ULL, | ||||
|     5699074391444795502ULL, | ||||
|     6148412876775458906ULL, | ||||
|     2511824900179921974ULL, | ||||
|     9520550565124263974ULL, | ||||
|     1879590205302053732ULL, | ||||
|     16445653421013634259ULL, | ||||
|     163664281434034891ULL, | ||||
|     14992345757081400517ULL, | ||||
|     3472557166861866135ULL, | ||||
|     7070790832398904932ULL, | ||||
|     14233035291283305411ULL, | ||||
|     17367771094700951804ULL, | ||||
|     14233035291283305411ULL, | ||||
|     17367771094700951804ULL, | ||||
|     4805103552407476622ULL, | ||||
|     10627247764815196413ULL, | ||||
|     4664450768495384140ULL, | ||||
|     11104978684669875710ULL, | ||||
|     4664450768495384140ULL, | ||||
|     11104978684669875710ULL, | ||||
|     16106838734002406075ULL, | ||||
|     16965457262918815126ULL, | ||||
| }; | ||||
| 
 | ||||
| const std::pair<const char *, uint32_t> SpagoniaFiles[] = { | ||||
|     { "#ActD_SubEU_03.ar.00", 2 }, | ||||
|     { "#ActD_SubEU_03.arl", 2 }, | ||||
|     { "#ActD_SubEU_04.ar.00", 2 }, | ||||
|     { "#ActD_SubEU_04.arl", 2 }, | ||||
|     { "#ActD_SubEU_05.ar.00", 2 }, | ||||
|     { "#ActD_SubEU_05.arl", 2 }, | ||||
|     { "#ActD_SubEU_06.ar.00", 2 }, | ||||
|     { "#ActD_SubEU_06.arl", 2 }, | ||||
|     { "#ActN_SubEU_01.ar.00", 2 }, | ||||
|     { "#ActN_SubEU_01.arl", 2 }, | ||||
|     { "#ActN_SubEU_02.ar.00", 2 }, | ||||
|     { "#ActN_SubEU_02.arl", 2 }, | ||||
|     { "#Application.ar.00", 2 }, | ||||
|     { "#Application.arl", 2 }, | ||||
|     { "ActD_SubEU_03.ar.00", 2 }, | ||||
|     { "ActD_SubEU_03.ar.01", 2 }, | ||||
|     { "ActD_SubEU_03.arl", 2 }, | ||||
|     { "ActD_SubEU_04.ar.00", 2 }, | ||||
|     { "ActD_SubEU_04.ar.01", 2 }, | ||||
|     { "ActD_SubEU_04.arl", 2 }, | ||||
|     { "ActN_SubEU_01.ar.00", 2 }, | ||||
|     { "ActN_SubEU_01.arl", 2 }, | ||||
|     { "Additional/ActD_EU/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubEU_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubEU_02/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubEU_03/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActD_SubEU_04/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_EUEvil/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/ActN_SubEU_01/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Event_M2_01_professor_room_new/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_EULabo/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_EULabo_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_EuropeanCity/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_EuropeanCityETF/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_EuropeanCityETF_Night/Stage-Add.pfd", 1 }, | ||||
|     { "Additional/Town_EuropeanCity_Night/Stage-Add.pfd", 1 }, | ||||
|     { "DLC.xml", 1 }, | ||||
|     { "Languages/English/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/English/WorldMap.arl", 2 }, | ||||
|     { "Languages/French/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/French/WorldMap.arl", 2 }, | ||||
|     { "Languages/German/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/German/WorldMap.arl", 2 }, | ||||
|     { "Languages/Italian/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Italian/WorldMap.arl", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Japanese/WorldMap.arl", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.ar.00", 2 }, | ||||
|     { "Languages/Spanish/WorldMap.arl", 2 }, | ||||
|     { "Packed/ActD_SubEU_03/Stage.pfd", 1 }, | ||||
|     { "Packed/ActD_SubEU_04/Stage.pfd", 1 }, | ||||
|     { "Packed/ActN_SubEU_01/Stage.pfd", 1 }, | ||||
|     { "WorldMap.ar.00", 2 }, | ||||
|     { "WorldMap.arl", 2 }, | ||||
|     { "work/ActD_EU/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubEU_01/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActD_SubEU_02/Terrain.prm.xml", 2 }, | ||||
|     { "work/ActN_EUEvil/Terrain.prm.xml", 2 }, | ||||
|     { "work/BossEggRayBird/Terrain.prm.xml", 2 }, | ||||
|     { "work/Event_M2_01_professor_room_new/Terrain.prm.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterEUDayActionSub03.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterEUDayActionSub04.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterEUDayActionSub05.seq.xml", 2 }, | ||||
|     { "work/SystemCommon/SR_EnterEUNightActionSub02.seq.xml", 2 }, | ||||
|     { "work/Town_EULabo/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_EULabo_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_EuropeanCity/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_EuropeanCityETF/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_EuropeanCityETF_Night/Terrain.prm.xml", 2 }, | ||||
|     { "work/Town_EuropeanCity_Night/Terrain.prm.xml", 2 }, | ||||
| }; | ||||
| 
 | ||||
| const size_t SpagoniaFilesSize = std::size(SpagoniaFiles); | ||||
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/spagonia.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/spagonia.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t SpagoniaHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> SpagoniaFiles[]; | ||||
| extern const size_t SpagoniaFilesSize; | ||||
| 
 | ||||
							
								
								
									
										89
									
								
								UnleashedRecomp/install/hashes/update.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								UnleashedRecomp/install/hashes/update.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t UpdateHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> UpdateFiles[]; | ||||
| extern const size_t UpdateFilesSize; | ||||
| 
 | ||||
| const uint64_t UpdateHashes[] = { | ||||
|     6914273463875662709ULL, | ||||
|     15542186142639918255ULL, | ||||
|     17773094197787397017ULL, | ||||
|     5694064368761413534ULL, | ||||
|     10660633045276223515ULL, | ||||
|     6259845327508088719ULL, | ||||
|     12140399948272279979ULL, | ||||
|     7418638713822288133ULL, | ||||
|     14117267789244251538ULL, | ||||
|     15188168166630393460ULL, | ||||
|     3161308809281010162ULL, | ||||
|     5322614488283725236ULL, | ||||
|     4606314145682200491ULL, | ||||
|     11955725616840921688ULL, | ||||
|     11797277641169528627ULL, | ||||
|     16412181293791116322ULL, | ||||
|     11038823378333165548ULL, | ||||
|     5479982606338303627ULL, | ||||
|     868771203788675788ULL, | ||||
|     17001824000099573976ULL, | ||||
|     9159616851251538481ULL, | ||||
|     15568568481517853886ULL, | ||||
|     10521442355407577670ULL, | ||||
|     9513615512117450320ULL, | ||||
|     4920031480332452542ULL, | ||||
|     17152633532642839320ULL, | ||||
|     1430448682009298272ULL, | ||||
|     2819842435698853770ULL, | ||||
|     1430448682009298272ULL, | ||||
|     2819842435698853770ULL, | ||||
|     13038055622504309783ULL, | ||||
|     1430448682009298272ULL, | ||||
|     2819842435698853770ULL, | ||||
|     3623875508130760747ULL, | ||||
|     1400141374194792546ULL, | ||||
|     3560111161029067800ULL, | ||||
|     2610060740632518377ULL, | ||||
|     2610060740632518377ULL, | ||||
| }; | ||||
| 
 | ||||
| const std::pair<const char *, uint32_t> UpdateFiles[] = { | ||||
|     { "default.xexp", 3 }, | ||||
|     { "work/ActD_MykonosAct1/Base.set.xml", 1 }, | ||||
|     { "work/ActD_NY/Mission_NYCity_S20_10.set.xml", 1 }, | ||||
|     { "work/ActD_SubEU_01/Set2.set.xml", 1 }, | ||||
|     { "work/ActD_SubNY_01/Set.set.xml", 1 }, | ||||
|     { "work/ActD_SubNY_01/Stage.stg.xml", 1 }, | ||||
|     { "work/ActD_SubNY_01/ny_sub_path.path.xml", 1 }, | ||||
|     { "work/ActD_SubSnow_01/Sub_Layer1_Set.set.xml", 1 }, | ||||
|     { "work/ActD_SubSnow_01/Sub_Layer2_Set.set.xml", 1 }, | ||||
|     { "work/ActD_SubSnow_01/Sub_Layer3_Set.set.xml", 1 }, | ||||
|     { "work/ActN_BeachEvil/evl_sea_obj_st_waterCircle.model", 1 }, | ||||
|     { "work/ActN_BeachEvil/sea_water_circle.material", 1 }, | ||||
|     { "work/ActN_ChinaEvil/area06_enemyset.set.xml", 1 }, | ||||
|     { "work/ActN_ChinaEvil/area07_enemyset.set.xml", 1 }, | ||||
|     { "work/ActN_ChinaEvil/system.set.xml", 1 }, | ||||
|     { "work/ActN_EUEvil/area16_gimmickset.set.xml", 1 }, | ||||
|     { "work/ActN_EUEvil/area18_gimmickset.set.xml", 1 }, | ||||
|     { "work/ActN_EUEvil/system.set.xml", 1 }, | ||||
|     { "work/ActN_PetraEvil/system.set.xml", 1 }, | ||||
|     { "work/ActN_SubChina_01/area01_enemyset.set.xml", 1 }, | ||||
|     { "work/ActN_SubChina_01/area01_gimmickset.set.xml", 1 }, | ||||
|     { "work/Act_EggmanLand/BaseEvil.set.xml", 1 }, | ||||
|     { "work/Act_EggmanLand/system.set.xml", 1 }, | ||||
|     { "work/Application/SR_AdjustTownState.seq.xml", 1 }, | ||||
|     { "work/CmnTown_Mykonos/myk_obj_soc_paperboxABC.dds", 1 }, | ||||
|     { "work/CmnTown_Snow/snw_obj_snowman04_dif.dds", 1 }, | ||||
|     { "work/EvilActionCommon_Mykonos/myk_obj_soc_paperboxABC.dds", 1 }, | ||||
|     { "work/EvilActionCommon_Snow/snw_obj_snowman04_dif.dds", 1 }, | ||||
|     { "work/Inspire/scene/evrt_m5_02/evrt_m5_02.inspire_resource.xml", 1 }, | ||||
|     { "work/SonicActionCommon_Mykonos/myk_obj_soc_paperboxABC.dds", 1 }, | ||||
|     { "work/SonicActionCommon_Snow/snw_obj_snowman04_dif.dds", 1 }, | ||||
|     { "work/Title/mat_mainmenu_common_001.dds", 1 }, | ||||
|     { "work/Town_EuropeanCity_Dispel/Mission_EuropeanCity_S30_10.set.xml", 1 }, | ||||
|     { "work/Town_NYCity_Dispel/Mission_MoveMission_S20_10.set.xml", 1 }, | ||||
|     { "work/Town_SouthEastAsiaETF/CommonOBJ.set.xml", 1 }, | ||||
|     { "work/Town_SouthEastAsiaETF_Night/CommonOBJ.set.xml", 1 }, | ||||
| }; | ||||
| 
 | ||||
| const size_t UpdateFilesSize = std::size(UpdateFiles); | ||||
							
								
								
									
										10
									
								
								UnleashedRecomp/install/hashes/update.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								UnleashedRecomp/install/hashes/update.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| // File automatically generated by fshasher
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| extern const uint64_t UpdateHashes[]; | ||||
| extern const std::pair<const char *, uint32_t> UpdateFiles[]; | ||||
| extern const size_t UpdateFilesSize; | ||||
| 
 | ||||
							
								
								
									
										492
									
								
								UnleashedRecomp/install/installer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										492
									
								
								UnleashedRecomp/install/installer.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,492 @@ | |||
| #include "installer.h" | ||||
| 
 | ||||
| #include <xxh3.h> | ||||
| 
 | ||||
| #include "directory_file_system.h" | ||||
| #include "iso_file_system.h" | ||||
| #include "xcontent_file_system.h" | ||||
| 
 | ||||
| #include "hashes/apotos_shamar.h" | ||||
| #include "hashes/chunnan.h" | ||||
| #include "hashes/empire_city_adabat.h" | ||||
| #include "hashes/game.h" | ||||
| #include "hashes/holoska.h" | ||||
| #include "hashes/mazuri.h" | ||||
| #include "hashes/spagonia.h" | ||||
| #include "hashes/update.h" | ||||
| 
 | ||||
| static const std::string GameDirectory = "game"; | ||||
| static const std::string DLCDirectory = "dlc"; | ||||
| static const std::string ApotosShamarDirectory = DLCDirectory + "/Apotos & Shamar Adventure Pack"; | ||||
| static const std::string ChunnanDirectory = DLCDirectory + "/Chunnan Adventure Pack"; | ||||
| static const std::string EmpireCityAdabatDirectory = DLCDirectory + "/Empire City & Adabat Adventure Pack"; | ||||
| static const std::string HoloskaDirectory = DLCDirectory + "/Holoska Adventure Pack"; | ||||
| static const std::string MazuriDirectory = DLCDirectory + "/Mazuri Adventure Pack"; | ||||
| static const std::string SpagoniaDirectory = DLCDirectory + "/Spagonia Adventure Pack"; | ||||
| static const std::string UpdateDirectory = "update"; | ||||
| static const std::string GameExecutableFile = "default.xex"; | ||||
| static const std::string DLCValidationFile = "DLC.xml"; | ||||
| static const std::string UpdateExecutablePatchFile = "default.xexp"; | ||||
| static const std::string ISOExtension = ".iso"; | ||||
| static const std::string OldExtension = ".old"; | ||||
| static const std::string TempExtension = ".tmp"; | ||||
| 
 | ||||
| static std::string fromU8(const std::u8string &str) | ||||
| { | ||||
|     return std::string(str.begin(), str.end()); | ||||
| } | ||||
| 
 | ||||
| static std::string fromPath(const std::filesystem::path &path) | ||||
| { | ||||
|     return fromU8(path.u8string()); | ||||
| } | ||||
| 
 | ||||
| static std::string toLower(std::string str) { | ||||
|     std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); }); | ||||
|     return str; | ||||
| }; | ||||
| 
 | ||||
| static std::unique_ptr<VirtualFileSystem> createFileSystemFromPath(const std::filesystem::path &path) | ||||
| { | ||||
|     if (XContentFileSystem::check(path)) | ||||
|     { | ||||
|         return XContentFileSystem::create(path); | ||||
|     } | ||||
|     else if (toLower(path.extension().string()) == ISOExtension) | ||||
|     { | ||||
|         return ISOFileSystem::create(path); | ||||
|     } | ||||
|     else if (std::filesystem::is_directory(path)) | ||||
|     { | ||||
|         return DirectoryFileSystem::create(path); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool copyFile(const FilePair &pair, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, bool skipHashChecks, std::vector<uint8_t> &fileData, Journal &journal, const std::function<void(uint32_t)> &progressCallback) { | ||||
|     const std::string filename(pair.first); | ||||
|     const uint32_t hashCount = pair.second; | ||||
|     if (!sourceVfs.exists(filename)) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::FileMissing; | ||||
|         journal.lastErrorMessage = std::format("File {} does not exist in the file system.", filename); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (!sourceVfs.load(filename, fileData)) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::FileReadFailed; | ||||
|         journal.lastErrorMessage = std::format("Failed to read file {} from the file system.", filename); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (!skipHashChecks) | ||||
|     { | ||||
|         uint64_t fileHash = XXH3_64bits(fileData.data(), fileData.size()); | ||||
|         bool fileHashFound = false; | ||||
|         for (uint32_t i = 0; i < hashCount && !fileHashFound; i++) | ||||
|         { | ||||
|             fileHashFound = fileHash == fileHashes[i]; | ||||
|         } | ||||
| 
 | ||||
|         if (!fileHashFound) | ||||
|         { | ||||
|             journal.lastResult = Journal::Result::FileHashFailed; | ||||
|             journal.lastErrorMessage = std::format("File {} from the file system did not match any of the known hashes.", filename); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::filesystem::path targetPath = targetDirectory / std::filesystem::path(std::u8string_view((const char8_t *)(pair.first))); | ||||
|     std::filesystem::path parentPath = targetPath.parent_path(); | ||||
|     if (!std::filesystem::exists(parentPath)) | ||||
|     { | ||||
|         std::filesystem::create_directories(parentPath); | ||||
|     } | ||||
|      | ||||
|     while (!parentPath.empty()) { | ||||
|         journal.createdDirectories.insert(parentPath); | ||||
| 
 | ||||
|         if (parentPath != targetDirectory) { | ||||
|             parentPath = parentPath.parent_path(); | ||||
|         } | ||||
|         else { | ||||
|             parentPath = std::filesystem::path(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::ofstream outStream(targetPath, std::ios::binary); | ||||
|     if (!outStream.is_open()) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::FileCreationFailed; | ||||
|         journal.lastErrorMessage = std::format("Failed to create file at {}.", targetPath.string()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     journal.createdFiles.push_back(targetPath); | ||||
| 
 | ||||
|     outStream.write((const char *)(fileData.data()), fileData.size()); | ||||
|     if (outStream.bad()) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::FileWriteFailed; | ||||
|         journal.lastErrorMessage = std::format("Failed to create file at {}.", targetPath.string()); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     progressCallback(++journal.progressCounter); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static DLC detectDLC(const std::filesystem::path &sourcePath, VirtualFileSystem &sourceVfs, Journal &journal) | ||||
| { | ||||
|     std::vector<uint8_t> dlcXmlBytes; | ||||
|     if (!sourceVfs.load(DLCValidationFile, dlcXmlBytes)) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::FileMissing; | ||||
|         journal.lastErrorMessage = std::format("File {} does not exist in the file system.", DLCValidationFile); | ||||
|         return DLC::Unknown; | ||||
|     } | ||||
| 
 | ||||
|     const char TypeStartString[] = "<Type>"; | ||||
|     const char TypeEndString[] = "</Type>"; | ||||
|     size_t dlcByteCount = dlcXmlBytes.size(); | ||||
|     dlcXmlBytes.resize(dlcByteCount + 1); | ||||
|     dlcXmlBytes[dlcByteCount] = '\0'; | ||||
|     const char *typeStartLocation = strstr((const char *)(dlcXmlBytes.data()), TypeStartString); | ||||
|     const char *typeEndLocation = typeStartLocation != nullptr ? strstr(typeStartLocation, TypeEndString) : nullptr; | ||||
|     if (typeStartLocation == nullptr || typeEndLocation == nullptr) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::DLCParsingFailed; | ||||
|         journal.lastErrorMessage = "Failed to find DLC type for " + sourcePath.string() + "."; | ||||
|         return DLC::Unknown; | ||||
|     } | ||||
| 
 | ||||
|     const char *typeNumberLocation = typeStartLocation + strlen(TypeStartString); | ||||
|     size_t typeNumberCount = typeEndLocation - typeNumberLocation; | ||||
|     if (typeNumberCount != 1) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::UnknownDLCType; | ||||
|         journal.lastErrorMessage = "DLC type for " + sourcePath.string() + " is unknown."; | ||||
|         return DLC::Unknown; | ||||
|     } | ||||
| 
 | ||||
|     switch (*typeNumberLocation) | ||||
|     { | ||||
|     case '1': | ||||
|         return DLC::Spagonia; | ||||
|     case '2': | ||||
|         return DLC::Chunnan; | ||||
|     case '3': | ||||
|         return DLC::Mazuri; | ||||
|     case '4': | ||||
|         return DLC::Holoska; | ||||
|     case '5': | ||||
|         return DLC::ApotosShamar; | ||||
|     case '7': | ||||
|         return DLC::EmpireCityAdabat; | ||||
|     default: | ||||
|         journal.lastResult = Journal::Result::UnknownDLCType; | ||||
|         journal.lastErrorMessage = "DLC type for " + sourcePath.string() + " is unknown."; | ||||
|         return DLC::Unknown; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool Installer::checkGameInstall(const std::filesystem::path &baseDirectory) | ||||
| { | ||||
|     return std::filesystem::exists(baseDirectory / GameDirectory / GameExecutableFile); | ||||
| } | ||||
| 
 | ||||
| bool Installer::copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<void(uint32_t)> &progressCallback) | ||||
| { | ||||
|     if (!std::filesystem::exists(targetDirectory) && !std::filesystem::create_directories(targetDirectory)) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::DirectoryCreationFailed; | ||||
|         journal.lastErrorMessage = "Unable to create directory at " + fromPath(targetDirectory); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     FilePair validationPair = {}; | ||||
|     uint32_t validationHashIndex = 0; | ||||
|     uint32_t hashIndex = 0; | ||||
|     uint32_t hashCount = 0; | ||||
|     std::vector<uint8_t> fileData; | ||||
|     for (FilePair pair : filePairs) | ||||
|     { | ||||
|         hashIndex = hashCount; | ||||
|         hashCount += pair.second; | ||||
| 
 | ||||
|         if (validationFile.compare(pair.first) == 0) | ||||
|         { | ||||
|             validationPair = pair; | ||||
|             validationHashIndex = hashIndex; | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if (!copyFile(pair, &fileHashes[hashIndex], sourceVfs, targetDirectory, skipHashChecks, fileData, journal, progressCallback)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Validation file is copied last after all other files have been copied.
 | ||||
|     if (validationPair.first != nullptr) | ||||
|     { | ||||
|         if (!copyFile(validationPair, &fileHashes[validationHashIndex], sourceVfs, targetDirectory, skipHashChecks, fileData, journal, progressCallback)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::ValidationFileMissing; | ||||
|         journal.lastErrorMessage = std::format("Unable to find validation file {} in file system.", validationFile); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool Installer::parseContent(const std::filesystem::path &sourcePath, std::unique_ptr<VirtualFileSystem> &targetVfs, Journal &journal) | ||||
| { | ||||
|     targetVfs = createFileSystemFromPath(sourcePath); | ||||
|     if (targetVfs != nullptr) | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::VirtualFileSystemFailed; | ||||
|         journal.lastErrorMessage = "Unable to open file system at " + fromPath(sourcePath); | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool Installer::install(const Input &input, const std::filesystem::path &targetDirectory, Journal &journal, const std::function<void(uint32_t)> &progressCallback) | ||||
| { | ||||
|     // Parse the contents of the base game.
 | ||||
|     std::unique_ptr<VirtualFileSystem> gameSource; | ||||
|     if (!input.gameSource.empty()) | ||||
|     { | ||||
|         if (!parseContent(input.gameSource, gameSource, journal)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         journal.progressTotal += GameFilesSize; | ||||
|     } | ||||
| 
 | ||||
|     // Parse the contents of Update.
 | ||||
|     std::unique_ptr<VirtualFileSystem> updateSource; | ||||
|     if (!input.updateSource.empty()) | ||||
|     { | ||||
|         if (!parseContent(input.updateSource, updateSource, journal)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         journal.progressTotal += UpdateFilesSize; | ||||
|     } | ||||
| 
 | ||||
|     // Parse the contents of the DLC Packs.
 | ||||
|     struct DLCSource { | ||||
|         std::unique_ptr<VirtualFileSystem> sourceVfs; | ||||
|         std::span<const FilePair> filePairs; | ||||
|         const uint64_t *fileHashes = nullptr; | ||||
|         std::string targetSubDirectory; | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<DLCSource> dlcSources; | ||||
|     for (const auto &path : input.dlcSources) | ||||
|     { | ||||
|         dlcSources.emplace_back(); | ||||
|         DLCSource &dlcSource = dlcSources.back(); | ||||
|         if (!parseContent(path, dlcSource.sourceVfs, journal)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         DLC dlc = detectDLC(path, *dlcSource.sourceVfs, journal); | ||||
|         switch (dlc) | ||||
|         { | ||||
|         case DLC::Spagonia: | ||||
|             dlcSource.filePairs = { SpagoniaFiles, SpagoniaFilesSize }; | ||||
|             dlcSource.fileHashes = SpagoniaHashes; | ||||
|             dlcSource.targetSubDirectory = SpagoniaDirectory; | ||||
|             break; | ||||
|         case DLC::Chunnan: | ||||
|             dlcSource.filePairs = { ChunnanFiles, ChunnanFilesSize }; | ||||
|             dlcSource.fileHashes = ChunnanHashes; | ||||
|             dlcSource.targetSubDirectory = ChunnanDirectory; | ||||
|             break; | ||||
|         case DLC::Mazuri: | ||||
|             dlcSource.filePairs = { MazuriFiles, MazuriFilesSize }; | ||||
|             dlcSource.fileHashes = MazuriHashes; | ||||
|             dlcSource.targetSubDirectory = MazuriDirectory; | ||||
|             break; | ||||
|         case DLC::Holoska: | ||||
|             dlcSource.filePairs = { HoloskaFiles, HoloskaFilesSize }; | ||||
|             dlcSource.fileHashes = HoloskaHashes; | ||||
|             dlcSource.targetSubDirectory = HoloskaDirectory; | ||||
|             break; | ||||
|         case DLC::ApotosShamar: | ||||
|             dlcSource.filePairs = { ApotosShamarFiles, ApotosShamarFilesSize }; | ||||
|             dlcSource.fileHashes = ApotosShamarHashes; | ||||
|             dlcSource.targetSubDirectory = ApotosShamarDirectory; | ||||
|             break; | ||||
|         case DLC::EmpireCityAdabat: | ||||
|             dlcSource.filePairs = { EmpireCityAdabatFiles, EmpireCityAdabatFilesSize }; | ||||
|             dlcSource.fileHashes = EmpireCityAdabatHashes; | ||||
|             dlcSource.targetSubDirectory = EmpireCityAdabatDirectory; | ||||
|             break; | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         journal.progressTotal += dlcSource.filePairs.size(); | ||||
|     } | ||||
| 
 | ||||
|     // Install the base game.
 | ||||
|     if (!copyFiles({ GameFiles, GameFilesSize }, GameHashes, *gameSource, targetDirectory / GameDirectory, GameExecutableFile, input.skipHashChecks, journal, progressCallback)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Install the update.
 | ||||
|     if (!copyFiles({ UpdateFiles, UpdateFilesSize }, UpdateHashes, *updateSource, targetDirectory / UpdateDirectory, UpdateExecutablePatchFile, input.skipHashChecks, journal, progressCallback)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Patch the executable with the update's file.
 | ||||
|     std::filesystem::path baseXexPath = targetDirectory / GameDirectory / GameExecutableFile; | ||||
|     std::filesystem::path patchPath = targetDirectory / UpdateDirectory / UpdateExecutablePatchFile; | ||||
|     std::filesystem::path patchedXexPath = targetDirectory / GameDirectory / (GameExecutableFile + TempExtension); | ||||
|     XexPatcher::Result patcherResult = XexPatcher::apply(baseXexPath, patchPath, patchedXexPath); | ||||
|     if (patcherResult != XexPatcher::Result::Success) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::PatchProcessFailed; | ||||
|         journal.lastPatcherResult = patcherResult; | ||||
|         journal.lastErrorMessage = "Patch process failed."; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Replace the executable by renaming and deleting in a safe way.
 | ||||
|     std::error_code ec; | ||||
|     std::filesystem::path oldXexPath = targetDirectory / GameDirectory / (GameExecutableFile + OldExtension); | ||||
|     std::filesystem::rename(baseXexPath, oldXexPath, ec); | ||||
|     if (ec) | ||||
|     { | ||||
|         journal.lastResult = Journal::Result::PatchReplacementFailed; | ||||
|         journal.lastErrorMessage = "Failed to rename executable."; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     std::filesystem::rename(patchedXexPath, baseXexPath, ec); | ||||
|     if (ec) | ||||
|     { | ||||
|         std::filesystem::rename(oldXexPath, baseXexPath, ec); | ||||
|         journal.lastResult = Journal::Result::PatchReplacementFailed; | ||||
|         journal.lastErrorMessage = "Failed to rename executable."; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     std::filesystem::remove(oldXexPath); | ||||
| 
 | ||||
|     // Install the DLC.
 | ||||
|     if (!dlcSources.empty()) | ||||
|     { | ||||
|         journal.createdDirectories.insert(targetDirectory / DLCDirectory); | ||||
|     } | ||||
| 
 | ||||
|     for (const DLCSource &dlcSource : dlcSources) | ||||
|     { | ||||
|         if (!copyFiles(dlcSource.filePairs, dlcSource.fileHashes, *dlcSource.sourceVfs, targetDirectory / dlcSource.targetSubDirectory, DLCValidationFile, input.skipHashChecks, journal, progressCallback)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void Installer::rollback(Journal &journal) | ||||
| { | ||||
|     std::error_code ec; | ||||
|     for (const auto &path : journal.createdFiles) | ||||
|     { | ||||
|         std::filesystem::remove(path, ec); | ||||
|     } | ||||
| 
 | ||||
|     for (auto it = journal.createdDirectories.rbegin(); it != journal.createdDirectories.rend(); it++) | ||||
|     { | ||||
|         std::filesystem::remove(*it, ec); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool Installer::parseGame(const std::filesystem::path &sourcePath) | ||||
| { | ||||
|     std::unique_ptr<VirtualFileSystem> sourceVfs = createFileSystemFromPath(sourcePath); | ||||
|     if (sourceVfs == nullptr) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return sourceVfs->exists(GameExecutableFile); | ||||
| } | ||||
| 
 | ||||
| bool Installer::parseUpdate(const std::filesystem::path &sourcePath) | ||||
| { | ||||
|     std::unique_ptr<VirtualFileSystem> sourceVfs = createFileSystemFromPath(sourcePath); | ||||
|     if (sourceVfs == nullptr) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return sourceVfs->exists(UpdateExecutablePatchFile); | ||||
| } | ||||
| 
 | ||||
| DLC Installer::parseDLC(const std::filesystem::path &sourcePath) | ||||
| { | ||||
|     Journal journal; | ||||
|     std::unique_ptr<VirtualFileSystem> sourceVfs = createFileSystemFromPath(sourcePath); | ||||
|     if (sourceVfs == nullptr) | ||||
|     { | ||||
|         return DLC::Unknown; | ||||
|     } | ||||
| 
 | ||||
|     return detectDLC(sourcePath, *sourceVfs, journal); | ||||
| } | ||||
| 
 | ||||
| XexPatcher::Result Installer::checkGameUpdateCompatibility(const std::filesystem::path &gameSourcePath, const std::filesystem::path &updateSourcePath) | ||||
| { | ||||
|     std::unique_ptr<VirtualFileSystem> gameSourceVfs = createFileSystemFromPath(gameSourcePath); | ||||
|     if (gameSourceVfs == nullptr) | ||||
|     { | ||||
|         return XexPatcher::Result::FileOpenFailed; | ||||
|     } | ||||
| 
 | ||||
|     std::unique_ptr<VirtualFileSystem> updateSourceVfs = createFileSystemFromPath(updateSourcePath); | ||||
|     if (updateSourceVfs == nullptr) | ||||
|     { | ||||
|         return XexPatcher::Result::FileOpenFailed; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<uint8_t> xexBytes; | ||||
|     std::vector<uint8_t> patchBytes; | ||||
|     if (!gameSourceVfs->load(GameExecutableFile, xexBytes)) | ||||
|     { | ||||
|         return XexPatcher::Result::FileOpenFailed; | ||||
|     } | ||||
| 
 | ||||
|     if (!updateSourceVfs->load(UpdateExecutablePatchFile, patchBytes)) | ||||
|     { | ||||
|         return XexPatcher::Result::FileOpenFailed; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<uint8_t> patchedBytes; | ||||
|     return XexPatcher::apply(xexBytes, patchBytes, patchedBytes, true); | ||||
| } | ||||
							
								
								
									
										76
									
								
								UnleashedRecomp/install/installer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								UnleashedRecomp/install/installer.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <span> | ||||
| #include <set> | ||||
| 
 | ||||
| #include "virtual_file_system.h" | ||||
| #include "xex_patcher.h" | ||||
| 
 | ||||
| enum class DLC { | ||||
|     Unknown, | ||||
|     Spagonia, | ||||
|     Chunnan, | ||||
|     Mazuri, | ||||
|     Holoska, | ||||
|     ApotosShamar, | ||||
|     EmpireCityAdabat | ||||
| }; | ||||
| 
 | ||||
| struct Journal | ||||
| { | ||||
|     enum class Result | ||||
|     { | ||||
|         Success, | ||||
|         VirtualFileSystemFailed, | ||||
|         DirectoryCreationFailed, | ||||
|         FileMissing, | ||||
|         FileReadFailed, | ||||
|         FileHashFailed, | ||||
|         FileCreationFailed, | ||||
|         FileWriteFailed, | ||||
|         ValidationFileMissing, | ||||
|         DLCParsingFailed, | ||||
|         PatchProcessFailed, | ||||
|         PatchReplacementFailed, | ||||
|         UnknownDLCType | ||||
|     }; | ||||
| 
 | ||||
|     uint32_t progressCounter = 0; | ||||
|     uint32_t progressTotal = 0; | ||||
|     std::list<std::filesystem::path> createdFiles; | ||||
|     std::set<std::filesystem::path> createdDirectories; | ||||
|     Result lastResult = Result::Success; | ||||
|     XexPatcher::Result lastPatcherResult = XexPatcher::Result::Success; | ||||
|     std::string lastErrorMessage; | ||||
| }; | ||||
| 
 | ||||
| using FilePair = std::pair<const char *, uint32_t>; | ||||
| 
 | ||||
| struct Installer | ||||
| { | ||||
|     struct Input | ||||
|     { | ||||
|         std::filesystem::path gameSource; | ||||
|         std::filesystem::path updateSource; | ||||
|         std::list<std::filesystem::path> dlcSources; | ||||
|         bool skipHashChecks = false; | ||||
|     }; | ||||
| 
 | ||||
|     static bool checkGameInstall(const std::filesystem::path &baseDirectory); | ||||
|     static bool copyFiles(std::span<const FilePair> filePairs, const uint64_t *fileHashes, VirtualFileSystem &sourceVfs, const std::filesystem::path &targetDirectory, const std::string &validationFile, bool skipHashChecks, Journal &journal, const std::function<void(uint32_t)> &progressCallback); | ||||
|     static bool parseContent(const std::filesystem::path &sourcePath, std::unique_ptr<VirtualFileSystem> &targetVfs, Journal &journal); | ||||
|     static bool install(const Input &input, const std::filesystem::path &targetDirectory, Journal &journal, const std::function<void(uint32_t)> &progressCallback); | ||||
|     static void rollback(Journal &journal); | ||||
| 
 | ||||
|     // Convenience method for checking if the specified file contains the game. This should be used when the user selects the file.
 | ||||
|     static bool parseGame(const std::filesystem::path &sourcePath); | ||||
| 
 | ||||
|     // Convenience method for checking if the specified file contains the update. This should be used when the user selects the file.
 | ||||
|     static bool parseUpdate(const std::filesystem::path &sourcePath); | ||||
| 
 | ||||
|     // Convenience method for the installer to check which DLC the file that was specified corresponds to. This should be used when the user selects the file.
 | ||||
|     static DLC parseDLC(const std::filesystem::path &sourcePath); | ||||
| 
 | ||||
|     // Convenience method for checking if a game and an update are compatible. This should be used when the user presses next during installation.
 | ||||
|     static XexPatcher::Result checkGameUpdateCompatibility(const std::filesystem::path &gameSourcePath, const std::filesystem::path &updateSourcePath); | ||||
| }; | ||||
							
								
								
									
										191
									
								
								UnleashedRecomp/install/iso_file_system.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								UnleashedRecomp/install/iso_file_system.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,191 @@ | |||
| // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/disc_image_device.cc
 | ||||
| 
 | ||||
| /**
 | ||||
|  ****************************************************************************** | ||||
|  * Xenia : Xbox 360 Emulator Research Project                                 * | ||||
|  ****************************************************************************** | ||||
|  * Copyright 2023 Ben Vanik. All rights reserved.                             * | ||||
|  * Released under the BSD license - see LICENSE in the root for more details. * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| #include "iso_file_system.h" | ||||
| 
 | ||||
| #include <stack> | ||||
| 
 | ||||
| ISOFileSystem::ISOFileSystem(const std::filesystem::path &isoPath) | ||||
| { | ||||
|     mappedFile.open(isoPath); | ||||
|     if (!mappedFile.isOpen()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Find root sector.
 | ||||
|     const uint8_t *mappedFileData = mappedFile.data(); | ||||
|     uint32_t gameOffset = 0; | ||||
|     const size_t XeSectorSize = 2048; | ||||
|     static const size_t PossibleOffsets[] = { 0x00000000, 0x0000FB20, 0x00020600, 0x02080000, 0x0FD90000, }; | ||||
|     bool magicFound = false; | ||||
|     const char RefMagic[] = "MICROSOFT*XBOX*MEDIA"; | ||||
|     for (size_t i = 0; i < std::size(PossibleOffsets); i++) | ||||
|     { | ||||
|         size_t fileOffset = PossibleOffsets[i] + (32 * XeSectorSize); | ||||
|         if ((fileOffset + strlen(RefMagic)) > mappedFile.size()) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if (std::memcmp(&mappedFileData[fileOffset], RefMagic, strlen(RefMagic)) == 0) | ||||
|         { | ||||
|             gameOffset = PossibleOffsets[i]; | ||||
|             magicFound = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     size_t rootInfoOffset = gameOffset + (32 * XeSectorSize) + 20; | ||||
|     if (!magicFound || (rootInfoOffset + 8) > mappedFile.size()) | ||||
|     { | ||||
|         mappedFile.close(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // Parse root information.
 | ||||
|     uint32_t rootSector = *(uint32_t *)(&mappedFileData[rootInfoOffset + 0]); | ||||
|     uint32_t rootSize = *(uint32_t *)(&mappedFileData[rootInfoOffset + 4]); | ||||
|     size_t rootOffset = gameOffset + (rootSector * XeSectorSize); | ||||
|     const uint32_t MinRootSize = 13; | ||||
|     const uint32_t MaxRootSize = 32 * 1024 * 1024; | ||||
|     if ((rootSize < MinRootSize) || (rootSize > MaxRootSize)) | ||||
|     { | ||||
|         mappedFile.close(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     struct IterationStep | ||||
|     { | ||||
|         std::string fileNameBase; | ||||
|         size_t nodeOffset = 0; | ||||
|         size_t entryOffset = 0; | ||||
| 
 | ||||
|         IterationStep() = default; | ||||
|         IterationStep(std::string fileNameBase, size_t nodeOffset, size_t entryOffset) : fileNameBase(fileNameBase), nodeOffset(nodeOffset), entryOffset(entryOffset) { } | ||||
|     }; | ||||
| 
 | ||||
|     std::stack<IterationStep> iterationStack; | ||||
|     iterationStack.emplace("", rootOffset, 0); | ||||
| 
 | ||||
|     IterationStep step; | ||||
|     uint16_t nodeL, nodeR; | ||||
|     uint32_t sector, length; | ||||
|     uint8_t attributes, nameLength; | ||||
|     char fileName[256]; | ||||
|     const uint8_t FileAttributeDirectory = 0x10; | ||||
|     while (!iterationStack.empty()) | ||||
|     { | ||||
|         step = iterationStack.top(); | ||||
|         iterationStack.pop(); | ||||
| 
 | ||||
|         size_t infoOffset = step.nodeOffset + step.entryOffset; | ||||
|         if ((infoOffset + 14) > mappedFile.size()) | ||||
|         { | ||||
|             mappedFile.close(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         nodeL = *(uint16_t *)(&mappedFileData[infoOffset + 0]); | ||||
|         nodeR = *(uint16_t *)(&mappedFileData[infoOffset + 2]); | ||||
|         sector = *(uint32_t *)(&mappedFileData[infoOffset + 4]); | ||||
|         length = *(uint32_t *)(&mappedFileData[infoOffset + 8]); | ||||
|         attributes = *(uint8_t *)(&mappedFileData[infoOffset + 12]); | ||||
|         nameLength = *(uint8_t *)(&mappedFileData[infoOffset + 13]); | ||||
| 
 | ||||
|         size_t nameOffset = infoOffset + 14; | ||||
|         if ((nameOffset + nameLength) > mappedFile.size()) | ||||
|         { | ||||
|             mappedFile.close(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         memcpy(fileName, &mappedFileData[nameOffset], nameLength); | ||||
|         fileName[nameLength] = '\0'; | ||||
| 
 | ||||
|         if (nodeL) | ||||
|         { | ||||
|             iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeL * 4); | ||||
|         } | ||||
| 
 | ||||
|         if (nodeR) | ||||
|         { | ||||
|             iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeR * 4); | ||||
|         } | ||||
| 
 | ||||
|         std::string fileNameUTF8 = step.fileNameBase + fileName; | ||||
|         if (attributes & FileAttributeDirectory) | ||||
|         { | ||||
|             if (length > 0) | ||||
|             { | ||||
|                 iterationStack.emplace(fileNameUTF8 + "/", gameOffset + sector * XeSectorSize, 0); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             fileMap[fileNameUTF8] = { gameOffset + sector * XeSectorSize, length}; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool ISOFileSystem::load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const | ||||
| { | ||||
|     auto it = fileMap.find(path); | ||||
|     if (it != fileMap.end()) | ||||
|     { | ||||
|         if (fileDataMaxByteCount < std::get<1>(it->second)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         const uint8_t *mappedFileData = mappedFile.data(); | ||||
|         memcpy(fileData, &mappedFileData[std::get<0>(it->second)], std::get<1>(it->second)); | ||||
|         return true; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| size_t ISOFileSystem::getSize(const std::string &path) const | ||||
| { | ||||
|     auto it = fileMap.find(path); | ||||
|     if (it != fileMap.end()) | ||||
|     { | ||||
|         return std::get<1>(it->second); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool ISOFileSystem::exists(const std::string &path) const | ||||
| { | ||||
|     return fileMap.find(path) != fileMap.end(); | ||||
| } | ||||
| 
 | ||||
| bool ISOFileSystem::empty() const  | ||||
| { | ||||
|     return !mappedFile.isOpen(); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<ISOFileSystem> ISOFileSystem::create(const std::filesystem::path &isoPath) { | ||||
|     std::unique_ptr<ISOFileSystem> isoFs = std::make_unique<ISOFileSystem>(isoPath); | ||||
|     if (!isoFs->empty()) | ||||
|     { | ||||
|         return isoFs; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return nullptr; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										33
									
								
								UnleashedRecomp/install/iso_file_system.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								UnleashedRecomp/install/iso_file_system.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/disc_image_device.cc
 | ||||
| 
 | ||||
| /**
 | ||||
|  ****************************************************************************** | ||||
|  * Xenia : Xbox 360 Emulator Research Project                                 * | ||||
|  ****************************************************************************** | ||||
|  * Copyright 2023 Ben Vanik. All rights reserved.                             * | ||||
|  * Released under the BSD license - see LICENSE in the root for more details. * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <filesystem> | ||||
| #include <map> | ||||
| 
 | ||||
| #include "virtual_file_system.h" | ||||
| 
 | ||||
| #include "memory_mapped_file.h" | ||||
| 
 | ||||
| struct ISOFileSystem : VirtualFileSystem | ||||
| { | ||||
|     MemoryMappedFile mappedFile; | ||||
|     std::map<std::string, std::tuple<size_t, size_t>> fileMap; | ||||
| 
 | ||||
|     ISOFileSystem(const std::filesystem::path &isoPath); | ||||
|     bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override; | ||||
|     size_t getSize(const std::string &path) const override; | ||||
|     bool exists(const std::string &path) const override; | ||||
|     bool empty() const; | ||||
| 
 | ||||
|     static std::unique_ptr<ISOFileSystem> create(const std::filesystem::path &isoPath); | ||||
| }; | ||||
							
								
								
									
										169
									
								
								UnleashedRecomp/install/memory_mapped_file.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								UnleashedRecomp/install/memory_mapped_file.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,169 @@ | |||
| #include "memory_mapped_file.h" | ||||
| 
 | ||||
| #if !defined(_WIN32) | ||||
| #   include <cstring> | ||||
| #   include <cstdio> | ||||
| #   include <fcntl.h> | ||||
| #   include <unistd.h> | ||||
| #endif | ||||
| 
 | ||||
| MemoryMappedFile::MemoryMappedFile() | ||||
| { | ||||
|     // Default constructor.
 | ||||
| } | ||||
| 
 | ||||
| MemoryMappedFile::MemoryMappedFile(const std::filesystem::path &path) | ||||
| { | ||||
|     open(path); | ||||
| } | ||||
| 
 | ||||
| MemoryMappedFile::~MemoryMappedFile() | ||||
| { | ||||
|     close(); | ||||
| } | ||||
| 
 | ||||
| MemoryMappedFile::MemoryMappedFile(MemoryMappedFile &&other) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|     fileHandle = other.fileHandle; | ||||
|     fileMappingHandle = other.fileMappingHandle; | ||||
|     fileView = other.fileView; | ||||
|     fileSize = other.fileSize; | ||||
| 
 | ||||
|     other.fileHandle = nullptr; | ||||
|     other.fileMappingHandle = nullptr; | ||||
|     other.fileView = nullptr; | ||||
|     other.fileSize.QuadPart = 0; | ||||
| #else | ||||
|     fileHandle = other.fileHandle; | ||||
|     fileView = other.fileView; | ||||
|     fileSize = other.fileSize; | ||||
| 
 | ||||
|     other.fileHandle = -1; | ||||
|     other.fileView = MAP_FAILED; | ||||
|     other.fileSize = 0; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| bool MemoryMappedFile::open(const std::filesystem::path &path) | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|     fileHandle = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); | ||||
|     if (fileHandle == INVALID_HANDLE_VALUE) | ||||
|     { | ||||
|         fprintf(stderr, "CreateFileW failed with error %lu.\n", GetLastError()); | ||||
|         fileHandle = nullptr; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (!GetFileSizeEx(fileHandle, &fileSize)) | ||||
|     { | ||||
|         fprintf(stderr, "GetFileSizeEx failed with error %lu.\n", GetLastError()); | ||||
|         CloseHandle(fileHandle); | ||||
|         fileHandle = nullptr; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     fileMappingHandle = CreateFileMappingW(fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr); | ||||
|     if (fileMappingHandle == nullptr) | ||||
|     { | ||||
|         fprintf(stderr, "CreateFileMappingW failed with error %lu.\n", GetLastError()); | ||||
|         CloseHandle(fileHandle); | ||||
|         fileHandle = nullptr; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     fileView = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0); | ||||
|     if (fileView == nullptr) | ||||
|     { | ||||
|         fprintf(stderr, "MapViewOfFile failed with error %lu.\n", GetLastError()); | ||||
|         CloseHandle(fileMappingHandle); | ||||
|         CloseHandle(fileHandle); | ||||
|         fileMappingHandle = nullptr; | ||||
|         fileHandle = nullptr; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| #else | ||||
|     fileHandle = ::open(path.c_str(), O_RDONLY); | ||||
|     if (fileHandle == -1) | ||||
|     { | ||||
|         fprintf(stderr, "open for %s failed with error %s.\n", path.c_str(), strerror(errno)); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     fileSize = lseek(fileHandle, 0, SEEK_END); | ||||
|     if (fileSize == (off_t)(-1)) | ||||
|     { | ||||
|         fprintf(stderr, "lseek failed with error %s.\n", strerror(errno)); | ||||
|         close(fileHandle); | ||||
|         fileHandle = -1; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     fileView = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0); | ||||
|     if (fileView == MAP_FAILED) | ||||
|     { | ||||
|         fprintf(stderr, "mmap failed with error %s.\n", strerror(errno)); | ||||
|         close(fileHandle); | ||||
|         fileHandle = -1; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void MemoryMappedFile::close() | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|     if (fileView != nullptr) | ||||
|     { | ||||
|         UnmapViewOfFile(fileView); | ||||
|     } | ||||
| 
 | ||||
|     if (fileMappingHandle != nullptr) | ||||
|     { | ||||
|         CloseHandle(fileMappingHandle); | ||||
|     } | ||||
| 
 | ||||
|     if (fileHandle != nullptr) | ||||
|     { | ||||
|         CloseHandle(fileHandle); | ||||
|     } | ||||
| #else | ||||
|     if (fileView != MAP_FAILED) | ||||
|     { | ||||
|         munmap(fileView, fileSize); | ||||
|     } | ||||
| 
 | ||||
|     if (fileHandle != -1) | ||||
|     { | ||||
|         close(fileHandle); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| bool MemoryMappedFile::isOpen() const | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|     return (fileView != nullptr); | ||||
| #else | ||||
|     return (fileView != MAP_FAILED); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| uint8_t *MemoryMappedFile::data() const | ||||
| { | ||||
|     return reinterpret_cast<uint8_t *>(fileView); | ||||
| } | ||||
| 
 | ||||
| size_t MemoryMappedFile::size() const | ||||
| { | ||||
| #if defined(_WIN32) | ||||
|     return fileSize.QuadPart; | ||||
| #else | ||||
|     return static_cast<size_t>(fileSize); | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										32
									
								
								UnleashedRecomp/install/memory_mapped_file.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								UnleashedRecomp/install/memory_mapped_file.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <filesystem> | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
| #   include <Windows.h> | ||||
| #else | ||||
| #   include <sys/mman.h> | ||||
| #endif | ||||
| 
 | ||||
| struct MemoryMappedFile { | ||||
| #if defined(_WIN32) | ||||
|     HANDLE fileHandle = nullptr; | ||||
|     HANDLE fileMappingHandle = nullptr; | ||||
|     LPVOID fileView = nullptr; | ||||
|     LARGE_INTEGER fileSize = {}; | ||||
| #else | ||||
|     int fileHandle = -1; | ||||
|     void *fileView = MAP_FAILED; | ||||
|     off_t fileSize = 0; | ||||
| #endif | ||||
| 
 | ||||
|     MemoryMappedFile(); | ||||
|     MemoryMappedFile(const std::filesystem::path &path); | ||||
|     MemoryMappedFile(MemoryMappedFile &&other); | ||||
|     ~MemoryMappedFile(); | ||||
|     bool open(const std::filesystem::path &path); | ||||
|     void close(); | ||||
|     bool isOpen() const; | ||||
|     uint8_t *data() const; | ||||
|     size_t size() const; | ||||
| }; | ||||
							
								
								
									
										24
									
								
								UnleashedRecomp/install/virtual_file_system.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								UnleashedRecomp/install/virtual_file_system.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <memory> | ||||
| 
 | ||||
| struct VirtualFileSystem { | ||||
|     virtual ~VirtualFileSystem() { }; | ||||
|     virtual bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const = 0; | ||||
|     virtual size_t getSize(const std::string &path) const = 0; | ||||
|     virtual bool exists(const std::string &path) const = 0; | ||||
| 
 | ||||
|     // Concrete implementation shortcut.
 | ||||
|     bool load(const std::string &path, std::vector<uint8_t> &fileData) | ||||
|     { | ||||
|         size_t fileDataSize = getSize(path); | ||||
|         if (fileDataSize == 0) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         fileData.resize(fileDataSize); | ||||
|         return load(path, fileData.data(), fileDataSize); | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										641
									
								
								UnleashedRecomp/install/xcontent_file_system.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										641
									
								
								UnleashedRecomp/install/xcontent_file_system.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,641 @@ | |||
| // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/xcontent_container_device.cc
 | ||||
| 
 | ||||
| /**
 | ||||
|  ****************************************************************************** | ||||
|  * Xenia : Xbox 360 Emulator Research Project                                 * | ||||
|  ****************************************************************************** | ||||
|  * Copyright 2023 Ben Vanik. All rights reserved.                             * | ||||
|  * Released under the BSD license - see LICENSE in the root for more details. * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include "xcontent_file_system.h" | ||||
| 
 | ||||
| #include <bit> | ||||
| #include <set> | ||||
| #include <stack> | ||||
| 
 | ||||
| enum class XContentPackageType | ||||
| { | ||||
|     CON = 0x434F4E20, | ||||
|     PIRS = 0x50495253, | ||||
|     LIVE = 0x4C495645, | ||||
| }; | ||||
| 
 | ||||
| struct XContentLicense | ||||
| { | ||||
|     be<uint64_t> licenseId; | ||||
|     be<uint32_t> licenseBits; | ||||
|     be<uint32_t> licenseFlags; | ||||
| }; | ||||
| 
 | ||||
| #pragma pack(push, 1) | ||||
| struct XContentHeader | ||||
| { | ||||
|     be<uint32_t> magic; | ||||
|     uint8_t signature[0x228]; | ||||
|     XContentLicense licenses[0x10]; | ||||
|     uint8_t contentId[0x14]; | ||||
|     be<uint32_t> headerSize; | ||||
| }; | ||||
| static_assert(sizeof(XContentHeader) == 0x344); | ||||
| 
 | ||||
| struct StfsVolumeDescriptor | ||||
| { | ||||
|     uint8_t descriptorLength; | ||||
|     uint8_t version; | ||||
| 
 | ||||
|     union | ||||
|     { | ||||
|         uint8_t asByte; | ||||
|         struct | ||||
|         { | ||||
|             uint8_t readOnlyFormat : 1; | ||||
|             uint8_t rootActiveIndex : 1; | ||||
|             uint8_t directoryOverallocated : 1; | ||||
|             uint8_t directoryIndexBoundsValid : 1; | ||||
|         } bits; | ||||
|     } flags; | ||||
| 
 | ||||
|     uint16_t fileTableBlockCount; | ||||
|     uint8_t fileTableBlockNumberRaw[3]; | ||||
|     uint8_t topHashTableHash[0x14]; | ||||
|     be<uint32_t> totalBlockCount; | ||||
|     be<uint32_t> freeBlockCount; | ||||
| }; | ||||
| static_assert(sizeof(StfsVolumeDescriptor) == 0x24); | ||||
| 
 | ||||
| struct StfsDirectoryEntry { | ||||
|     char name[40]; | ||||
| 
 | ||||
|     struct | ||||
|     { | ||||
|         uint8_t nameLength : 6; | ||||
|         uint8_t contiguous : 1; | ||||
|         uint8_t directory : 1; | ||||
|     } flags; | ||||
| 
 | ||||
|     uint8_t validDataBlocksRaw[3]; | ||||
|     uint8_t allocatedDataBlocksRaw[3]; | ||||
|     uint8_t startBlockNumberRaw[3]; | ||||
|     be<uint16_t> directoryIndex; | ||||
|     be<uint32_t> length; | ||||
|     be<uint16_t> createDate; | ||||
|     be<uint16_t> createTime; | ||||
|     be<uint16_t> modifiedDate; | ||||
|     be<uint16_t> modifiedTime; | ||||
| }; | ||||
| static_assert(sizeof(StfsDirectoryEntry) == 0x40); | ||||
| 
 | ||||
| struct StfsDirectoryBlock { | ||||
|     StfsDirectoryEntry entries[0x40]; | ||||
| }; | ||||
| static_assert(sizeof(StfsDirectoryBlock) == 0x1000); | ||||
| 
 | ||||
| struct StfsHashEntry { | ||||
|     uint8_t sha1[0x14]; | ||||
|     be<uint32_t> infoRaw; | ||||
| }; | ||||
| static_assert(sizeof(StfsHashEntry) == 0x18); | ||||
| 
 | ||||
| struct StfsHashTable { | ||||
|     StfsHashEntry entries[170]; | ||||
|     be<uint32_t> numBlocks; | ||||
|     uint8_t padding[12]; | ||||
| }; | ||||
| static_assert(sizeof(StfsHashTable) == 0x1000); | ||||
| 
 | ||||
| struct SvodDeviceDescriptor { | ||||
|     uint8_t descriptorLength; | ||||
|     uint8_t blockCacheElementCount; | ||||
|     uint8_t workerThreadProcessor; | ||||
|     uint8_t workerThreadPriority; | ||||
|     uint8_t firstFragmentHashEntry[0x14]; | ||||
|     union { | ||||
|         uint8_t asByte; | ||||
|         struct { | ||||
|             uint8_t mustBeZeroForFutureUsage : 6; | ||||
|             uint8_t enhancedGdfLayout : 1; | ||||
|             uint8_t zeroForDownlevelClients : 1; | ||||
|         } bits; | ||||
|     } features; | ||||
|     uint8_t numDataBlocksRaw[3]; | ||||
|     uint8_t startDataBlockRaw[3]; | ||||
|     uint8_t reserved[5]; | ||||
| }; | ||||
| static_assert(sizeof(SvodDeviceDescriptor) == 0x24); | ||||
| 
 | ||||
| struct SvodDirectoryEntry { | ||||
|     uint16_t nodeL; | ||||
|     uint16_t nodeR; | ||||
|     uint32_t dataBlock; | ||||
|     uint32_t length; | ||||
|     uint8_t attributes; | ||||
|     uint8_t nameLength; | ||||
| }; | ||||
| static_assert(sizeof(SvodDirectoryEntry) == 0xE); | ||||
| 
 | ||||
| struct XContentMetadata | ||||
| { | ||||
|     be<uint32_t> contentType; | ||||
|     be<uint32_t> metadataVersion; | ||||
|     be<uint64_t> contentSize; | ||||
|     uint8_t executionInfo[24]; | ||||
|     uint8_t consoleId[5]; | ||||
|     be<uint64_t> profileId; | ||||
| 
 | ||||
|     union { | ||||
|         StfsVolumeDescriptor stfsVolumeDescriptor; | ||||
|         SvodDeviceDescriptor svodDeviceDescriptor; | ||||
|     }; | ||||
| 
 | ||||
|     be<uint32_t> dataFileCount; | ||||
|     be<uint64_t> dataFileSize; | ||||
|     be<uint32_t> volumeType; | ||||
|     be<uint64_t> onlineCreator; | ||||
|     be<uint32_t> category; | ||||
| }; | ||||
| static_assert(sizeof(XContentMetadata) == 0x75); | ||||
| 
 | ||||
| #pragma pack(pop) | ||||
| 
 | ||||
| struct XContentContainerHeader | ||||
| { | ||||
|     XContentHeader contentHeader; | ||||
|     XContentMetadata contentMetadata; | ||||
| }; | ||||
| 
 | ||||
| const uint32_t StfsBlockSize = 0x1000; | ||||
| const uint32_t StfsBlocksHashLevelAmount = 3; | ||||
| const uint32_t StfsBlocksPerHashLevel[StfsBlocksHashLevelAmount] = { 170, 28900, 4913000 }; | ||||
| const uint32_t StfsEndOfChain = 0xFFFFFF; | ||||
| const uint32_t StfsEntriesPerDirectoryBlock = StfsBlockSize / sizeof(StfsDirectoryEntry); | ||||
| 
 | ||||
| uint32_t parseUint24(const uint8_t *bytes) { | ||||
|     return bytes[0] | (bytes[1] << 8U) | (bytes[2] << 16U); | ||||
| } | ||||
| 
 | ||||
| size_t blockIndexToOffset(uint64_t baseOffset, uint64_t blockIndex) | ||||
| { | ||||
|     uint64_t block = blockIndex; | ||||
|     for (uint32_t i = 0; i < StfsBlocksHashLevelAmount; i++) | ||||
|     { | ||||
|         uint32_t levelBase = StfsBlocksPerHashLevel[i]; | ||||
|         block += ((blockIndex + levelBase) / levelBase); | ||||
|         if (blockIndex < levelBase) | ||||
|         { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return baseOffset + (block << 12); | ||||
| } | ||||
| 
 | ||||
| uint32_t blockIndexToHashBlockNumber(uint32_t blockIndex) { | ||||
|     if (blockIndex < StfsBlocksPerHashLevel[0]) | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     uint32_t block = (blockIndex / StfsBlocksPerHashLevel[0]) * (StfsBlocksPerHashLevel[0] + 1); | ||||
|     block += ((blockIndex / StfsBlocksPerHashLevel[1]) + 1); | ||||
|     if (blockIndex < StfsBlocksPerHashLevel[1]) | ||||
|     { | ||||
|         return block; | ||||
|     } | ||||
| 
 | ||||
|     return block + 1; | ||||
| } | ||||
| 
 | ||||
| size_t blockIndexToHashBlockOffset(uint64_t baseOffset, uint32_t blockIndex) | ||||
| { | ||||
|     size_t blockNumber = blockIndexToHashBlockNumber(blockIndex); | ||||
|     return baseOffset + (blockNumber << 12); | ||||
| } | ||||
| 
 | ||||
| const StfsHashEntry *hashEntryFromBlockIndex(const uint8_t *fileData, uint64_t baseOffset, uint64_t blockIndex) | ||||
| { | ||||
|     size_t hashOffset = blockIndexToHashBlockOffset(baseOffset, blockIndex); | ||||
|     const StfsHashTable *hashTable = (const StfsHashTable *)(&fileData[hashOffset]); | ||||
|     return &hashTable->entries[blockIndex % StfsBlocksPerHashLevel[0]]; | ||||
| } | ||||
| 
 | ||||
| void blockToOffsetAndFile(SvodLayoutType svodLayoutType, size_t svodStartDataBlock, size_t svodBaseOffset, size_t block, size_t &outOffset, size_t &outFileIndex) | ||||
| { | ||||
|     const size_t BlockSize = 0x800; | ||||
|     const size_t HashBlockSize = 0x1000; | ||||
|     const size_t BlocksPerL0Hash = 0x198; | ||||
|     const size_t HashesPerL1Hash = 0xA1C4; | ||||
|     const size_t BlocksPerFile = 0x14388; | ||||
|     const size_t MaxFileSize = 0xA290000; | ||||
|     size_t trueBlock = block - (svodStartDataBlock * 2); | ||||
|     if (svodLayoutType == SvodLayoutType::EnhancedGDF) | ||||
|     { | ||||
|         trueBlock += 0x2; | ||||
|     } | ||||
| 
 | ||||
|     size_t fileBlock = trueBlock % BlocksPerFile; | ||||
|     outFileIndex = trueBlock / BlocksPerFile; | ||||
| 
 | ||||
|     size_t offset = 0; | ||||
|     size_t level0TableCount = (fileBlock / BlocksPerL0Hash) + 1; | ||||
|     offset += level0TableCount * HashBlockSize; | ||||
| 
 | ||||
|     size_t level1TableCount = (level0TableCount / HashesPerL1Hash) + 1; | ||||
|     offset += level1TableCount * HashBlockSize; | ||||
| 
 | ||||
|     if (svodLayoutType == SvodLayoutType::SingleFile) | ||||
|     { | ||||
|         offset += svodBaseOffset; | ||||
|     } | ||||
| 
 | ||||
|     outOffset = (fileBlock * BlockSize) + offset; | ||||
|     if (outOffset >= MaxFileSize) | ||||
|     { | ||||
|         outOffset = (outOffset % MaxFileSize) + 0x2000; | ||||
|         outFileIndex++; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| XContentFileSystem::XContentFileSystem(const std::filesystem::path &contentPath) | ||||
| { | ||||
|     mappedFiles.emplace_back(); | ||||
| 
 | ||||
|     MemoryMappedFile &rootMappedFile = mappedFiles.back(); | ||||
|     rootMappedFile.open(contentPath); | ||||
|     if (!rootMappedFile.isOpen()) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const uint8_t *rootMappedFileData = rootMappedFile.data(); | ||||
|     if (sizeof(XContentContainerHeader) > rootMappedFile.size()) | ||||
|     { | ||||
|         mappedFiles.clear(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     XContentContainerHeader contentContainerHeader = *(const XContentContainerHeader *)(rootMappedFileData); | ||||
|     XContentPackageType packageType = XContentPackageType(contentContainerHeader.contentHeader.magic.get()); | ||||
|     if (packageType != XContentPackageType::CON && packageType != XContentPackageType::LIVE && packageType != XContentPackageType::PIRS) | ||||
|     { | ||||
|         mappedFiles.clear(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const XContentMetadata &metadata = contentContainerHeader.contentMetadata; | ||||
|     volumeType = XContentVolumeType(metadata.volumeType.get()); | ||||
|     if (volumeType == XContentVolumeType::STFS) | ||||
|     { | ||||
|         const StfsVolumeDescriptor &descriptor = metadata.stfsVolumeDescriptor; | ||||
|         if (descriptor.descriptorLength != sizeof(StfsVolumeDescriptor) || !descriptor.flags.bits.readOnlyFormat) | ||||
|         { | ||||
|             mappedFiles.clear(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         baseOffset = ((contentContainerHeader.contentHeader.headerSize + StfsBlockSize - 1) / StfsBlockSize) * StfsBlockSize; | ||||
| 
 | ||||
|         uint32_t entryCount = 0; | ||||
|         uint32_t tableBlockIndex = parseUint24(descriptor.fileTableBlockNumberRaw); | ||||
|         uint32_t tableBlockCount = descriptor.fileTableBlockCount; | ||||
|         std::map<uint32_t, std::string> directoryNames; | ||||
|         for (uint32_t i = 0; i < tableBlockCount; i++) | ||||
|         { | ||||
|             size_t offset = blockIndexToOffset(baseOffset, tableBlockIndex); | ||||
|             if (offset + sizeof(StfsDirectoryBlock) > rootMappedFile.size()) | ||||
|             { | ||||
|                 mappedFiles.clear(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             StfsDirectoryBlock *directoryBlock = (StfsDirectoryBlock *)(&rootMappedFileData[offset]); | ||||
|             for (uint32_t j = 0; j < StfsEntriesPerDirectoryBlock; j++) | ||||
|             { | ||||
|                 const StfsDirectoryEntry &directoryEntry = directoryBlock->entries[j]; | ||||
|                 if (directoryEntry.name[0] == '\0') | ||||
|                 { | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 std::string fileNameBase = directoryNames[directoryEntry.directoryIndex]; | ||||
|                 std::string fileName(directoryEntry.name, directoryEntry.flags.nameLength & 0x3F); | ||||
|                 if (directoryEntry.flags.directory) | ||||
|                 { | ||||
|                     directoryNames[entryCount++] = fileNameBase + fileName + "/"; | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 uint32_t fileBlockIndex = parseUint24(directoryEntry.startBlockNumberRaw); | ||||
|                 uint32_t fileBlockCount = parseUint24(directoryEntry.allocatedDataBlocksRaw); | ||||
|                 fileMap[fileNameBase + fileName] = { directoryEntry.length, fileBlockIndex, fileBlockCount }; | ||||
|                 entryCount++; | ||||
|             } | ||||
| 
 | ||||
|             const StfsHashEntry *hashEntry = hashEntryFromBlockIndex(rootMappedFileData, baseOffset, tableBlockIndex); | ||||
|             tableBlockIndex = hashEntry->infoRaw & 0xFFFFFF; | ||||
|             if (tableBlockIndex == StfsEndOfChain) | ||||
|             { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else if (volumeType == XContentVolumeType::SVOD) | ||||
|     { | ||||
|         mappedFiles.clear(); | ||||
| 
 | ||||
|         // Close the root file and open all the files inside the directory with the same name instead.
 | ||||
|         std::filesystem::path dataDirectory(contentPath.u8string() + u8".data"); | ||||
|         if (!std::filesystem::is_directory(dataDirectory)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Find all data files inside the directory.
 | ||||
|         std::set<std::filesystem::path> orderedPaths; | ||||
|         for (auto &entry : std::filesystem::directory_iterator(dataDirectory)) | ||||
|         { | ||||
|             if (!entry.is_regular_file()) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             orderedPaths.emplace(entry.path()); | ||||
|         } | ||||
| 
 | ||||
|         // Memory map all the files that were found.
 | ||||
|         for (auto &path : orderedPaths) | ||||
|         { | ||||
|             mappedFiles.emplace_back(); | ||||
|             if (!mappedFiles.back().open(path)) | ||||
|             { | ||||
|                 mappedFiles.clear(); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (mappedFiles.empty()) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Determine the layout of the SVOD from the first file.
 | ||||
|         MemoryMappedFile &firstMappedFile = mappedFiles.front(); | ||||
|         const uint8_t *firstMappedFileData = firstMappedFile.data(); | ||||
|         const char *RefMagic = "MICROSOFT*XBOX*MEDIA"; | ||||
|         size_t RefXSFMagicOffset = 0x12000; | ||||
|         size_t SingleFileMagicOffset = 0xD000; | ||||
|         if (metadata.svodDeviceDescriptor.features.bits.enhancedGdfLayout) | ||||
|         { | ||||
|             size_t EGDFMagicOffset = 0x2000; | ||||
|             if (EGDFMagicOffset >= firstMappedFile.size() || std::memcmp(&firstMappedFileData[EGDFMagicOffset], RefMagic, strlen(RefMagic)) != 0) | ||||
|             { | ||||
|                 mappedFiles.clear(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             svodBaseOffset = 0; | ||||
|             svodMagicOffset = EGDFMagicOffset; | ||||
|             svodLayoutType = SvodLayoutType::EnhancedGDF; | ||||
|         } | ||||
|         else if (RefXSFMagicOffset < firstMappedFile.size() && std::memcmp(&firstMappedFileData[RefXSFMagicOffset], RefMagic, strlen(RefMagic)) == 0) | ||||
|         { | ||||
|             const char *XSFMagic = "XSF"; | ||||
|             size_t XSFMagicOffset = 0x2000; | ||||
|             svodBaseOffset = 0x10000; | ||||
|             svodMagicOffset = 0x12000; | ||||
| 
 | ||||
|             if (std::memcmp(&firstMappedFileData[XSFMagicOffset], XSFMagic, strlen(XSFMagic)) == 0) | ||||
|             { | ||||
|                 svodLayoutType = SvodLayoutType::XSF; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 svodLayoutType = SvodLayoutType::Unknown; | ||||
|             } | ||||
|         } | ||||
|         else if (SingleFileMagicOffset < firstMappedFile.size() && std::memcmp(&firstMappedFileData[SingleFileMagicOffset], RefMagic, strlen(RefMagic)) == 0) | ||||
|         { | ||||
|             svodBaseOffset = 0xB000; | ||||
|             svodMagicOffset = 0xD000; | ||||
|             svodLayoutType = SvodLayoutType::SingleFile; | ||||
|         } | ||||
|         else { | ||||
|             mappedFiles.clear(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         svodStartDataBlock = parseUint24(metadata.svodDeviceDescriptor.startDataBlockRaw); | ||||
| 
 | ||||
|         struct IterationStep | ||||
|         { | ||||
|             std::string fileNameBase; | ||||
|             uint32_t blockIndex = 0; | ||||
|             uint32_t ordinalIndex = 0; | ||||
| 
 | ||||
|             IterationStep() = default; | ||||
|             IterationStep(std::string fileNameBase, uint32_t blockIndex, uint32_t ordinalIndex) : fileNameBase(fileNameBase), blockIndex(blockIndex), ordinalIndex(ordinalIndex) { } | ||||
|         }; | ||||
| 
 | ||||
|         std::stack<IterationStep> iterationStack; | ||||
|         uint32_t rootBlock = *(uint32_t *)(&firstMappedFileData[svodMagicOffset + 0x14]); | ||||
|         iterationStack.emplace("", rootBlock, 0); | ||||
| 
 | ||||
|         IterationStep step; | ||||
|         size_t fileOffset, fileIndex; | ||||
|         char fileName[256]; | ||||
|         const uint8_t FileAttributeDirectory = 0x10; | ||||
|         while (!iterationStack.empty()) | ||||
|         { | ||||
|             step = iterationStack.top(); | ||||
|             iterationStack.pop(); | ||||
| 
 | ||||
|             size_t ordinalOffset = step.ordinalIndex * 0x4; | ||||
|             size_t blockOffset = ordinalOffset / 0x800; | ||||
|             size_t trueOrdinalOffset = ordinalOffset % 0x800; | ||||
|             blockToOffsetAndFile(svodLayoutType, svodStartDataBlock, svodBaseOffset, step.blockIndex + blockOffset, fileOffset, fileIndex); | ||||
|             fileOffset += trueOrdinalOffset; | ||||
|             if (fileIndex >= mappedFiles.size()) | ||||
|             { | ||||
|                 mappedFiles.clear(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const MemoryMappedFile &mappedFile = mappedFiles[fileIndex]; | ||||
|             if ((fileOffset + sizeof(SvodDirectoryEntry)) > mappedFile.size()) | ||||
|             { | ||||
|                 mappedFiles.clear(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const uint8_t *mappedFileData = mappedFile.data(); | ||||
|             const SvodDirectoryEntry *directoryEntry = (const SvodDirectoryEntry *)(&mappedFileData[fileOffset]); | ||||
|             size_t nameOffset = fileOffset + sizeof(SvodDirectoryEntry); | ||||
|             if ((nameOffset + directoryEntry->nameLength) > mappedFile.size()) | ||||
|             { | ||||
|                 mappedFiles.clear(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             memcpy(fileName, &mappedFileData[nameOffset], directoryEntry->nameLength); | ||||
|             fileName[directoryEntry->nameLength] = '\0'; | ||||
| 
 | ||||
|             if (directoryEntry->nodeL) | ||||
|             { | ||||
|                 iterationStack.emplace(step.fileNameBase, step.blockIndex, directoryEntry->nodeL); | ||||
|             } | ||||
| 
 | ||||
|             if (directoryEntry->nodeR) | ||||
|             { | ||||
|                 iterationStack.emplace(step.fileNameBase, step.blockIndex, directoryEntry->nodeR); | ||||
|             } | ||||
| 
 | ||||
|             std::string fileNameUTF8 = step.fileNameBase + fileName; | ||||
|             if (directoryEntry->attributes & FileAttributeDirectory) | ||||
|             { | ||||
|                 if (directoryEntry->length > 0) | ||||
|                 { | ||||
|                     iterationStack.emplace(fileNameUTF8 + "/", directoryEntry->dataBlock, 0); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 fileMap[fileNameUTF8] = { directoryEntry->length, directoryEntry->dataBlock, 0 }; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         mappedFiles.clear(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool XContentFileSystem::load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const | ||||
| { | ||||
|     auto it = fileMap.find(path); | ||||
|     if (it != fileMap.end()) | ||||
|     { | ||||
|         if (fileDataMaxByteCount < it->second.size) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (volumeType == XContentVolumeType::STFS) | ||||
|         { | ||||
|             const MemoryMappedFile &rootMappedFile = mappedFiles.back(); | ||||
|             const uint8_t *rootMappedFileData = rootMappedFile.data(); | ||||
|             size_t fileDataOffset = 0; | ||||
|             size_t remainingSize = it->second.size; | ||||
|             uint32_t fileBlockIndex = it->second.blockIndex; | ||||
|             for (uint32_t i = 0; i < it->second.blockCount && fileBlockIndex != StfsEndOfChain; i++) | ||||
|             { | ||||
|                 size_t blockSize = std::min(size_t(StfsBlockSize), remainingSize); | ||||
|                 size_t blockOffset = blockIndexToOffset(baseOffset, fileBlockIndex); | ||||
|                 if (blockOffset + blockSize > rootMappedFile.size()) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 memcpy(&fileData[fileDataOffset], &rootMappedFileData[blockOffset], blockSize); | ||||
| 
 | ||||
|                 const StfsHashEntry *hashEntry = hashEntryFromBlockIndex(rootMappedFileData, baseOffset, fileBlockIndex); | ||||
|                 fileBlockIndex = hashEntry->infoRaw & 0xFFFFFF; | ||||
|                 fileDataOffset += blockSize; | ||||
|                 remainingSize -= blockSize; | ||||
|             } | ||||
| 
 | ||||
|             return remainingSize == 0; | ||||
|         } | ||||
|         else if (volumeType == XContentVolumeType::SVOD) | ||||
|         { | ||||
|             size_t fileDataOffset = 0; | ||||
|             size_t remainingSize = it->second.size; | ||||
|             size_t currentBlock = it->second.blockIndex; | ||||
|             while (remainingSize > 0) | ||||
|             { | ||||
|                 size_t blockFileOffset, blockFileIndex; | ||||
|                 blockToOffsetAndFile(svodLayoutType, svodStartDataBlock, svodBaseOffset, currentBlock, blockFileOffset, blockFileIndex); | ||||
|                 if (blockFileIndex >= mappedFiles.size()) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 const MemoryMappedFile &mappedFile = mappedFiles[blockFileIndex]; | ||||
|                 const uint8_t *mappedFileData = mappedFile.data(); | ||||
|                 size_t blockSize = std::min(size_t(0x800), remainingSize); | ||||
|                 if (blockFileOffset + blockSize > mappedFile.size()) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
| 
 | ||||
|                 memcpy(&fileData[fileDataOffset], &mappedFileData[blockFileOffset], blockSize); | ||||
| 
 | ||||
|                 fileDataOffset += blockSize; | ||||
|                 remainingSize -= blockSize; | ||||
|                 currentBlock++; | ||||
|             } | ||||
| 
 | ||||
|             return remainingSize == 0; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| size_t XContentFileSystem::getSize(const std::string &path) const | ||||
| { | ||||
|     auto it = fileMap.find(path); | ||||
|     if (it != fileMap.end()) | ||||
|     { | ||||
|         return it->second.size; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool XContentFileSystem::exists(const std::string &path) const | ||||
| { | ||||
|     return fileMap.find(path) != fileMap.end(); | ||||
| } | ||||
| 
 | ||||
| bool XContentFileSystem::empty() const | ||||
| { | ||||
|     return mappedFiles.empty(); | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<XContentFileSystem> XContentFileSystem::create(const std::filesystem::path &contentPath) | ||||
| { | ||||
|     std::unique_ptr<XContentFileSystem> xContentFS = std::make_unique<XContentFileSystem>(contentPath); | ||||
|     if (!xContentFS->empty()) | ||||
|     { | ||||
|         return xContentFS; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool XContentFileSystem::check(const std::filesystem::path &contentPath) | ||||
| { | ||||
|     std::ifstream contentStream(contentPath, std::ios::binary); | ||||
|     if (!contentStream.is_open()) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     uint32_t packageTypeUint = 0; | ||||
|     contentStream.read((char *)(&packageTypeUint), sizeof(uint32_t)); | ||||
|     packageTypeUint = std::byteswap(packageTypeUint); | ||||
|     XContentPackageType packageType = XContentPackageType(packageTypeUint); | ||||
|     return packageType == XContentPackageType::CON || packageType == XContentPackageType::LIVE || packageType == XContentPackageType::PIRS; | ||||
| } | ||||
							
								
								
									
										61
									
								
								UnleashedRecomp/install/xcontent_file_system.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								UnleashedRecomp/install/xcontent_file_system.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/xcontent_container_device.cc
 | ||||
| 
 | ||||
| /**
 | ||||
|  ****************************************************************************** | ||||
|  * Xenia : Xbox 360 Emulator Research Project                                 * | ||||
|  ****************************************************************************** | ||||
|  * Copyright 2023 Ben Vanik. All rights reserved.                             * | ||||
|  * Released under the BSD license - see LICENSE in the root for more details. * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <filesystem> | ||||
| #include <map> | ||||
| 
 | ||||
| #include "virtual_file_system.h" | ||||
| 
 | ||||
| #include "memory_mapped_file.h" | ||||
| 
 | ||||
| enum class XContentVolumeType | ||||
| { | ||||
|     STFS = 0, | ||||
|     SVOD = 1, | ||||
| }; | ||||
| 
 | ||||
| enum class SvodLayoutType  | ||||
| { | ||||
|     Unknown = 0x0, | ||||
|     EnhancedGDF = 0x1, | ||||
|     XSF = 0x2, | ||||
|     SingleFile = 0x4, | ||||
| }; | ||||
| 
 | ||||
| struct XContentFileSystem : VirtualFileSystem | ||||
| { | ||||
|     struct File | ||||
|     { | ||||
|         size_t size = 0; | ||||
|         uint32_t blockIndex = 0; | ||||
|         uint32_t blockCount = 0; | ||||
|     }; | ||||
| 
 | ||||
|     XContentVolumeType volumeType = XContentVolumeType::STFS; | ||||
|     SvodLayoutType svodLayoutType = SvodLayoutType::Unknown; | ||||
|     size_t svodStartDataBlock = 0; | ||||
|     size_t svodBaseOffset = 0; | ||||
|     size_t svodMagicOffset = 0; | ||||
|     std::vector<MemoryMappedFile> mappedFiles; | ||||
|     uint64_t baseOffset = 0; | ||||
|     std::map<std::string, File> fileMap; | ||||
| 
 | ||||
|     XContentFileSystem(const std::filesystem::path &contentPath); | ||||
|     bool load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const override; | ||||
|     size_t getSize(const std::string &path) const override; | ||||
|     bool exists(const std::string &path) const override; | ||||
|     bool empty() const; | ||||
| 
 | ||||
|     static std::unique_ptr<XContentFileSystem> create(const std::filesystem::path &contentPath); | ||||
|     static bool check(const std::filesystem::path &contentPath); | ||||
| }; | ||||
							
								
								
									
										693
									
								
								UnleashedRecomp/install/xex_patcher.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										693
									
								
								UnleashedRecomp/install/xex_patcher.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,693 @@ | |||
| // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc
 | ||||
| 
 | ||||
| /**
 | ||||
|  ****************************************************************************** | ||||
|  * Xenia : Xbox 360 Emulator Research Project                                 * | ||||
|  ****************************************************************************** | ||||
|  * Copyright 2023 Ben Vanik. All rights reserved.                             * | ||||
|  * Released under the BSD license - see LICENSE in the root for more details. * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| #include "xex_patcher.h" | ||||
| 
 | ||||
| #include <bit> | ||||
| #include <cassert> | ||||
| 
 | ||||
| #include <aes.hpp> | ||||
| #include <lzx.h> | ||||
| #include <mspack.h> | ||||
| #include <TinySHA1.hpp> | ||||
| 
 | ||||
| #include "memory_mapped_file.h" | ||||
| 
 | ||||
| enum Xex2ModuleFlags | ||||
| { | ||||
|     XEX_MODULE_MODULE_PATCH = 0x10, | ||||
|     XEX_MODULE_PATCH_FULL = 0x20, | ||||
|     XEX_MODULE_PATCH_DELTA = 0x40, | ||||
| }; | ||||
| 
 | ||||
| enum Xex2HeaderKeys | ||||
| { | ||||
|     XEX_HEADER_FILE_FORMAT_INFO = 0x3FF, | ||||
|     XEX_HEADER_DELTA_PATCH_DESCRIPTOR = 0x5FF, | ||||
| }; | ||||
| 
 | ||||
| enum Xex2EncryptionType | ||||
| { | ||||
|     XEX_ENCRYPTION_NONE = 0, | ||||
|     XEX_ENCRYPTION_NORMAL = 1, | ||||
| }; | ||||
| 
 | ||||
| enum Xex2CompressionType | ||||
| { | ||||
|     XEX_COMPRESSION_NONE = 0, | ||||
|     XEX_COMPRESSION_BASIC = 1, | ||||
|     XEX_COMPRESSION_NORMAL = 2, | ||||
|     XEX_COMPRESSION_DELTA = 3, | ||||
| }; | ||||
| 
 | ||||
| enum Xex2SectionType | ||||
| { | ||||
|     XEX_SECTION_CODE = 1, | ||||
|     XEX_SECTION_DATA = 2, | ||||
|     XEX_SECTION_READONLY_DATA = 3, | ||||
| }; | ||||
| 
 | ||||
| struct Xex2OptHeader | ||||
| { | ||||
|     be<uint32_t> key; | ||||
| 
 | ||||
|     union | ||||
|     { | ||||
|         be<uint32_t> value; | ||||
|         be<uint32_t> offset; | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2Header | ||||
| { | ||||
|     be<uint32_t> magic; | ||||
|     be<uint32_t> moduleFlags; | ||||
|     be<uint32_t> headerSize; | ||||
|     be<uint32_t> reserved; | ||||
|     be<uint32_t> securityOffset; | ||||
|     be<uint32_t> headerCount; | ||||
|     Xex2OptHeader headers[1]; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2PageDescriptor | ||||
| { | ||||
|     union | ||||
|     { | ||||
|         // Must be endian-swapped before reading the bitfield.
 | ||||
|         uint32_t beValue; | ||||
|         struct | ||||
|         { | ||||
|             uint32_t info : 4; | ||||
|             uint32_t pageCount : 28; | ||||
|         }; | ||||
|     }; | ||||
| 
 | ||||
|     char dataDigest[0x14]; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2SecurityInfo | ||||
| { | ||||
|     be<uint32_t> headerSize; | ||||
|     be<uint32_t> imageSize; | ||||
|     char rsaSignature[0x100]; | ||||
|     be<uint32_t> unknown; | ||||
|     be<uint32_t> imageFlags; | ||||
|     be<uint32_t> loadAddress; | ||||
|     char sectionDigest[0x14]; | ||||
|     be<uint32_t> importTableCount; | ||||
|     char importTableDigest[0x14]; | ||||
|     char xgd2MediaId[0x10]; | ||||
|     char aesKey[0x10]; | ||||
|     be<uint32_t> exportTable; | ||||
|     char headerDigest[0x14]; | ||||
|     be<uint32_t> region; | ||||
|     be<uint32_t> allowedMediaTypes; | ||||
|     be<uint32_t> pageDescriptorCount; | ||||
|     Xex2PageDescriptor pageDescriptors[1]; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2DeltaPatch | ||||
| { | ||||
|     be<uint32_t> oldAddress; | ||||
|     be<uint32_t> newAddress; | ||||
|     be<uint16_t> uncompressedLength; | ||||
|     be<uint16_t> compressedLength; | ||||
|     char patchData[1]; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2OptDeltaPatchDescriptor | ||||
| { | ||||
|     be<uint32_t> size; | ||||
|     be<uint32_t> targetVersionValue; | ||||
|     be<uint32_t> sourceVersionValue; | ||||
|     uint8_t digestSource[0x14]; | ||||
|     uint8_t imageKeySource[0x10]; | ||||
|     be<uint32_t> sizeOfTargetHeaders; | ||||
|     be<uint32_t> deltaHeadersSourceOffset; | ||||
|     be<uint32_t> deltaHeadersSourceSize; | ||||
|     be<uint32_t> deltaHeadersTargetOffset; | ||||
|     be<uint32_t> deltaImageSourceOffset; | ||||
|     be<uint32_t> deltaImageSourceSize; | ||||
|     be<uint32_t> deltaImageTargetOffset; | ||||
|     Xex2DeltaPatch info; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2FileBasicCompressionBlock | ||||
| { | ||||
|     be<uint32_t> dataSize; | ||||
|     be<uint32_t> zeroSize; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2FileBasicCompressionInfo | ||||
| { | ||||
|     Xex2FileBasicCompressionBlock firstBlock; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2CompressedBlockInfo | ||||
| { | ||||
|     be<uint32_t> blockSize; | ||||
|     uint8_t blockHash[20]; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2FileNormalCompressionInfo | ||||
| { | ||||
|     be<uint32_t> windowSize; | ||||
|     Xex2CompressedBlockInfo firstBlock; | ||||
| }; | ||||
| 
 | ||||
| struct Xex2OptFileFormatInfo | ||||
| { | ||||
|     be<uint32_t> infoSize; | ||||
|     be<uint16_t> encryptionType; | ||||
|     be<uint16_t> compressionType; | ||||
|     union | ||||
|     { | ||||
|         Xex2FileBasicCompressionInfo basic; | ||||
|         Xex2FileNormalCompressionInfo normal; | ||||
|     } compressionInfo; | ||||
| }; | ||||
| 
 | ||||
| static const void *getOptHeaderPtr(std::span<const uint8_t> moduleBytes, uint32_t headerKey) | ||||
| { | ||||
|     if ((headerKey & 0xFF) == 0) | ||||
|     { | ||||
|         assert(false && "Wrong type of method for this key. Expected return value is a number."); | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     const Xex2Header *xex2Header = (const Xex2Header *)(moduleBytes.data()); | ||||
|     for (uint32_t i = 0; i < xex2Header->headerCount; i++) | ||||
|     { | ||||
|         const Xex2OptHeader &optHeader = xex2Header->headers[i]; | ||||
|         if (optHeader.key == headerKey) | ||||
|         { | ||||
|             if ((headerKey & 0xFF) == 1) | ||||
|             { | ||||
|                 return &optHeader.value; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return &moduleBytes.data()[optHeader.offset]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| struct mspack_memory_file | ||||
| { | ||||
|     mspack_system sys; | ||||
|     void *buffer; | ||||
|     size_t bufferSize; | ||||
|     size_t offset; | ||||
| }; | ||||
| 
 | ||||
| static mspack_memory_file *mspack_memory_open(mspack_system *sys, void *buffer, size_t bufferSize) | ||||
| { | ||||
|     assert(bufferSize < INT_MAX); | ||||
| 
 | ||||
|     if (bufferSize >= INT_MAX) | ||||
|     { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     mspack_memory_file *memoryFile = (mspack_memory_file *)(std::calloc(1, sizeof(mspack_memory_file))); | ||||
|     if (memoryFile == nullptr) | ||||
|     { | ||||
|         return memoryFile; | ||||
|     } | ||||
| 
 | ||||
|     memoryFile->buffer = buffer; | ||||
|     memoryFile->bufferSize = bufferSize; | ||||
|     memoryFile->offset = 0; | ||||
|     return memoryFile; | ||||
| } | ||||
| 
 | ||||
| static void mspack_memory_close(mspack_memory_file *file) | ||||
| { | ||||
|     std::free(file); | ||||
| } | ||||
| 
 | ||||
| static int mspack_memory_read(mspack_file *file, void *buffer, int chars) | ||||
| { | ||||
|     mspack_memory_file *memoryFile = (mspack_memory_file *)(file); | ||||
|     const size_t remaining = memoryFile->bufferSize - memoryFile->offset; | ||||
|     const size_t total = std::min(size_t(chars), remaining); | ||||
|     std::memcpy(buffer, (uint8_t *)(memoryFile->buffer) + memoryFile->offset, total); | ||||
|     memoryFile->offset += total; | ||||
|     return int(total); | ||||
| } | ||||
| 
 | ||||
| static int mspack_memory_write(mspack_file *file, void *buffer, int chars) | ||||
| { | ||||
|     mspack_memory_file *memoryFile = (mspack_memory_file *)(file); | ||||
|     const size_t remaining = memoryFile->bufferSize - memoryFile->offset; | ||||
|     const size_t total = std::min(size_t(chars), remaining); | ||||
|     std::memcpy((uint8_t *)(memoryFile->buffer) + memoryFile->offset, buffer, total); | ||||
|     memoryFile->offset += total; | ||||
|     return int(total); | ||||
| } | ||||
| 
 | ||||
| static void *mspack_memory_alloc(mspack_system *sys, size_t chars) | ||||
| { | ||||
|     return std::calloc(chars, 1); | ||||
| } | ||||
| 
 | ||||
| static void mspack_memory_free(void *ptr) | ||||
| { | ||||
|     std::free(ptr); | ||||
| } | ||||
| 
 | ||||
| static void mspack_memory_copy(void *src, void *dest, size_t chars) | ||||
| { | ||||
|     std::memcpy(dest, src, chars); | ||||
| } | ||||
| 
 | ||||
| static mspack_system *mspack_memory_sys_create() | ||||
| { | ||||
|     auto sys = (mspack_system *)(std::calloc(1, sizeof(mspack_system))); | ||||
|     if (!sys) | ||||
|     { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     sys->read = mspack_memory_read; | ||||
|     sys->write = mspack_memory_write; | ||||
|     sys->alloc = mspack_memory_alloc; | ||||
|     sys->free = mspack_memory_free; | ||||
|     sys->copy = mspack_memory_copy; | ||||
|     return sys; | ||||
| } | ||||
| 
 | ||||
| static void mspack_memory_sys_destroy(struct mspack_system *sys) | ||||
| { | ||||
|     free(sys); | ||||
| } | ||||
| 
 | ||||
| #if defined(_WIN32) | ||||
| inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex) | ||||
| { | ||||
|     return _BitScanForward((unsigned long *)(outFirstSetIndex), v) != 0; | ||||
| } | ||||
| 
 | ||||
| inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex) | ||||
| { | ||||
|     return _BitScanForward64((unsigned long *)(outFirstSetIndex), v) != 0; | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| inline bool bitScanForward(uint32_t v, uint32_t *outFirstSetIndex) | ||||
| { | ||||
|     int i = ffs(v); | ||||
|     *out_first_set_index = i - 1; | ||||
|     return i != 0; | ||||
| } | ||||
| 
 | ||||
| inline bool bitScanForward(uint64_t v, uint32_t *outFirstSetIndex) | ||||
| { | ||||
|     int i = __builtin_ffsll(v); | ||||
|     *out_first_set_index = i - 1; | ||||
|     return i != 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static int lzxDecompress(const void *lzxData, size_t lzxLength, void *dst, size_t dstLength, uint32_t windowSize, void *windowData, size_t windowDataLength) | ||||
| { | ||||
|     int resultCode = 1; | ||||
|     uint32_t windowBits; | ||||
|     if (!bitScanForward(windowSize, &windowBits)) { | ||||
|         return resultCode; | ||||
|     } | ||||
| 
 | ||||
|     mspack_system *sys = mspack_memory_sys_create(); | ||||
|     mspack_memory_file *lzxSrc = mspack_memory_open(sys, (void *)(lzxData), lzxLength); | ||||
|     mspack_memory_file *lzxDst = mspack_memory_open(sys, dst, dstLength); | ||||
|     lzxd_stream *lzxd = lzxd_init(sys, (mspack_file *)(lzxSrc), (mspack_file *)(lzxDst), windowBits, 0, 0x8000, dstLength, 0); | ||||
|     if (lzxd != nullptr) { | ||||
|         if (windowData != nullptr) { | ||||
|             size_t paddingLength = windowSize - windowDataLength; | ||||
|             std::memset(&lzxd->window[0], 0, paddingLength); | ||||
|             std::memcpy(&lzxd->window[paddingLength], windowData, windowDataLength); | ||||
|             lzxd->ref_data_size = windowSize; | ||||
|         } | ||||
| 
 | ||||
|         resultCode = lzxd_decompress(lzxd, dstLength); | ||||
|         lzxd_free(lzxd); | ||||
|     } | ||||
| 
 | ||||
|     if (lzxSrc) { | ||||
|         mspack_memory_close(lzxSrc); | ||||
|     } | ||||
| 
 | ||||
|     if (lzxDst) { | ||||
|         mspack_memory_close(lzxDst); | ||||
|     } | ||||
| 
 | ||||
|     if (sys) { | ||||
|         mspack_memory_sys_destroy(sys); | ||||
|     } | ||||
| 
 | ||||
|     return resultCode; | ||||
| } | ||||
| 
 | ||||
| static int lzxDeltaApplyPatch(const Xex2DeltaPatch *deltaPatch, uint32_t patchLength, uint32_t windowSize, uint8_t *dstData) | ||||
| { | ||||
|     const void *patchEnd = (const uint8_t *)(deltaPatch) + patchLength; | ||||
|     const Xex2DeltaPatch *curPatch = deltaPatch; | ||||
|     while (patchEnd > curPatch) | ||||
|     { | ||||
|         int patchSize = -4;  | ||||
|         if (curPatch->compressedLength == 0 && curPatch->uncompressedLength == 0 && curPatch->newAddress == 0 && curPatch->oldAddress == 0) | ||||
|         { | ||||
|             // End of patch.
 | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         switch (curPatch->compressedLength) | ||||
|         { | ||||
|         case 0: | ||||
|             // Set the data to zeroes.
 | ||||
|             std::memset(&dstData[curPatch->newAddress], 0, curPatch->uncompressedLength); | ||||
|             break; | ||||
|         case 1: | ||||
|             // Move the data.
 | ||||
|             std::memcpy(&dstData[curPatch->newAddress], &dstData[curPatch->oldAddress], curPatch->uncompressedLength); | ||||
|             break; | ||||
|         default: | ||||
|             // Decompress the data into the destination.
 | ||||
|             patchSize = curPatch->compressedLength - 4; | ||||
|             int result = lzxDecompress(curPatch->patchData, curPatch->compressedLength, &dstData[curPatch->newAddress], curPatch->uncompressedLength, windowSize, &dstData[curPatch->oldAddress], curPatch->uncompressedLength); | ||||
|             if (result != 0) | ||||
|             { | ||||
|                 return result; | ||||
|             } | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         curPatch++; | ||||
|         curPatch = (const Xex2DeltaPatch *)((const uint8_t *)(curPatch) + patchSize); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| XexPatcher::Result XexPatcher::apply(std::span<const uint8_t> xexBytes, std::span<const uint8_t> patchBytes, std::vector<uint8_t> &outBytes, bool skipData) | ||||
| { | ||||
|     // Validate headers.
 | ||||
|     static const char Xex2Magic[] = "XEX2"; | ||||
|     const Xex2Header *xexHeader = (const Xex2Header *)(xexBytes.data()); | ||||
|     if (memcmp(xexBytes.data(), Xex2Magic, 4) != 0) | ||||
|     { | ||||
|         return Result::XexFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     const Xex2Header *patchHeader = (const Xex2Header *)(patchBytes.data()); | ||||
|     if (memcmp(patchBytes.data(), Xex2Magic, 4) != 0) | ||||
|     { | ||||
|         return Result::PatchFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     if ((patchHeader->moduleFlags & (XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA | XEX_MODULE_PATCH_FULL)) == 0) | ||||
|     { | ||||
|         return Result::PatchFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     // Validate patch.
 | ||||
|     const Xex2OptDeltaPatchDescriptor *patchDescriptor = (const Xex2OptDeltaPatchDescriptor *)(getOptHeaderPtr(patchBytes, XEX_HEADER_DELTA_PATCH_DESCRIPTOR)); | ||||
|     if (patchDescriptor == nullptr) | ||||
|     { | ||||
|         return Result::PatchFileInvalid; | ||||
|     } | ||||
|      | ||||
|     const Xex2OptFileFormatInfo *patchFileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(patchBytes, XEX_HEADER_FILE_FORMAT_INFO)); | ||||
|     if (patchFileFormatInfo == nullptr) | ||||
|     { | ||||
|         return Result::PatchFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     if (patchFileFormatInfo->compressionType != XEX_COMPRESSION_DELTA) | ||||
|     { | ||||
|         return Result::PatchFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     if (patchDescriptor->deltaHeadersSourceOffset > xexHeader->headerSize) | ||||
|     { | ||||
|         return Result::PatchIncompatible; | ||||
|     } | ||||
| 
 | ||||
|     if (patchDescriptor->deltaHeadersSourceSize > (xexHeader->headerSize - patchDescriptor->deltaHeadersSourceOffset)) | ||||
|     { | ||||
|         return Result::PatchIncompatible; | ||||
|     } | ||||
| 
 | ||||
|     if (patchDescriptor->deltaHeadersTargetOffset > patchDescriptor->sizeOfTargetHeaders) | ||||
|     { | ||||
|         return Result::PatchIncompatible; | ||||
|     } | ||||
| 
 | ||||
|     uint32_t deltaTargetSize = patchDescriptor->sizeOfTargetHeaders - patchDescriptor->deltaHeadersTargetOffset; | ||||
|     if (patchDescriptor->deltaHeadersSourceSize > deltaTargetSize) | ||||
|     { | ||||
|         return Result::PatchIncompatible; | ||||
|     } | ||||
| 
 | ||||
|     // Apply patch.
 | ||||
|     uint32_t headerTargetSize = patchDescriptor->sizeOfTargetHeaders; | ||||
|     if (headerTargetSize == 0) | ||||
|     { | ||||
|         headerTargetSize = patchDescriptor->deltaHeadersTargetOffset + patchDescriptor->deltaHeadersSourceSize; | ||||
|     } | ||||
| 
 | ||||
|     // Create the bytes for the new XEX header. Copy over the existing data.
 | ||||
|     uint32_t newXexHeaderSize = std::max(headerTargetSize, xexHeader->headerSize.get()); | ||||
|     outBytes.resize(newXexHeaderSize); | ||||
|     memset(outBytes.data(), 0, newXexHeaderSize); | ||||
|     memcpy(outBytes.data(), xexBytes.data(), headerTargetSize); | ||||
| 
 | ||||
|     Xex2Header *newXexHeader = (Xex2Header *)(outBytes.data()); | ||||
|     if (patchDescriptor->deltaHeadersSourceOffset > 0) | ||||
|     { | ||||
|         memcpy(&outBytes[patchDescriptor->deltaHeadersTargetOffset], &outBytes[patchDescriptor->deltaHeadersSourceOffset], patchDescriptor->deltaHeadersSourceSize); | ||||
|     } | ||||
| 
 | ||||
|     int resultCode = lzxDeltaApplyPatch(&patchDescriptor->info, patchDescriptor->size, patchFileFormatInfo->compressionInfo.normal.windowSize, outBytes.data()); | ||||
|     if (resultCode != 0) | ||||
|     { | ||||
|         return Result::PatchFailed; | ||||
|     } | ||||
| 
 | ||||
|     // Make the header the specified size by the patch.
 | ||||
|     outBytes.resize(headerTargetSize); | ||||
|     newXexHeader = (Xex2Header *)(outBytes.data()); | ||||
| 
 | ||||
|     // Copy the rest of the data.
 | ||||
|     const Xex2SecurityInfo *newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]); | ||||
|     outBytes.resize(outBytes.size() + newSecurityInfo->imageSize); | ||||
|     memset(&outBytes[headerTargetSize], 0, outBytes.size() - headerTargetSize); | ||||
|     memcpy(&outBytes[headerTargetSize], &xexBytes[xexHeader->headerSize], xexBytes.size() - xexHeader->headerSize); | ||||
|     newXexHeader = (Xex2Header *)(outBytes.data()); | ||||
|     newSecurityInfo = (const Xex2SecurityInfo *)(&outBytes[newXexHeader->securityOffset]); | ||||
|      | ||||
|     // Decrypt the keys and validate that the patch is compatible with the base file.
 | ||||
|     static const uint32_t KeySize = 16; | ||||
|     static const uint8_t Xex2RetailKey[16] = { 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91 }; | ||||
|     static const uint8_t AESBlankIV[AES_BLOCKLEN] = {}; | ||||
|     const Xex2SecurityInfo *originalSecurityInfo = (const Xex2SecurityInfo *)(&xexBytes[xexHeader->securityOffset]); | ||||
|     const Xex2SecurityInfo *patchSecurityInfo = (const Xex2SecurityInfo *)(&patchBytes[patchHeader->securityOffset]); | ||||
|     uint8_t decryptedOriginalKey[KeySize]; | ||||
|     uint8_t decryptedNewKey[KeySize]; | ||||
|     uint8_t decryptedPatchKey[KeySize]; | ||||
|     uint8_t decrpytedImageKeySource[KeySize]; | ||||
|     memcpy(decryptedOriginalKey, originalSecurityInfo->aesKey, KeySize); | ||||
|     memcpy(decryptedNewKey, newSecurityInfo->aesKey, KeySize); | ||||
|     memcpy(decryptedPatchKey, patchSecurityInfo->aesKey, KeySize); | ||||
|     memcpy(decrpytedImageKeySource, patchDescriptor->imageKeySource, KeySize); | ||||
| 
 | ||||
|     AES_ctx aesContext; | ||||
|     AES_init_ctx_iv(&aesContext, Xex2RetailKey, AESBlankIV); | ||||
|     AES_CBC_decrypt_buffer(&aesContext, decryptedOriginalKey, KeySize); | ||||
| 
 | ||||
|     AES_ctx_set_iv(&aesContext, AESBlankIV); | ||||
|     AES_CBC_decrypt_buffer(&aesContext, decryptedNewKey, KeySize); | ||||
| 
 | ||||
|     AES_init_ctx_iv(&aesContext, decryptedNewKey, AESBlankIV); | ||||
|     AES_CBC_decrypt_buffer(&aesContext, decryptedPatchKey, KeySize); | ||||
| 
 | ||||
|     AES_ctx_set_iv(&aesContext, AESBlankIV); | ||||
|     AES_CBC_decrypt_buffer(&aesContext, decrpytedImageKeySource, KeySize); | ||||
| 
 | ||||
|     // Validate the patch's key matches the one from the original XEX.
 | ||||
|     if (memcmp(decrpytedImageKeySource, decryptedOriginalKey, KeySize) != 0) | ||||
|     { | ||||
|         return Result::PatchIncompatible; | ||||
|     } | ||||
| 
 | ||||
|     // Don't process the rest of the patch.
 | ||||
|     if (skipData) | ||||
|     { | ||||
|         return Result::Success; | ||||
|     } | ||||
|      | ||||
|     // Decrypt base XEX if necessary.
 | ||||
|     const Xex2OptFileFormatInfo *fileFormatInfo = (const Xex2OptFileFormatInfo *)(getOptHeaderPtr(xexBytes, XEX_HEADER_FILE_FORMAT_INFO)); | ||||
|     if (fileFormatInfo == nullptr) | ||||
|     { | ||||
|         return Result::XexFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     if (fileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL) | ||||
|     { | ||||
|         AES_init_ctx_iv(&aesContext, decryptedOriginalKey, AESBlankIV); | ||||
|         AES_CBC_decrypt_buffer(&aesContext, &outBytes[headerTargetSize], xexBytes.size() - xexHeader->headerSize); | ||||
|     } | ||||
|     else if (fileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE) | ||||
|     { | ||||
|         return Result::XexFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     // Decompress base XEX if necessary.
 | ||||
|     if (fileFormatInfo->compressionType == XEX_COMPRESSION_BASIC) | ||||
|     { | ||||
|         const Xex2FileBasicCompressionBlock *blocks = &fileFormatInfo->compressionInfo.basic.firstBlock; | ||||
|         int32_t numBlocks = (fileFormatInfo->infoSize / sizeof(Xex2FileBasicCompressionBlock)) - 1; | ||||
|         int32_t baseCompressedSize = 0; | ||||
|         int32_t baseImageSize = 0; | ||||
|         for (int32_t i = 0; i < numBlocks; i++) { | ||||
|             baseCompressedSize += blocks[i].dataSize; | ||||
|             baseImageSize += blocks[i].dataSize + blocks[i].zeroSize; | ||||
|         } | ||||
| 
 | ||||
|         if (outBytes.size() < (headerTargetSize + baseImageSize)) | ||||
|         { | ||||
|             return Result::XexFileInvalid; | ||||
|         } | ||||
|          | ||||
|         // Reverse iteration allows to perform this decompression in place.
 | ||||
|         uint8_t *srcDataCursor = outBytes.data() + headerTargetSize + baseCompressedSize; | ||||
|         uint8_t *outDataCursor = outBytes.data() + headerTargetSize + baseImageSize; | ||||
|         for (int32_t i = numBlocks - 1; i >= 0; i--) | ||||
|         { | ||||
|             outDataCursor -= blocks[i].zeroSize; | ||||
|             memset(outDataCursor, 0, blocks[i].zeroSize); | ||||
|             outDataCursor -= blocks[i].dataSize; | ||||
|             srcDataCursor -= blocks[i].dataSize; | ||||
|             memmove(outDataCursor, srcDataCursor, blocks[i].dataSize); | ||||
|         } | ||||
|     } | ||||
|     else if (fileFormatInfo->compressionType == XEX_COMPRESSION_NORMAL || fileFormatInfo->compressionType == XEX_COMPRESSION_DELTA) | ||||
|     { | ||||
|         return Result::XexFileUnsupported; | ||||
|     } | ||||
|     else if (fileFormatInfo->compressionType != XEX_COMPRESSION_NONE) | ||||
|     { | ||||
|         return Result::XexFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     Xex2OptFileFormatInfo *newFileFormatInfo = (Xex2OptFileFormatInfo *)(getOptHeaderPtr(outBytes, XEX_HEADER_FILE_FORMAT_INFO)); | ||||
|     if (newFileFormatInfo == nullptr) | ||||
|     { | ||||
|         return Result::PatchFailed; | ||||
|     } | ||||
|      | ||||
|     // Update the header to indicate no encryption or compression is used.
 | ||||
|     newFileFormatInfo->encryptionType = XEX_ENCRYPTION_NONE; | ||||
|     newFileFormatInfo->compressionType = XEX_COMPRESSION_NONE; | ||||
| 
 | ||||
|     // Copy and decrypt patch data if necessary.
 | ||||
|     std::vector<uint8_t> patchData; | ||||
|     patchData.resize(patchBytes.size() - patchHeader->headerSize); | ||||
|     memcpy(patchData.data(), &patchBytes[patchHeader->headerSize], patchData.size()); | ||||
| 
 | ||||
|     if (patchFileFormatInfo->encryptionType == XEX_ENCRYPTION_NORMAL) | ||||
|     { | ||||
|         AES_init_ctx_iv(&aesContext, decryptedPatchKey, AESBlankIV); | ||||
|         AES_CBC_decrypt_buffer(&aesContext, patchData.data(), patchData.size()); | ||||
|     } | ||||
|     else if (patchFileFormatInfo->encryptionType != XEX_ENCRYPTION_NONE) | ||||
|     { | ||||
|         return Result::PatchFileInvalid; | ||||
|     } | ||||
| 
 | ||||
|     const Xex2CompressedBlockInfo *currentBlock = &patchFileFormatInfo->compressionInfo.normal.firstBlock; | ||||
|     uint8_t *outExe = &outBytes[newXexHeader->headerSize]; | ||||
|     if (patchDescriptor->deltaImageSourceOffset > 0) | ||||
|     { | ||||
|         memcpy(&outExe[patchDescriptor->deltaImageTargetOffset], &outExe[patchDescriptor->deltaImageSourceOffset], patchDescriptor->deltaImageSourceSize); | ||||
|     } | ||||
| 
 | ||||
|     static const uint32_t DigestSize = 20; | ||||
|     uint8_t sha1Digest[DigestSize]; | ||||
|     sha1::SHA1 sha1Context; | ||||
|     uint8_t *patchDataCursor = patchData.data(); | ||||
|     while (currentBlock->blockSize > 0) | ||||
|     { | ||||
|         const Xex2CompressedBlockInfo *nextBlock = (const Xex2CompressedBlockInfo *)(patchDataCursor); | ||||
| 
 | ||||
|         // Hash and validate the block.
 | ||||
|         sha1Context.reset(); | ||||
|         sha1Context.processBytes(patchDataCursor, currentBlock->blockSize); | ||||
|         sha1Context.finalize(sha1Digest); | ||||
|         if (memcmp(sha1Digest, currentBlock->blockHash, DigestSize) != 0) | ||||
|         { | ||||
|             return Result::PatchFailed; | ||||
|         } | ||||
| 
 | ||||
|         patchDataCursor += 24; | ||||
| 
 | ||||
|         // Apply the block's patch data.
 | ||||
|         uint32_t blockDataSize = currentBlock->blockSize - 24; | ||||
|         if (lzxDeltaApplyPatch((const Xex2DeltaPatch *)(patchDataCursor), blockDataSize, patchFileFormatInfo->compressionInfo.normal.windowSize, outExe) != 0) | ||||
|         { | ||||
|             return Result::PatchFailed; | ||||
|         } | ||||
| 
 | ||||
|         patchDataCursor += blockDataSize; | ||||
|         currentBlock = nextBlock; | ||||
|     } | ||||
| 
 | ||||
|     return Result::Success; | ||||
| } | ||||
| 
 | ||||
| XexPatcher::Result XexPatcher::apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath) | ||||
| { | ||||
|     MemoryMappedFile baseXexFile(baseXexPath); | ||||
|     MemoryMappedFile patchFile(patchXexPath); | ||||
|     if (!baseXexFile.isOpen() || !patchFile.isOpen()) | ||||
|     { | ||||
|         return Result::FileOpenFailed; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<uint8_t> newXexBytes; | ||||
|     Result result = apply({ baseXexFile.data(), baseXexFile.size() }, { patchFile.data(), patchFile.size() }, newXexBytes, false); | ||||
|     if (result != Result::Success) | ||||
|     { | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     std::ofstream newXexFile(newXexPath, std::ios::binary); | ||||
|     if (!newXexFile.is_open()) | ||||
|     { | ||||
|         return Result::FileOpenFailed; | ||||
|     } | ||||
| 
 | ||||
|     newXexFile.write((const char *)(newXexBytes.data()), newXexBytes.size()); | ||||
|     newXexFile.close(); | ||||
| 
 | ||||
|     if (newXexFile.bad()) | ||||
|     { | ||||
|         std::filesystem::remove(newXexPath); | ||||
|         return Result::FileWriteFailed; | ||||
|     } | ||||
| 
 | ||||
|     return Result::Success; | ||||
| } | ||||
							
								
								
									
										35
									
								
								UnleashedRecomp/install/xex_patcher.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								UnleashedRecomp/install/xex_patcher.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| // Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/cpu/xex_module.cc
 | ||||
| 
 | ||||
| /**
 | ||||
|  ****************************************************************************** | ||||
|  * Xenia : Xbox 360 Emulator Research Project                                 * | ||||
|  ****************************************************************************** | ||||
|  * Copyright 2023 Ben Vanik. All rights reserved.                             * | ||||
|  * Released under the BSD license - see LICENSE in the root for more details. * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <filesystem> | ||||
| #include <span> | ||||
| #include <vector> | ||||
| 
 | ||||
| struct XexPatcher | ||||
| { | ||||
|     enum class Result { | ||||
|         Success, | ||||
|         FileOpenFailed, | ||||
|         FileWriteFailed, | ||||
|         XexFileUnsupported, | ||||
|         XexFileInvalid, | ||||
|         PatchFileInvalid, | ||||
|         PatchIncompatible, | ||||
|         PatchFailed, | ||||
|         PatchUnsupported | ||||
|     }; | ||||
| 
 | ||||
|     static Result apply(std::span<const uint8_t> xexBytes, std::span<const uint8_t> patchBytes, std::vector<uint8_t> &outBytes, bool skipData); | ||||
|     static Result apply(const std::filesystem::path &baseXexPath, const std::filesystem::path &patchXexPath, const std::filesystem::path &newXexPath); | ||||
| }; | ||||
|  | @ -12,6 +12,7 @@ | |||
| #include <apu/audio.h> | ||||
| #include <hid/hid.h> | ||||
| #include <cfg/config.h> | ||||
| #include <install/installer.h> | ||||
| 
 | ||||
| #define GAME_XEX_PATH "game:\\default.xex" | ||||
| 
 | ||||
|  | @ -96,13 +97,15 @@ uint32_t LdrLoadModule(const char* path) | |||
|     auto format = Xex2FindOptionalHeader<XEX_FILE_FORMAT_INFO>(xex, XEX_HEADER_FILE_FORMAT_INFO); | ||||
|     auto entry = *Xex2FindOptionalHeader<uint32_t>(xex, XEX_HEADER_ENTRY_POINT); | ||||
|     ByteSwap(entry); | ||||
|     assert(format->CompressionType >= 1); | ||||
| 
 | ||||
|     if (format->CompressionType == 1) | ||||
|     auto srcData = (char *)xex + xex->SizeOfHeader; | ||||
|     auto destData = (char *)g_memory.Translate(security->ImageBase); | ||||
|     if (format->CompressionType == 0) | ||||
|     { | ||||
|         memcpy(destData, srcData, security->SizeOfImage); | ||||
|     } | ||||
|     else if (format->CompressionType == 1) | ||||
|     { | ||||
|         auto srcData = (char*)xex + xex->SizeOfHeader; | ||||
|         auto destData = (char*)g_memory.Translate(security->ImageBase); | ||||
| 
 | ||||
|         auto numBlocks = (format->SizeOfHeader / sizeof(XEX_BASIC_FILE_COMPRESSION_INFO)) - 1; | ||||
|         auto blocks = reinterpret_cast<const XEX_BASIC_FILE_COMPRESSION_INFO*>(format + 1); | ||||
| 
 | ||||
|  | @ -117,6 +120,10 @@ uint32_t LdrLoadModule(const char* path) | |||
|             destData += blocks[i].SizeOfPadding; | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         assert(false && "Unknown compression type."); | ||||
|     } | ||||
| 
 | ||||
|     return entry; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										5
									
								
								thirdparty/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								thirdparty/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							|  | @ -1,3 +1,4 @@ | |||
| add_subdirectory(${SWA_THIRDPARTY_ROOT}/PowerRecomp) | ||||
| add_subdirectory(${SWA_THIRDPARTY_ROOT}/ShaderRecomp) | ||||
| add_subdirectory(${SWA_THIRDPARTY_ROOT}/o1heap) | ||||
| add_subdirectory(${SWA_THIRDPARTY_ROOT}/ShaderRecomp) | ||||
| add_subdirectory(${SWA_THIRDPARTY_ROOT}/o1heap) | ||||
| add_subdirectory(${SWA_THIRDPARTY_ROOT}/fshasher) | ||||
|  |  | |||
							
								
								
									
										2
									
								
								thirdparty/PowerRecomp
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								thirdparty/PowerRecomp
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | |||
| Subproject commit 7dd4f91ac635b001a56cc7a27af48f0436bbad3f | ||||
| Subproject commit 675b482ec4852b873590fb999d24b426bade2b3a | ||||
							
								
								
									
										2
									
								
								thirdparty/ShaderRecomp
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								thirdparty/ShaderRecomp
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +1 @@ | |||
| Subproject commit f936ed2212d8291439003eb0c0d8edc0ecafd24d | ||||
| Subproject commit 30f598604767602e3afce56b947e99dba2b51211 | ||||
							
								
								
									
										223
									
								
								thirdparty/TinySHA1/TinySHA1.hpp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								thirdparty/TinySHA1/TinySHA1.hpp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,223 @@ | |||
| /*
 | ||||
|  * | ||||
|  * TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based | ||||
|  * on the implementation in boost::uuid::details. | ||||
|  * | ||||
|  * SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1
 | ||||
|  * | ||||
|  * Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
|  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
|  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  * | ||||
|  * Taken from https://github.com/mohaps/TinySHA1
 | ||||
|  * Modified for use by Xenia | ||||
|  */ | ||||
| #ifndef _TINY_SHA1_HPP_ | ||||
| #define _TINY_SHA1_HPP_ | ||||
| 
 | ||||
| #include <cstdio> | ||||
| #include <cstdlib> | ||||
| #include <cstring> | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| namespace sha1 { | ||||
| class SHA1 { | ||||
|  public: | ||||
|   typedef uint32_t digest32_t[5]; | ||||
|   typedef uint8_t digest8_t[20]; | ||||
|   inline static uint32_t LeftRotate(uint32_t value, size_t count) { | ||||
|     return (value << count) ^ (value >> (32 - count)); | ||||
|   } | ||||
|   SHA1() { reset(); } | ||||
|   virtual ~SHA1() {} | ||||
|   SHA1(const SHA1& s) { *this = s; } | ||||
|   const SHA1& operator=(const SHA1& s) { | ||||
|     memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t)); | ||||
|     memcpy(m_block, s.m_block, 64); | ||||
|     m_blockByteIndex = s.m_blockByteIndex; | ||||
|     m_byteCount = s.m_byteCount; | ||||
| 
 | ||||
|     return *this; | ||||
|   } | ||||
| 
 | ||||
|   SHA1& init(const uint32_t digest[5], const uint8_t block[64], | ||||
|              uint32_t count) { | ||||
|     std::memcpy(m_digest, digest, 20); | ||||
|     std::memcpy(m_block, block, count % 64); | ||||
|     m_byteCount = count; | ||||
|     m_blockByteIndex = count % 64; | ||||
| 
 | ||||
|     return *this; | ||||
|   } | ||||
| 
 | ||||
|   const uint32_t* getDigest() const { return m_digest; } | ||||
|   const uint8_t* getBlock() const { return m_block; } | ||||
|   size_t getBlockByteIndex() const { return m_blockByteIndex; } | ||||
|   size_t getByteCount() const { return m_byteCount; } | ||||
| 
 | ||||
|   SHA1& reset() { | ||||
|     m_digest[0] = 0x67452301; | ||||
|     m_digest[1] = 0xEFCDAB89; | ||||
|     m_digest[2] = 0x98BADCFE; | ||||
|     m_digest[3] = 0x10325476; | ||||
|     m_digest[4] = 0xC3D2E1F0; | ||||
|     m_blockByteIndex = 0; | ||||
|     m_byteCount = 0; | ||||
|     return *this; | ||||
|   } | ||||
| 
 | ||||
|   SHA1& processByte(uint8_t octet) { | ||||
|     this->m_block[this->m_blockByteIndex++] = octet; | ||||
|     ++this->m_byteCount; | ||||
|     if (m_blockByteIndex == 64) { | ||||
|       this->m_blockByteIndex = 0; | ||||
|       processBlock(); | ||||
|     } | ||||
| 
 | ||||
|     return *this; | ||||
|   } | ||||
| 
 | ||||
|   SHA1& processBlock(const void* const start, const void* const end) { | ||||
|     const uint8_t* begin = static_cast<const uint8_t*>(start); | ||||
|     const uint8_t* finish = static_cast<const uint8_t*>(end); | ||||
|     while (begin != finish) { | ||||
|       processByte(*begin); | ||||
|       begin++; | ||||
|     } | ||||
|     return *this; | ||||
|   } | ||||
| 
 | ||||
|   SHA1& processBytes(const void* const data, size_t len) { | ||||
|     const uint8_t* block = static_cast<const uint8_t*>(data); | ||||
|     processBlock(block, block + len); | ||||
|     return *this; | ||||
|   } | ||||
| 
 | ||||
|   const uint32_t* finalize(digest32_t digest) { | ||||
|     size_t bitCount = this->m_byteCount * 8; | ||||
|     processByte(0x80); | ||||
|     if (this->m_blockByteIndex > 56) { | ||||
|       while (m_blockByteIndex != 0) { | ||||
|         processByte(0); | ||||
|       } | ||||
|       while (m_blockByteIndex < 56) { | ||||
|         processByte(0); | ||||
|       } | ||||
|     } else { | ||||
|       while (m_blockByteIndex < 56) { | ||||
|         processByte(0); | ||||
|       } | ||||
|     } | ||||
|     processByte(0); | ||||
|     processByte(0); | ||||
|     processByte(0); | ||||
|     processByte(0); | ||||
|     processByte(static_cast<unsigned char>((bitCount >> 24) & 0xFF)); | ||||
|     processByte(static_cast<unsigned char>((bitCount >> 16) & 0xFF)); | ||||
|     processByte(static_cast<unsigned char>((bitCount >> 8) & 0xFF)); | ||||
|     processByte(static_cast<unsigned char>((bitCount)&0xFF)); | ||||
| 
 | ||||
|     memcpy(digest, m_digest, 5 * sizeof(uint32_t)); | ||||
|     return digest; | ||||
|   } | ||||
| 
 | ||||
|   const uint8_t* finalize(digest8_t digest) { | ||||
|     digest32_t d32; | ||||
|     finalize(d32); | ||||
|     size_t di = 0; | ||||
|     digest[di++] = ((d32[0] >> 24) & 0xFF); | ||||
|     digest[di++] = ((d32[0] >> 16) & 0xFF); | ||||
|     digest[di++] = ((d32[0] >> 8) & 0xFF); | ||||
|     digest[di++] = ((d32[0]) & 0xFF); | ||||
| 
 | ||||
|     digest[di++] = ((d32[1] >> 24) & 0xFF); | ||||
|     digest[di++] = ((d32[1] >> 16) & 0xFF); | ||||
|     digest[di++] = ((d32[1] >> 8) & 0xFF); | ||||
|     digest[di++] = ((d32[1]) & 0xFF); | ||||
| 
 | ||||
|     digest[di++] = ((d32[2] >> 24) & 0xFF); | ||||
|     digest[di++] = ((d32[2] >> 16) & 0xFF); | ||||
|     digest[di++] = ((d32[2] >> 8) & 0xFF); | ||||
|     digest[di++] = ((d32[2]) & 0xFF); | ||||
| 
 | ||||
|     digest[di++] = ((d32[3] >> 24) & 0xFF); | ||||
|     digest[di++] = ((d32[3] >> 16) & 0xFF); | ||||
|     digest[di++] = ((d32[3] >> 8) & 0xFF); | ||||
|     digest[di++] = ((d32[3]) & 0xFF); | ||||
| 
 | ||||
|     digest[di++] = ((d32[4] >> 24) & 0xFF); | ||||
|     digest[di++] = ((d32[4] >> 16) & 0xFF); | ||||
|     digest[di++] = ((d32[4] >> 8) & 0xFF); | ||||
|     digest[di++] = ((d32[4]) & 0xFF); | ||||
|     return digest; | ||||
|   } | ||||
| 
 | ||||
|  protected: | ||||
|   void processBlock() { | ||||
|     uint32_t w[80]; | ||||
|     for (size_t i = 0; i < 16; i++) { | ||||
|       w[i] = (m_block[i * 4 + 0] << 24); | ||||
|       w[i] |= (m_block[i * 4 + 1] << 16); | ||||
|       w[i] |= (m_block[i * 4 + 2] << 8); | ||||
|       w[i] |= (m_block[i * 4 + 3]); | ||||
|     } | ||||
|     for (size_t i = 16; i < 80; i++) { | ||||
|       w[i] = LeftRotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); | ||||
|     } | ||||
| 
 | ||||
|     uint32_t a = m_digest[0]; | ||||
|     uint32_t b = m_digest[1]; | ||||
|     uint32_t c = m_digest[2]; | ||||
|     uint32_t d = m_digest[3]; | ||||
|     uint32_t e = m_digest[4]; | ||||
| 
 | ||||
|     for (std::size_t i = 0; i < 80; ++i) { | ||||
|       uint32_t f = 0; | ||||
|       uint32_t k = 0; | ||||
| 
 | ||||
|       if (i < 20) { | ||||
|         f = (b & c) | (~b & d); | ||||
|         k = 0x5A827999; | ||||
|       } else if (i < 40) { | ||||
|         f = b ^ c ^ d; | ||||
|         k = 0x6ED9EBA1; | ||||
|       } else if (i < 60) { | ||||
|         f = (b & c) | (b & d) | (c & d); | ||||
|         k = 0x8F1BBCDC; | ||||
|       } else { | ||||
|         f = b ^ c ^ d; | ||||
|         k = 0xCA62C1D6; | ||||
|       } | ||||
|       uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i]; | ||||
|       e = d; | ||||
|       d = c; | ||||
|       c = LeftRotate(b, 30); | ||||
|       b = a; | ||||
|       a = temp; | ||||
|     } | ||||
| 
 | ||||
|     m_digest[0] += a; | ||||
|     m_digest[1] += b; | ||||
|     m_digest[2] += c; | ||||
|     m_digest[3] += d; | ||||
|     m_digest[4] += e; | ||||
|   } | ||||
| 
 | ||||
|  private: | ||||
|   digest32_t m_digest; | ||||
|   uint8_t m_block[64]; | ||||
|   size_t m_blockByteIndex; | ||||
|   size_t m_byteCount; | ||||
| }; | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										7
									
								
								thirdparty/fshasher/CMakeLists.txt
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								thirdparty/fshasher/CMakeLists.txt
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| project("fshasher") | ||||
| 
 | ||||
| add_executable(fshasher "fshasher.cpp") | ||||
| 
 | ||||
| find_package(xxhash CONFIG REQUIRED) | ||||
| 
 | ||||
| target_link_libraries(fshasher PRIVATE xxHash::xxhash) | ||||
							
								
								
									
										203
									
								
								thirdparty/fshasher/fshasher.cpp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								thirdparty/fshasher/fshasher.cpp
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,203 @@ | |||
| // 
 | ||||
| // fshasher - CLI tool to generate a hash map from a file system.
 | ||||
| // 
 | ||||
| // This is free and unencumbered software released into the public domain.
 | ||||
| // 
 | ||||
| // Anyone is free to copy, modify, publish, use, compile, sell, or
 | ||||
| // distribute this software, either in source code form or as a compiled
 | ||||
| // binary, for any purpose, commercial or non-commercial, and by any
 | ||||
| // means.
 | ||||
| // 
 | ||||
| // In jurisdictions that recognize copyright laws, the author or authors
 | ||||
| // of this software dedicate any and all copyright interest in the
 | ||||
| // software to the public domain. We make this dedication for the benefit
 | ||||
| // of the public at large and to the detriment of our heirs and
 | ||||
| // successors. We intend this dedication to be an overt act of
 | ||||
| // relinquishment in perpetuity of all present and future rights to this
 | ||||
| // software under copyright law.
 | ||||
| // 
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | ||||
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | ||||
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | ||||
| // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | ||||
| // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | ||||
| // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | ||||
| // OTHER DEALINGS IN THE SOFTWARE.
 | ||||
| //
 | ||||
| 
 | ||||
| #include <cstdio> | ||||
| #include <filesystem> | ||||
| #include <fstream> | ||||
| #include <iostream> | ||||
| #include <list> | ||||
| #include <map> | ||||
| #include <set> | ||||
| 
 | ||||
| #include <xxh3.h> | ||||
| 
 | ||||
| #include "plainargs.h" | ||||
| 
 | ||||
| void showHelp() { | ||||
|     std::cout << "fshasher --directory <directory1 directory2 ...> --source <source file> --header <header file> --variable <variable name>" << std::endl; | ||||
| } | ||||
| 
 | ||||
| int process(const std::list<std::filesystem::path> &searchDirectories, std::ofstream &outputSourceStream, std::ofstream &outputHeaderStream, const std::string &variableName) { | ||||
|     auto writeExterns = [&](std::ofstream &outputStream) | ||||
|     { | ||||
|         outputStream << "extern const uint64_t " << variableName << "Hashes[];" << std::endl; | ||||
|         outputStream << "extern const std::pair<const char *, uint32_t> " << variableName << "Files[];" << std::endl; | ||||
|         outputStream << "extern const size_t " << variableName << "FilesSize;" << std::endl << std::endl; | ||||
|     }; | ||||
| 
 | ||||
|     // Generate header.
 | ||||
|     outputHeaderStream << "// File automatically generated by fshasher" << std::endl << std::endl; | ||||
|     outputHeaderStream << "#pragma once" << std::endl << std::endl; | ||||
|     outputHeaderStream << "#include <utility>" << std::endl << std::endl; | ||||
|     writeExterns(outputHeaderStream); | ||||
| 
 | ||||
|     if (outputHeaderStream.bad())  | ||||
|     { | ||||
|         std::cerr << "Failed to write to output header." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     outputSourceStream << "// File automatically generated by fshasher" << std::endl << std::endl; | ||||
|     outputSourceStream << "#include <utility>" << std::endl << std::endl; | ||||
|     writeExterns(outputSourceStream); | ||||
| 
 | ||||
|     std::map<std::u8string, std::set<uint64_t>> fileHashSets; | ||||
|     char fileData[65536]; | ||||
|     XXH3_state_t xxh3; | ||||
|     for (const std::filesystem::path &searchDirectory : searchDirectories) | ||||
|     { | ||||
|         if (!std::filesystem::is_directory(searchDirectory)) | ||||
|         { | ||||
|             std::cerr << "Specified directory " << searchDirectory << " does not exist." << std::endl; | ||||
|             return 1; | ||||
|         } | ||||
| 
 | ||||
|         for (const std::filesystem::directory_entry &entry : std::filesystem::recursive_directory_iterator(searchDirectory)) | ||||
|         { | ||||
|             if (!entry.is_regular_file()) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             std::filesystem::path entryPath = entry.path(); | ||||
|             std::filesystem::path entryRelative = std::filesystem::relative(entryPath, searchDirectory); | ||||
|             std::ifstream entryStream(entryPath, std::ios::binary); | ||||
|             if (!entryStream.is_open()) | ||||
|             { | ||||
|                 std::cerr << "Could not open " << entryPath << " for reading." << std::endl; | ||||
|                 return 1; | ||||
|             } | ||||
| 
 | ||||
|             std::cout << "Reading " << entryRelative << "." << std::endl; | ||||
|             XXH3_64bits_reset(&xxh3); | ||||
|             while (!entryStream.eof() && !entryStream.bad()) | ||||
|             { | ||||
|                 entryStream.read(fileData, sizeof(fileData)); | ||||
|                 XXH3_64bits_update(&xxh3, fileData, entryStream.gcount()); | ||||
|             } | ||||
| 
 | ||||
|             if (entryStream.bad()) | ||||
|             { | ||||
|                 std::cerr << "Could not read " << entryPath << " successfully." << std::endl; | ||||
|                 return 1; | ||||
|             } | ||||
| 
 | ||||
|             std::u8string entryRelativeU8 = entryRelative.u8string(); | ||||
|             std::replace(entryRelativeU8.begin(), entryRelativeU8.end(), '\\', '/'); | ||||
|             fileHashSets[entryRelativeU8].insert(XXH3_64bits_digest(&xxh3)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     outputSourceStream << "const uint64_t " << variableName << "Hashes[] = {" << std::endl; | ||||
| 
 | ||||
|     for (auto &it : fileHashSets) | ||||
|     { | ||||
|         for (uint64_t hash : it.second) | ||||
|         { | ||||
|             outputSourceStream << "    " << hash << "ULL," << std::endl; | ||||
|         } | ||||
| 
 | ||||
|         if (outputSourceStream.bad()) | ||||
|         { | ||||
|             std::cerr << "Failed to write to output source." << std::endl; | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     outputSourceStream << "};" << std::endl << std::endl; | ||||
|     outputSourceStream << "const std::pair<const char *, uint32_t> " << variableName << "Files[] = {" << std::endl; | ||||
| 
 | ||||
|     for (const auto &it : fileHashSets) | ||||
|     { | ||||
|         outputSourceStream << "    { \"" << (const char *)(it.first.c_str()) << "\", " << it.second.size() << " }," << std::endl; | ||||
|         if (outputSourceStream.bad()) | ||||
|         { | ||||
|             std::cerr << "Failed to write to output source." << std::endl; | ||||
|             return 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     outputSourceStream << "};" << std::endl << std::endl; | ||||
|     outputSourceStream << "const size_t " << variableName << "FilesSize = std::size(" << variableName << "Files);" << std::endl; | ||||
| 
 | ||||
|     if (outputSourceStream.bad()) | ||||
|     { | ||||
|         std::cerr << "Failed to write to output source." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
|     plainargs::Result argsResult = plainargs::parse(argc, argv); | ||||
|     std::vector<std::string> directories = argsResult.getValues("directory", "d"); | ||||
|     std::string variable = argsResult.getValue("variable", "v"); | ||||
|     std::string source = argsResult.getValue("source", "s"); | ||||
|     std::string header = argsResult.getValue("header", "h"); | ||||
|     if (directories.empty() || variable.empty() || source.empty() || header.empty()) | ||||
|     { | ||||
|         showHelp(); | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     std::filesystem::path sourcePath(source); | ||||
|     std::ofstream sourceStream(sourcePath); | ||||
|     if (!sourceStream.is_open()) | ||||
|     { | ||||
|         std::cerr << "Could not open " << sourcePath << " for writing." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     std::filesystem::path headerPath(header); | ||||
|     std::ofstream headerStream(headerPath); | ||||
|     if (!headerStream.is_open()) | ||||
|     { | ||||
|         std::cerr << "Could not open " << headerPath << " for writing." << std::endl; | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     std::list<std::filesystem::path> searchDirectories; | ||||
|     for (std::string &directory : directories) | ||||
|     { | ||||
|         searchDirectories.emplace_back(directory); | ||||
|     } | ||||
| 
 | ||||
|     int resultCode = process(searchDirectories, sourceStream, headerStream, variable); | ||||
|     sourceStream.close(); | ||||
|     headerStream.close(); | ||||
| 
 | ||||
|     if (resultCode != 0) | ||||
|     { | ||||
|         std::cerr << "Failed to generate " << sourcePath << "and" << headerPath << "." << std::endl; | ||||
|         std::filesystem::remove(sourcePath); | ||||
|         std::filesystem::remove(headerPath); | ||||
|     } | ||||
| 
 | ||||
|     return resultCode; | ||||
| } | ||||
							
								
								
									
										147
									
								
								thirdparty/fshasher/plainargs.h
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								thirdparty/fshasher/plainargs.h
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,147 @@ | |||
| // 
 | ||||
| // plainargs - A very plain CLI arguments parsing header-only library.
 | ||||
| // 
 | ||||
| // This is free and unencumbered software released into the public domain.
 | ||||
| // 
 | ||||
| // Anyone is free to copy, modify, publish, use, compile, sell, or
 | ||||
| // distribute this software, either in source code form or as a compiled
 | ||||
| // binary, for any purpose, commercial or non-commercial, and by any
 | ||||
| // means.
 | ||||
| // 
 | ||||
| // In jurisdictions that recognize copyright laws, the author or authors
 | ||||
| // of this software dedicate any and all copyright interest in the
 | ||||
| // software to the public domain. We make this dedication for the benefit
 | ||||
| // of the public at large and to the detriment of our heirs and
 | ||||
| // successors. We intend this dedication to be an overt act of
 | ||||
| // relinquishment in perpetuity of all present and future rights to this
 | ||||
| // software under copyright law.
 | ||||
| // 
 | ||||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | ||||
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | ||||
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | ||||
| // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | ||||
| // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | ||||
| // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | ||||
| // OTHER DEALINGS IN THE SOFTWARE.
 | ||||
| //
 | ||||
| 
 | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| namespace plainargs { | ||||
|     class Result { | ||||
|     private: | ||||
|         struct Option { | ||||
|             uint32_t keyIndex; | ||||
|             uint32_t valueCount; | ||||
|         }; | ||||
| 
 | ||||
|         std::string directory; | ||||
|         std::vector<std::string> arguments; | ||||
|         std::vector<Option> options; | ||||
|         std::unordered_map<std::string, uint32_t> shortKeyMap; | ||||
|         std::unordered_map<std::string, uint32_t> longKeyMap; | ||||
|     public: | ||||
|         // Arguments are the same as main().
 | ||||
|         Result(int argc, char *argv[]) { | ||||
|             if (argc < 1) { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             directory = argv[0]; | ||||
| 
 | ||||
|             arguments.resize(size_t(argc - 1)); | ||||
|             for (uint32_t i = 1; i < uint32_t(argc); i++) { | ||||
|                 std::string &argument = arguments[i - 1]; | ||||
|                 argument = std::string(argv[i]); | ||||
| 
 | ||||
|                 if (!argument.empty()) { | ||||
|                     bool shortKey = (argument.size() > 1) && (argument[0] == '-'); | ||||
|                     bool longKey = (argument.size() > 2) && (argument[0] == '-') && (argument[1] == '-'); | ||||
|                     if (longKey) { | ||||
|                         longKeyMap[argument.substr(2)] = uint32_t(options.size()); | ||||
|                         options.emplace_back(Option{ i - 1, 0 }); | ||||
|                     } | ||||
|                     else if (shortKey) { | ||||
|                         shortKeyMap[argument.substr(1)] = uint32_t(options.size()); | ||||
|                         options.emplace_back(Option{ i - 1, 0 }); | ||||
|                     } | ||||
|                     else if (!options.empty()) { | ||||
|                         options.back().valueCount++; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Return all the values associated to the long key or the short key in order.
 | ||||
|         std::vector<std::string> getValues(const std::string &longKey, const std::string &shortKey = "", uint32_t maxValues = 0) const { | ||||
|             std::vector<std::string> values; | ||||
|             auto optionIt = options.end(); | ||||
|             if (!longKey.empty()) { | ||||
|                 auto it = longKeyMap.find(longKey); | ||||
|                 if (it != longKeyMap.end()) { | ||||
|                     optionIt = options.begin() + it->second; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if ((optionIt == options.end()) && !shortKey.empty()) { | ||||
|                 auto it = shortKeyMap.find(shortKey); | ||||
|                 if (it != shortKeyMap.end()) { | ||||
|                     optionIt = options.begin() + it->second; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (optionIt != options.end()) { | ||||
|                 uint32_t valueCount = optionIt->valueCount; | ||||
|                 if ((maxValues > 0) && (valueCount > maxValues)) { | ||||
|                     valueCount = maxValues; | ||||
|                 } | ||||
| 
 | ||||
|                 values.resize(valueCount); | ||||
|                 for (uint32_t i = 0; i < valueCount; i++) { | ||||
|                     values[i] = arguments[optionIt->keyIndex + i + 1]; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return values; | ||||
|         } | ||||
| 
 | ||||
|         std::string getValue(const std::string &longKey, const std::string &shortKey = "") const { | ||||
|             std::vector<std::string> values = getValues(longKey, shortKey, 1); | ||||
|             return !values.empty() ? values.front() : std::string(); | ||||
|         } | ||||
| 
 | ||||
|         // Return whether an option with the long key or short key was specified.
 | ||||
|         bool hasOption(const std::string &longKey, const std::string &shortKey = "") const { | ||||
|             if (!longKey.empty() && (longKeyMap.find(longKey) != longKeyMap.end())) { | ||||
|                 return true; | ||||
|             } | ||||
|             else if (!shortKey.empty() && (shortKeyMap.find(shortKey) != shortKeyMap.end())) { | ||||
|                 return true; | ||||
|             } | ||||
|             else { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Corresponds to argv[0].
 | ||||
|         const std::string &getDirectory() const { | ||||
|             return directory; | ||||
|         } | ||||
| 
 | ||||
|         // No bounds checking, must be a valid index.
 | ||||
|         const std::string getArgument(uint32_t index) const { | ||||
|             return arguments[index]; | ||||
|         } | ||||
| 
 | ||||
|         // Will be one less than argc.
 | ||||
|         uint32_t getArgumentCount() const { | ||||
|             return arguments.size(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     // Parse and return the arguments in a structure that can be queried easily. Does not perform any validation of the arguments.
 | ||||
|     Result parse(int argc, char *argv[]) { | ||||
|         return Result(argc, argv); | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										1
									
								
								thirdparty/libmspack
									
										
									
									
										vendored
									
									
										Submodule
									
								
							
							
						
						
									
										1
									
								
								thirdparty/libmspack
									
										
									
									
										vendored
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | |||
| Subproject commit 305907723a4e7ab2018e58040059ffb5e77db837 | ||||
|  | @ -16,6 +16,7 @@ | |||
|         "zstd", | ||||
|         "stb", | ||||
|         "concurrentqueue", | ||||
|         "tiny-aes-c", | ||||
|         { | ||||
|             "name": "imgui", | ||||
|             "features": [ "sdl2-binding" ] | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Darío
						Darío