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
							
								
									2eec75e258
								
							
						
					
					
						commit
						07653a83e0
					
				
					 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-unused-template) | ||||
| 
 | ||||
| add_subdirectory(thirdparty/pe-parse/pe-parser-library EXCLUDE_FROM_ALL) | ||||
| add_subdirectory(framegen) | ||||
| 
 | ||||
| if(LSFGVK_EXCESS_DEBUG) | ||||
|  | @ -44,7 +43,6 @@ target_include_directories(lsfg-vk SYSTEM | |||
| target_include_directories(lsfg-vk | ||||
|     PUBLIC include) | ||||
| target_link_libraries(lsfg-vk PUBLIC | ||||
|     pe-parse | ||||
|     lsfg-vk-framegen) | ||||
| 
 | ||||
| # 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 "config/config.hpp" | ||||
| #include "extract/dll.hpp" | ||||
| 
 | ||||
| #include <pe-parse/parse.h> | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <unordered_map> | ||||
| #include <filesystem> | ||||
| #include <algorithm> | ||||
| #include <stdexcept> | ||||
| #include <cstdint> | ||||
| #include <cstdlib> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| #include <array> | ||||
| 
 | ||||
|  | @ -73,22 +71,11 @@ const std::unordered_map<std::string, uint32_t> nameIdxTable = {{ | |||
| }}; | ||||
| 
 | ||||
| namespace { | ||||
|     auto& pshaders() { | ||||
|     auto& shaders() { | ||||
|         static std::unordered_map<uint32_t, std::array<std::vector<uint8_t>, 2>> 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{{ | ||||
|         ".local/share/Steam/steamapps/common", | ||||
|         ".steam/steam/steamapps/common", | ||||
|  | @ -121,44 +108,39 @@ namespace { | |||
| } | ||||
| 
 | ||||
| void Extract::extractShaders() { | ||||
|     if (!pshaders().empty()) | ||||
|     if (!shaders().empty()) | ||||
|         return; | ||||
| 
 | ||||
|     std::unordered_map<uint32_t, std::vector<uint8_t>> shaders{}; | ||||
| 
 | ||||
|     // parse the dll
 | ||||
|     peparse::parsed_pe* dll = peparse::ParsePEFromFile(getDllPath().c_str()); | ||||
|     if (!dll) | ||||
|         throw std::runtime_error("Unable to read Lossless.dll, is it installed?"); | ||||
|     peparse::IterRsrc(dll, on_resource, reinterpret_cast<void*>(&shaders)); | ||||
|     peparse::DestructParsedPE(dll); | ||||
|     const auto resources = DLL::parse_dll(getDllPath()); | ||||
|     std::cerr << "lsfg-vk: Extracted " << resources.size() << " resources from dll.\n"; | ||||
| 
 | ||||
|     // ensure all shaders are present
 | ||||
|     for (const auto& [name, idx] : nameIdxTable) { | ||||
|         auto fp16 = shaders.find(idx); | ||||
|         if (fp16 == shaders.end()) | ||||
|         auto fp16 = resources.find(idx); | ||||
|         if (fp16 == resources.end()) | ||||
|             throw std::runtime_error("Shader not found: " + name + " (FP16).\n- Is Lossless Scaling up to date?"); | ||||
|         auto fp32 = shaders.find(idx + FP); | ||||
|         if (fp32 == shaders.end()) | ||||
|         auto fp32 = resources.find(idx + FP); | ||||
|         if (fp32 == resources.end()) | ||||
|             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>{ | ||||
|             std::move(fp32->second), | ||||
|             std::move(fp16->second) | ||||
|         shaders().emplace(idx, std::array<std::vector<uint8_t>, 2>{ | ||||
|             fp32->second, | ||||
|             fp16->second | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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."); | ||||
| 
 | ||||
|     auto hit = nameIdxTable.find(name); | ||||
|     if (hit == nameIdxTable.end()) | ||||
|         throw std::runtime_error("Shader hash not found: " + name); | ||||
| 
 | ||||
|     auto sit = pshaders().find(hit->second); | ||||
|     if (sit == pshaders().end()) | ||||
|     auto sit = shaders().find(hit->second); | ||||
|     if (sit == shaders().end()) | ||||
|         throw std::runtime_error("Shader not found: " + name); | ||||
|     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