mirror of
				https://github.com/PancakeTAS/lsfg-vk.git
				synced 2025-10-30 07:01:10 +00:00 
			
		
		
		
	enhancement(nodeps): remove pe-parse dependency
This commit is contained in:
		
							parent
							
								
									77c9cc632d
								
							
						
					
					
						commit
						82fb53f089
					
				
					 6 changed files with 241 additions and 40 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							|  | @ -1,3 +0,0 @@ | ||||||
| [submodule "thirdparty/pe-parse"] |  | ||||||
| 	path = thirdparty/pe-parse |  | ||||||
| 	url = https://github.com/trailofbits/pe-parse |  | ||||||
|  | @ -12,7 +12,6 @@ add_compile_options(-fPIC | ||||||
|     -Wno-deprecated-declarations |     -Wno-deprecated-declarations | ||||||
|     -Wno-unused-template) |     -Wno-unused-template) | ||||||
| 
 | 
 | ||||||
| add_subdirectory(thirdparty/pe-parse/pe-parser-library EXCLUDE_FROM_ALL) |  | ||||||
| add_subdirectory(framegen) | add_subdirectory(framegen) | ||||||
| 
 | 
 | ||||||
| if(LSFGVK_EXCESS_DEBUG) | if(LSFGVK_EXCESS_DEBUG) | ||||||
|  | @ -44,7 +43,6 @@ target_include_directories(lsfg-vk SYSTEM | ||||||
| target_include_directories(lsfg-vk | target_include_directories(lsfg-vk | ||||||
|     PUBLIC include) |     PUBLIC include) | ||||||
| target_link_libraries(lsfg-vk PUBLIC | target_link_libraries(lsfg-vk PUBLIC | ||||||
|     pe-parse |  | ||||||
|     lsfg-vk-framegen) |     lsfg-vk-framegen) | ||||||
| 
 | 
 | ||||||
| # diagnostics | # diagnostics | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								include/extract/dll.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								include/extract/dll.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | namespace DLL { | ||||||
|  | 
 | ||||||
|  |     ///
 | ||||||
|  |     /// Parse all resources from a DLL file.
 | ||||||
|  |     ///
 | ||||||
|  |     /// *Shouldn't* cause any segmentation faults.
 | ||||||
|  |     ///
 | ||||||
|  |     /// @param filename Path to the DLL file.
 | ||||||
|  |     /// @return A map of resource IDs to their binary data.
 | ||||||
|  |     ///
 | ||||||
|  |     /// @throws std::runtime_error on various failure points.
 | ||||||
|  |     ///
 | ||||||
|  |     std::unordered_map<uint32_t, std::vector<uint8_t>> parse_dll(const std::string& filename); | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										203
									
								
								src/extract/dll.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								src/extract/dll.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,203 @@ | ||||||
|  | #include "extract/dll.hpp" | ||||||
|  | 
 | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <stdexcept> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <iostream> | ||||||
|  | #include <optional> | ||||||
|  | #include <cstddef> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <fstream> | ||||||
|  | #include <utility> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | #include <array> | ||||||
|  | #include <span> | ||||||
|  | 
 | ||||||
|  | /// DOS file header
 | ||||||
|  | struct DOSHeader { | ||||||
|  |     uint16_t magic; // 0x5A4D
 | ||||||
|  |     std::array<uint16_t, 29> pad; | ||||||
|  |     int32_t pe_offset; // file offset
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// PE header
 | ||||||
|  | struct PEHeader { | ||||||
|  |     uint32_t signature; // "PE\0\0"
 | ||||||
|  |     std::array<uint16_t, 1> pad1; | ||||||
|  |     uint16_t sect_count; | ||||||
|  |     std::array<uint16_t, 6> pad2; | ||||||
|  |     uint16_t opt_hdr_size; | ||||||
|  |     std::array<uint16_t, 1> pad3; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// (partial!) PE optional header
 | ||||||
|  | struct PEOptionalHeader { | ||||||
|  |     uint16_t magic; // 0x20B
 | ||||||
|  |     std::array<uint16_t, 63> pad4; | ||||||
|  |     std::pair<uint32_t, uint32_t> resource_table; // file offset/size
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// Section header
 | ||||||
|  | struct SectionHeader { | ||||||
|  |     std::array<uint16_t, 4> pad1; | ||||||
|  |     uint32_t vsize; // virtual
 | ||||||
|  |     uint32_t vaddress; | ||||||
|  |     uint32_t fsize; // raw
 | ||||||
|  |     uint32_t foffset; | ||||||
|  |     std::array<uint16_t, 8> pad2; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// Resource directory
 | ||||||
|  | struct ResourceDirectory { | ||||||
|  |     std::array<uint16_t, 6> pad; | ||||||
|  |     uint16_t name_count; | ||||||
|  |     uint16_t id_count; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// Resource directory entry
 | ||||||
|  | struct ResourceDirectoryEntry { | ||||||
|  |     uint32_t id; | ||||||
|  |     uint32_t offset; // high bit = directory
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// Resource data entry
 | ||||||
|  | struct ResourceDataEntry { | ||||||
|  |     uint32_t offset; | ||||||
|  |     uint32_t size; | ||||||
|  |     std::array<uint32_t, 2> pad; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #pragma clang diagnostic push | ||||||
|  | #pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-container" | ||||||
|  | namespace { | ||||||
|  |     /// Safely cast a vector to a pointer of type T
 | ||||||
|  |     template<typename T> | ||||||
|  |     const T* safe_cast(const std::vector<uint8_t>& data, size_t offset) { | ||||||
|  |         const size_t end = offset + sizeof(T); | ||||||
|  |         if (end > data.size() || end < offset) | ||||||
|  |             throw std::runtime_error("Buffer overflow during safe cast"); | ||||||
|  |         return reinterpret_cast<const T*>(&data.at(offset)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Safely cast a vector to a span of T
 | ||||||
|  |     template<typename T> | ||||||
|  |     std::span<const T> span_cast(const std::vector<uint8_t>& data, size_t offset, size_t count) { | ||||||
|  |         const size_t end = offset + (count * sizeof(T)); | ||||||
|  |         if (end > data.size() || end < offset) | ||||||
|  |             throw std::runtime_error("Buffer overflow during safe cast"); | ||||||
|  |         return std::span<const T>(reinterpret_cast<const T*>(&data.at(offset)), count); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #pragma clang diagnostic pop | ||||||
|  | 
 | ||||||
|  | std::unordered_map<uint32_t, std::vector<uint8_t>> DLL::parse_dll(const std::string& filename) { | ||||||
|  |     std::ifstream file(filename, std::ios::binary | std::ios::ate); | ||||||
|  |     if (!file.is_open()) | ||||||
|  |         throw std::runtime_error("Failed to open Lossless.dll"); | ||||||
|  | 
 | ||||||
|  |     const auto size = file.tellg(); | ||||||
|  |     file.seekg(0, std::ios::beg); | ||||||
|  | 
 | ||||||
|  |     std::vector<uint8_t> data(static_cast<size_t>(size)); | ||||||
|  |     if (!file.read(reinterpret_cast<char*>(data.data()), size)) | ||||||
|  |         throw std::runtime_error("Failed to read Lossless.dll"); | ||||||
|  | 
 | ||||||
|  |     // parse dos header
 | ||||||
|  |     size_t fileOffset = 0; | ||||||
|  |     const auto* dosHdr = safe_cast<const DOSHeader>(data, 0); | ||||||
|  |     if (dosHdr->magic != 0x5A4D) | ||||||
|  |         throw std::runtime_error("Invalid DOS header magic number"); | ||||||
|  | 
 | ||||||
|  |     // parse pe header
 | ||||||
|  |     fileOffset += static_cast<size_t>(dosHdr->pe_offset); | ||||||
|  |     const auto* peHdr = safe_cast<const PEHeader>(data, fileOffset); | ||||||
|  |     if (peHdr->signature != 0x00004550) | ||||||
|  |         throw std::runtime_error("Invalid PE header signature"); | ||||||
|  | 
 | ||||||
|  |     // parse optional pe header
 | ||||||
|  |     fileOffset += sizeof(PEHeader); | ||||||
|  |     const auto* peOptHdr = safe_cast<const PEOptionalHeader>(data, fileOffset); | ||||||
|  |     if (peOptHdr->magic != 0x20B) | ||||||
|  |         throw std::runtime_error("Unsupported PE format (not PE32+)"); | ||||||
|  |     const auto& [rsrc_rva, rsrc_size] = peOptHdr->resource_table; | ||||||
|  | 
 | ||||||
|  |     // locate section containing resources
 | ||||||
|  |     std::optional<size_t> rsrc_offset; | ||||||
|  |     fileOffset += peHdr->opt_hdr_size; | ||||||
|  |     const auto sectHdrs = span_cast<const SectionHeader>(data, fileOffset, peHdr->sect_count); | ||||||
|  |     for (const auto& sectHdr : sectHdrs) { | ||||||
|  |         if (rsrc_rva < sectHdr.vaddress || rsrc_rva > (sectHdr.vaddress + sectHdr.vsize)) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         rsrc_offset.emplace((rsrc_rva - sectHdr.vaddress) + sectHdr.foffset); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     if (!rsrc_offset) | ||||||
|  |         throw std::runtime_error("Failed to locate resource section"); | ||||||
|  | 
 | ||||||
|  |     // parse resource directory
 | ||||||
|  |     fileOffset = rsrc_offset.value(); | ||||||
|  |     const auto* rsrcDir = safe_cast<const ResourceDirectory>(data, fileOffset); | ||||||
|  |     if (rsrcDir->id_count < 3) | ||||||
|  |         throw std::runtime_error("Incorrect resource directory"); | ||||||
|  | 
 | ||||||
|  |     // find resource table with data type
 | ||||||
|  |     std::optional<size_t> rsrc_tbl_offset; | ||||||
|  |     fileOffset = rsrc_offset.value() + sizeof(ResourceDirectory); | ||||||
|  |     const auto rsrcDirEntries = span_cast<const ResourceDirectoryEntry>( | ||||||
|  |         data, fileOffset, rsrcDir->name_count + rsrcDir->id_count); | ||||||
|  |     for (const auto& rsrcDirEntry : rsrcDirEntries) { | ||||||
|  |         if (rsrcDirEntry.id != 10) // RT_RCDATA
 | ||||||
|  |             continue; | ||||||
|  |         if ((rsrcDirEntry.offset & 0x80000000) == 0) | ||||||
|  |             throw std::runtime_error("Expected resource directory, but found data entry"); | ||||||
|  | 
 | ||||||
|  |         rsrc_tbl_offset.emplace(rsrcDirEntry.offset & 0x7FFFFFFF); | ||||||
|  |     } | ||||||
|  |     if (!rsrc_tbl_offset) | ||||||
|  |         throw std::runtime_error("Failed to locate RT_RCDATA directory"); | ||||||
|  | 
 | ||||||
|  |     // parse data type resource directory
 | ||||||
|  |     fileOffset = rsrc_offset.value() + rsrc_tbl_offset.value(); | ||||||
|  |     const auto* rsrcTbl = safe_cast<const ResourceDirectory>(data, fileOffset); | ||||||
|  |     if (rsrcTbl->id_count < 1) | ||||||
|  |         throw std::runtime_error("Incorrect RT_RCDATA directory"); | ||||||
|  | 
 | ||||||
|  |     // collect all resources
 | ||||||
|  |     fileOffset += sizeof(ResourceDirectory); | ||||||
|  |     const auto rsrcTblEntries = span_cast<const ResourceDirectoryEntry>( | ||||||
|  |         data, fileOffset, rsrcTbl->name_count + rsrcTbl->id_count); | ||||||
|  |     std::unordered_map<uint32_t, std::vector<uint8_t>> resources; | ||||||
|  |     for (const auto& rsrcTblEntry : rsrcTblEntries) { | ||||||
|  |         if ((rsrcTblEntry.offset & 0x80000000) == 0) | ||||||
|  |             throw std::runtime_error("Expected resource directory, but found data entry"); | ||||||
|  | 
 | ||||||
|  |         // skip over language directory
 | ||||||
|  |         fileOffset = rsrc_offset.value() + (rsrcTblEntry.offset & 0x7FFFFFFF); | ||||||
|  |         const auto* langDir = safe_cast<const ResourceDirectory>(data, fileOffset); | ||||||
|  |         if (langDir->id_count < 1) | ||||||
|  |             throw std::runtime_error("Incorrect language directory"); | ||||||
|  | 
 | ||||||
|  |         fileOffset += sizeof(ResourceDirectory); | ||||||
|  |         const auto* langDirEntry = safe_cast<const ResourceDirectoryEntry>(data, fileOffset); | ||||||
|  |         if ((langDirEntry->offset & 0x80000000) != 0) | ||||||
|  |             throw std::runtime_error("Expected resource data entry, but found directory"); | ||||||
|  | 
 | ||||||
|  |         // parse resource data entry
 | ||||||
|  |         fileOffset = rsrc_offset.value() + (langDirEntry->offset & 0x7FFFFFFF); | ||||||
|  |         const auto* entry = safe_cast<const ResourceDataEntry>(data, fileOffset); | ||||||
|  |         if (entry->offset < rsrc_rva || entry->offset > (rsrc_rva + rsrc_size)) | ||||||
|  |             throw std::runtime_error("Resource data entry points outside resource section"); | ||||||
|  | 
 | ||||||
|  |         // extract resource
 | ||||||
|  |         std::vector<uint8_t> resource(entry->size); | ||||||
|  |         fileOffset = (entry->offset - rsrc_rva) + rsrc_offset.value(); | ||||||
|  |         if (fileOffset + entry->size > data.size()) | ||||||
|  |             throw std::runtime_error("Resource data entry points outside file"); | ||||||
|  |         std::copy_n(&data.at(fileOffset), entry->size, resource.data()); | ||||||
|  |         resources.emplace(rsrcTblEntry.id, std::move(resource)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return resources; | ||||||
|  | } | ||||||
|  | @ -1,16 +1,14 @@ | ||||||
| #include "extract/extract.hpp" | #include "extract/extract.hpp" | ||||||
| #include "config/config.hpp" | #include "config/config.hpp" | ||||||
|  | #include "extract/dll.hpp" | ||||||
| 
 | 
 | ||||||
| #include <pe-parse/parse.h> | #include <iostream> | ||||||
| 
 |  | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <filesystem> | #include <filesystem> | ||||||
| #include <algorithm> |  | ||||||
| #include <stdexcept> | #include <stdexcept> | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
| #include <cstdlib> | #include <cstdlib> | ||||||
| #include <string> | #include <string> | ||||||
| #include <utility> |  | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <array> | #include <array> | ||||||
| 
 | 
 | ||||||
|  | @ -73,22 +71,11 @@ const std::unordered_map<std::string, uint32_t> nameIdxTable = {{ | ||||||
| }}; | }}; | ||||||
| 
 | 
 | ||||||
| namespace { | namespace { | ||||||
|     auto& pshaders() { |     auto& shaders() { | ||||||
|         static std::unordered_map<uint32_t, std::array<std::vector<uint8_t>, 2>> shaderData; |         static std::unordered_map<uint32_t, std::array<std::vector<uint8_t>, 2>> shaderData; | ||||||
|         return shaderData; |         return shaderData; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     int on_resource(void* ptr, const peparse::resource& res) { |  | ||||||
|         if (res.type != peparse::RT_RCDATA || res.buf == nullptr || res.buf->bufLen <= 0) |  | ||||||
|             return 0; |  | ||||||
|         std::vector<uint8_t> resource_data(res.buf->bufLen); |  | ||||||
|         std::copy_n(res.buf->buf, res.buf->bufLen, resource_data.data()); |  | ||||||
| 
 |  | ||||||
|         auto* shaders = reinterpret_cast<std::unordered_map<uint32_t, std::vector<uint8_t>>*>(ptr); |  | ||||||
|         shaders->emplace(res.name, std::move(resource_data)); |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const std::vector<std::filesystem::path> PATHS{{ |     const std::vector<std::filesystem::path> PATHS{{ | ||||||
|         ".local/share/Steam/steamapps/common", |         ".local/share/Steam/steamapps/common", | ||||||
|         ".steam/steam/steamapps/common", |         ".steam/steam/steamapps/common", | ||||||
|  | @ -121,44 +108,39 @@ namespace { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Extract::extractShaders() { | void Extract::extractShaders() { | ||||||
|     if (!pshaders().empty()) |     if (!shaders().empty()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     std::unordered_map<uint32_t, std::vector<uint8_t>> shaders{}; |  | ||||||
| 
 |  | ||||||
|     // parse the dll
 |     // parse the dll
 | ||||||
|     peparse::parsed_pe* dll = peparse::ParsePEFromFile(getDllPath().c_str()); |     const auto resources = DLL::parse_dll(getDllPath()); | ||||||
|     if (!dll) |     std::cerr << "lsfg-vk: Extracted " << resources.size() << " resources from dll.\n"; | ||||||
|         throw std::runtime_error("Unable to read Lossless.dll, is it installed?"); |  | ||||||
|     peparse::IterRsrc(dll, on_resource, reinterpret_cast<void*>(&shaders)); |  | ||||||
|     peparse::DestructParsedPE(dll); |  | ||||||
| 
 | 
 | ||||||
|     // ensure all shaders are present
 |     // ensure all shaders are present
 | ||||||
|     for (const auto& [name, idx] : nameIdxTable) { |     for (const auto& [name, idx] : nameIdxTable) { | ||||||
|         auto fp16 = shaders.find(idx); |         auto fp16 = resources.find(idx); | ||||||
|         if (fp16 == shaders.end()) |         if (fp16 == resources.end()) | ||||||
|             throw std::runtime_error("Shader not found: " + name + " (FP16).\n- Is Lossless Scaling up to date?"); |             throw std::runtime_error("Shader not found: " + name + " (FP16).\n- Is Lossless Scaling up to date?"); | ||||||
|         auto fp32 = shaders.find(idx + FP); |         auto fp32 = resources.find(idx + FP); | ||||||
|         if (fp32 == shaders.end()) |         if (fp32 == resources.end()) | ||||||
|             throw std::runtime_error("Shader not found: " + name + " (FP32).\n- Is Lossless Scaling up to date?"); |             throw std::runtime_error("Shader not found: " + name + " (FP32).\n- Is Lossless Scaling up to date?"); | ||||||
| 
 | 
 | ||||||
|         pshaders().emplace(idx, std::array<std::vector<uint8_t>, 2>{ |         shaders().emplace(idx, std::array<std::vector<uint8_t>, 2>{ | ||||||
|             std::move(fp32->second), |             fp32->second, | ||||||
|             std::move(fp16->second) |             fp16->second | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<uint8_t> Extract::getShader(const std::string& name, bool fp16) { | std::vector<uint8_t> Extract::getShader(const std::string& name, bool fp16) { | ||||||
|     if (pshaders().empty()) |     if (shaders().empty()) | ||||||
|         throw std::runtime_error("Shaders are not loaded."); |         throw std::runtime_error("Shaders are not loaded."); | ||||||
| 
 | 
 | ||||||
|     auto hit = nameIdxTable.find(name); |     auto hit = nameIdxTable.find(name); | ||||||
|     if (hit == nameIdxTable.end()) |     if (hit == nameIdxTable.end()) | ||||||
|         throw std::runtime_error("Shader hash not found: " + name); |         throw std::runtime_error("Shader hash not found: " + name); | ||||||
| 
 | 
 | ||||||
|     auto sit = pshaders().find(hit->second); |     auto sit = shaders().find(hit->second); | ||||||
|     if (sit == pshaders().end()) |     if (sit == shaders().end()) | ||||||
|         throw std::runtime_error("Shader not found: " + name); |         throw std::runtime_error("Shader not found: " + name); | ||||||
|     return fp16 ? sit->second.at(1) : sit->second.at(0); |     return fp16 ? sit->second.at(1) : sit->second.at(0); | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								thirdparty/pe-parse
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								thirdparty/pe-parse
									
										
									
									
										vendored
									
									
								
							|  | @ -1 +0,0 @@ | ||||||
| Subproject commit 31ac5966503689d5693cd9fb520bd525a8710e17 |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 PancakeTAS
						PancakeTAS