Skip to content

Commit

Permalink
Add LE3PNGScreenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
Mgamerz committed Jan 31, 2022
1 parent 858643b commit 2c125d4
Show file tree
Hide file tree
Showing 5 changed files with 3,647 additions and 1 deletion.
10 changes: 9 additions & 1 deletion LE3-ASI-Plugins.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LE3SeqAct_LogEnabler", "LE3
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LE3DebugLogger", "LE3DebugLogger\LE3DebugLogger.vcxproj", "{7172D8EC-2994-4D0D-92A0-7AA3F9FAB08E}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dll1", "Dll1\Dll1.vcxproj", "{17A89D60-0608-4CC7-92F2-B98082769E92}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LE3LEXInterop", "Dll1\Dll1.vcxproj", "{17A89D60-0608-4CC7-92F2-B98082769E92}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LE3PNGScreenShots", "LE3PNGScreenshots\LE3PNGScreenshots.vcxproj", "{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -65,6 +67,12 @@ Global
{17A89D60-0608-4CC7-92F2-B98082769E92}.Release|x64.Build.0 = Release|x64
{17A89D60-0608-4CC7-92F2-B98082769E92}.Release|x86.ActiveCfg = Release|Win32
{17A89D60-0608-4CC7-92F2-B98082769E92}.Release|x86.Build.0 = Release|Win32
{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}.Debug|x64.ActiveCfg = Debug|x64
{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}.Debug|x64.Build.0 = Debug|x64
{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}.Debug|x86.ActiveCfg = Debug|x64
{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}.Release|x64.ActiveCfg = Release|x64
{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}.Release|x64.Build.0 = Release|x64
{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
159 changes: 159 additions & 0 deletions LE3PNGScreenshots/LE3PNGScreenshots.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//#define GAMELE1
//#define GAMELE2
#define GAMELE3
#define MYHOOK "PNGScreenShots_"


#include <filesystem>
#include <stdio.h>
#include <fstream>
#include <Shlwapi.h>
#include "fpng/fpng.h"

#ifdef GAMELE1
#include "../LE1-SDK/SdkInitializer.h"
#include "../LE1-SDK/SdkHeaders.h"
#include "../LE1-SDK/Interface.h"
#include "../LE1-SDK/Common.h"
SPI_PLUGINSIDE_SUPPORT(L"PNGScreenShots", L"1.0.0", L"ME3Tweaks", SPI_GAME_LE1, SPI_VERSION_ANY);
#elif defined(GAMELE2)
#include "../LE2-SDK/SdkInitializer.h"
#include "../LE2-SDK/SdkHeaders.h"
#include "../LE2-SDK/Interface.h"
#include "../LE2-SDK/Common.h"
SPI_PLUGINSIDE_SUPPORT(L"PNGScreenShots", L"1.0.0", L"ME3Tweaks", SPI_GAME_LE2, SPI_VERSION_ANY);
#elif defined(GAMELE3)
#include "../LE3-SDK/SdkInitializer.h"
#include "../LE3-SDK/SdkHeaders.h"
#include "../LE3-SDK/Interface.h"
#include "../LE3-SDK/Common.h"
SPI_PLUGINSIDE_SUPPORT(L"PNGScreenShots", L"1.0.0", L"ME3Tweaks", SPI_GAME_LE3, SPI_VERSION_ANY);
#endif

SPI_PLUGINSIDE_POSTLOAD;
SPI_PLUGINSIDE_ASYNCATTACH;

// This project doesn't use C++ 20 (due to it making it not compilable) so we use c++ 11
// Taken from https://stackoverflow.com/a/26221725/800318
template<typename ... Args>
std::wstring wstring_format(const std::wstring& format, Args ... args)
{
int size_s = _snwprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0'
if (size_s <= 0) { throw std::runtime_error("Error during formatting."); }
auto size = static_cast<size_t>(size_s);
auto buf = std::make_unique<wchar_t[]>(size);
_snwprintf(buf.get(), size, format.c_str(), args ...);
return std::wstring(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}

// ======================================================================
// appCreateBitmap hook
// ======================================================================

// Prototypes for appCreateBitmap
typedef void (*tAppCreateBitmap)(wchar_t* pattern, int width, int height, FColor* data, void* fileManager);
tAppCreateBitmap appCreateBitmap = nullptr;
tAppCreateBitmap appCreateBitmap_orig = nullptr;

bool fpngInitialized = false;

// The initial screenshot index we should check against. As we take screenshots we know the previous images will always exist
// and as such we can just increment this on auto-generated screenshots to match the game's incremental system
int cachedScreenshotIndex = 1;

// Hopefully user never gets here but this is so it doesn't make an infinite loop
int maxScreenshotIndex = 99999;

// data is an array of FColor structs (BGRA) of size width * height
void appCreateBitmap_hook(wchar_t* inputBaseName, int width, int height, FColor* data, void* fileManager)
{
if (!fpngInitialized)
{
// Initialize the library before use
fpng::fpng_init();
fpngInitialized = true;
}

long byteCount = width * height * 3;

// Color order needs swapped around for FPNG to access data
// since nothing supports BRGA.
std::vector<unsigned char> newImageData(byteCount);
int pixelIndex = 0; // Which pixel we are one
long totalCount = width * height; // how many pixels
long position = 0; // Where to place into vector
for (long i = 0; i < totalCount; i++)
{
auto color = &data[pixelIndex];
newImageData[position] = color->R;
newImageData[position + 1L] = color->G;
newImageData[position + 2L] = color->B;

pixelIndex++;
position = pixelIndex * 3;
}

// Determine output filename.
auto path = std::filesystem::path(inputBaseName);
auto extension = path.extension();
if (extension != ".png")
{
// Calculate a new name
auto outPath = std::filesystem::path(inputBaseName);
std::wstring newFname;
while (cachedScreenshotIndex < maxScreenshotIndex)
{
#ifdef GAMELE1
newFname = wstring_format(L"PNGLE1ScreenShot%05i.png", cachedScreenshotIndex);
#elif defined(GAMELE2)
newFname = wstring_format(L"PNGLE2ScreenShot%05i.png", cachedScreenshotIndex);
#elif defined(GAMELE3)
newFname = wstring_format(L"PNGLE3ScreenShot%05i.png", cachedScreenshotIndex);
#endif
cachedScreenshotIndex++; // Increment the cached index immediately.
outPath.replace_filename(newFname);

// Check if file exists
if (!std::filesystem::exists(outPath))
{
// File doesn't exist. Use this one
path = outPath;
break;
}
// File exists, go to next one
}

if (cachedScreenshotIndex == maxScreenshotIndex)
return; // Can't take any more screenshots
}

// Save the image data using the fpng library.
fpng::fpng_encode_image_to_wfile(path.c_str(), newImageData.data(), width, height, 3, 0U); // 3bpp, no flags
}


SPI_IMPLEMENT_ATTACH
{
// Hook appCreateBitmap and provide our own implementation of saving the bitmap data to disk.
auto _ = SDKInitializer::Instance();
#ifdef GAMELE1
INIT_FIND_PATTERN_POSTHOOK(appCreateBitmap, /*40 55 53 56 57*/ "41 54 41 55 41 56 41 57 48 8d ac 24 38 f8 ff ff 48 81 ec c8 08 00 00 48 c7 44 24 70 fe ff ff ff 48 8b 05 3c b6 55 01 48 33 c4 48 89 85 b0 07 00 00 4c 89 4c 24 48 44 89 44 24 58");
#elif defined(GAMELE2)
INIT_FIND_PATTERN_POSTHOOK(appCreateBitmap, /*40 55 53 56 57 */ "41 54 41 55 41 56 41 57 48 8d ac 24 38 f8 ff ff 48 81 ec c8 08 00 00 48 c7 44 24 70 fe ff ff ff 48 8b 05 ec 2a 58 01");
#elif defined(GAMELE3)
INIT_FIND_PATTERN_POSTHOOK(appCreateBitmap, /*40 55 53 56 57 */ "41 54 41 55 41 56 41 57 48 8d ac 24 78 f8 ff ff 48 81 ec 88 08 00 00 48 c7 44 24 60 fe ff ff ff");
#endif

if (auto rc = InterfacePtr->InstallHook(MYHOOK "appCreateBitmap", appCreateBitmap, appCreateBitmap_hook, (void**)&appCreateBitmap_orig);
rc != SPIReturn::Success)
{
writeln(L"Attach - failed to hook appCreateBitmap: %d / %s", rc, SPIReturnToString(rc));
return false;
}
return true;
}

SPI_IMPLEMENT_DETACH
{
return true;
}
108 changes: 108 additions & 0 deletions LE3PNGScreenshots/LE3PNGScreenshots.vcxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{8298E95F-CC4D-440D-B9E6-7B7BC0DF2FE5}</ProjectGuid>
<RootNamespace>TwoDAPrinter</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>LE3PNGScreenShots</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<TargetExt>.asi</TargetExt>
<TargetName>LE3PNGScreenShots</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<TargetExt>.asi</TargetExt>
<TargetName>LE3PNGScreenShots</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>ASI_DEBUG;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="fpng\fpng.cpp" />
<ClCompile Include="LE3PNGScreenShots.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Common.h" />
<ClInclude Include="..\Interface.h" />
<ClInclude Include="fpng\fpng.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LE1-SDK\LE1-SDK.vcxproj">
<Project>{e8071203-085e-4b90-9c5c-39d546ed6fe2}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
Loading

0 comments on commit 2c125d4

Please sign in to comment.