77#include " common/Path.h"
88#include " common/StringUtil.h"
99#include " common/ScopedGuard.h"
10+ #include " common/ZipHelpers.h"
1011
1112#include " GS/Renderers/HW/GSTextureReplacements.h"
1213
14+ #include < cstdio>
1315#include < csetjmp>
1416#include < png.h>
1517
@@ -146,7 +148,7 @@ static void ConvertTexture_R8G8B8(u32 width, u32 height, std::vector<u8>& data,
146148// PNG Handlers
147149// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
148150
149- bool PNGLoader ( const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
151+ static bool PNGLoaderFromFile ( FILE * fp, const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
150152{
151153 png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING , nullptr , nullptr , nullptr );
152154 if (!png_ptr)
@@ -163,14 +165,106 @@ bool PNGLoader(const std::string& filename, GSTextureReplacements::ReplacementTe
163165 png_destroy_read_struct (&png_ptr, &info_ptr, nullptr );
164166 });
165167
168+ if (setjmp (png_jmpbuf (png_ptr)))
169+ return false ;
170+
171+ png_init_io (png_ptr, fp);
172+ png_read_info (png_ptr, info_ptr);
173+
174+ png_uint_32 width = 0 ;
175+ png_uint_32 height = 0 ;
176+ int bitDepth = 0 ;
177+ int colorType = -1 ;
178+ if (png_get_IHDR (png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, nullptr , nullptr , nullptr ) != 1 ||
179+ width == 0 || height == 0 )
180+ {
181+ return false ;
182+ }
183+
184+ const u32 pitch = width * sizeof (u32 );
185+ tex->width = width;
186+ tex->height = height;
187+ tex->format = GSTexture::Format::Color;
188+ tex->pitch = pitch;
189+ tex->data .resize (pitch * height);
190+
191+ const png_uint_32 row_bytes = png_get_rowbytes (png_ptr, info_ptr);
192+ std::vector<u8 > row_data (row_bytes);
193+
194+ for (u32 y = 0 ; y < height; y++)
195+ {
196+ png_read_row (png_ptr, static_cast <png_bytep>(row_data.data ()), nullptr );
197+
198+ const u8 * row_ptr = row_data.data ();
199+ u8 * out_ptr = tex->data .data () + y * pitch;
200+ if (colorType == PNG_COLOR_TYPE_RGB )
201+ {
202+ for (u32 x = 0 ; x < width; x++)
203+ {
204+ u32 pixel = static_cast <u32 >(*(row_ptr)++);
205+ pixel |= static_cast <u32 >(*(row_ptr)++) << 8 ;
206+ pixel |= static_cast <u32 >(*(row_ptr)++) << 16 ;
207+ pixel |= 0x80000000u ; // make opaque
208+ std::memcpy (out_ptr, &pixel, sizeof (pixel));
209+ out_ptr += sizeof (pixel);
210+ }
211+ }
212+ else if (colorType == PNG_COLOR_TYPE_RGBA )
213+ {
214+ std::memcpy (out_ptr, row_ptr, pitch);
215+ }
216+ }
217+
218+ return true ;
219+ }
220+
221+ bool PNGLoader (const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
222+ {
166223 auto fp = FileSystem::OpenManagedCFile (filename.c_str (), " rb" );
167224 if (!fp)
168225 return false ;
169226
227+ return PNGLoaderFromFile (fp.get (), filename, tex, only_base_image);
228+ }
229+
230+ static void PNGReadFromMemory (png_structp png_ptr, png_bytep out, png_size_t count)
231+ {
232+ auto * src = static_cast <std::vector<u8 >*>(png_get_io_ptr (png_ptr));
233+ if (src->empty ())
234+ {
235+ png_error (png_ptr, " unexpected end of PNG data" );
236+ return ;
237+ }
238+
239+ const size_t available = std::min<size_t >(count, src->size ());
240+ std::memcpy (out, src->data (), available);
241+ src->erase (src->begin (), src->begin () + static_cast <std::ptrdiff_t >(available));
242+ if (available < count)
243+ png_error (png_ptr, " unexpected end of PNG data" );
244+ }
245+
246+ static bool PNGLoaderFromBuffer (const std::vector<u8 >& data, const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
247+ {
248+ png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING , nullptr , nullptr , nullptr );
249+ if (!png_ptr)
250+ return false ;
251+
252+ png_infop info_ptr = png_create_info_struct (png_ptr);
253+ if (!info_ptr)
254+ {
255+ png_destroy_read_struct (&png_ptr, nullptr , nullptr );
256+ return false ;
257+ }
258+
259+ ScopedGuard cleanup ([&png_ptr, &info_ptr]() {
260+ png_destroy_read_struct (&png_ptr, &info_ptr, nullptr );
261+ });
262+
263+ std::vector<u8 > buffer = data;
170264 if (setjmp (png_jmpbuf (png_ptr)))
171265 return false ;
172266
173- png_init_io (png_ptr, fp. get () );
267+ png_set_read_fn (png_ptr, &buffer, PNGReadFromMemory );
174268 png_read_info (png_ptr, info_ptr);
175269
176270 png_uint_32 width = 0 ;
@@ -206,7 +300,7 @@ bool PNGLoader(const std::string& filename, GSTextureReplacements::ReplacementTe
206300 u32 pixel = static_cast <u32 >(*(row_ptr)++);
207301 pixel |= static_cast <u32 >(*(row_ptr)++) << 8 ;
208302 pixel |= static_cast <u32 >(*(row_ptr)++) << 16 ;
209- pixel |= 0x80000000u ; // make opaque
303+ pixel |= 0x80000000u ;
210304 std::memcpy (out_ptr, &pixel, sizeof (pixel));
211305 out_ptr += sizeof (pixel);
212306 }
@@ -593,25 +687,21 @@ static bool ReadDDSMipLevel(std::FILE* fp, const std::string& filename, u32 mip_
593687 return true ;
594688}
595689
596- bool DDSLoader ( const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
690+ static bool DDSLoaderFromFile ( FILE * fp, const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
597691{
598- auto fp = FileSystem::OpenManagedCFile (filename.c_str (), " rb" );
599- if (!fp)
600- return false ;
601-
602692 DDSLoadInfo info;
603- if (!ParseDDSHeader (fp. get () , &info))
693+ if (!ParseDDSHeader (fp, &info))
604694 return false ;
605695
606696 // always load the base image
607- if (FileSystem::FSeek64 (fp. get () , info.base_image_offset , SEEK_SET ) != 0 )
697+ if (FileSystem::FSeek64 (fp, info.base_image_offset , SEEK_SET ) != 0 )
608698 return false ;
609699
610700 tex->format = info.format ;
611701 tex->width = info.width ;
612702 tex->height = info.height ;
613703 tex->pitch = info.base_image_pitch ;
614- if (!ReadDDSMipLevel (fp. get () , filename, 0 , info, tex->width , tex->height , tex->data , tex->pitch , info.base_image_size ))
704+ if (!ReadDDSMipLevel (fp, filename, 0 , info, tex->width , tex->height , tex->data , tex->pitch , info.base_image_size ))
615705 return false ;
616706
617707 // Read in any remaining mip levels in the file.
@@ -622,7 +712,7 @@ bool DDSLoader(const std::string& filename, GSTextureReplacements::ReplacementTe
622712 GSTextureReplacements::ReplacementTexture::MipData md;
623713 u32 mip_size;
624714 CalcBlockMipmapSize (info.block_size , info.bytes_per_block , info.width , info.height , level, md.width , md.height , md.pitch , mip_size);
625- if (!ReadDDSMipLevel (fp. get () , filename, level, info, md.width , md.height , md.data , md.pitch , mip_size))
715+ if (!ReadDDSMipLevel (fp, filename, level, info, md.width , md.height , md.data , md.pitch , mip_size))
626716 break ;
627717
628718 tex->mips .push_back (std::move (md));
@@ -631,3 +721,49 @@ bool DDSLoader(const std::string& filename, GSTextureReplacements::ReplacementTe
631721
632722 return true ;
633723}
724+
725+ bool DDSLoader (const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
726+ {
727+ auto fp = FileSystem::OpenManagedCFile (filename.c_str (), " rb" );
728+ if (!fp)
729+ return false ;
730+
731+ return DDSLoaderFromFile (fp.get (), filename, tex, only_base_image);
732+ }
733+
734+ static bool DDSLoaderFromBuffer (const std::vector<u8 >& data, const std::string& filename, GSTextureReplacements::ReplacementTexture* tex, bool only_base_image)
735+ {
736+ FILE * fp = std::tmpfile ();
737+ if (!fp)
738+ return false ;
739+
740+ ScopedGuard cleanup ([&fp]() {
741+ std::fclose (fp);
742+ });
743+
744+ if (std::fwrite (data.data (), 1 , data.size (), fp) != data.size ())
745+ return false ;
746+ if (std::fflush (fp) != 0 )
747+ return false ;
748+ std::rewind (fp);
749+
750+ return DDSLoaderFromFile (fp, filename, tex, only_base_image);
751+ }
752+
753+ bool GSTextureReplacements::LoadImageFromArchiveEntry (const std::string& archive_path,
754+ const std::string& normalized_name,
755+ GSTextureReplacements::ReplacementTexture* tex,
756+ bool only_base_image)
757+ {
758+ const auto data = ReadArchiveEntryBytes (archive_path, normalized_name);
759+ if (!data.has_value ())
760+ return false ;
761+
762+ const std::string_view ext = Path::GetExtension (normalized_name);
763+ if (StringUtil::Strncasecmp (ext.data (), " png" , ext.size ()) == 0 )
764+ return PNGLoaderFromBuffer (*data, normalized_name, tex, only_base_image);
765+ if (StringUtil::Strncasecmp (ext.data (), " dds" , ext.size ()) == 0 )
766+ return DDSLoaderFromBuffer (*data, normalized_name, tex, only_base_image);
767+
768+ return false ;
769+ }
0 commit comments