diff --git a/build/gamesource/textures/decal/decals.ini b/build/engine/config/decals/decals.cfg similarity index 87% rename from build/gamesource/textures/decal/decals.ini rename to build/engine/config/decals/decals.cfg index 438710c7aed537951bd0844fc34a9322d07eba3c..5054a8a4802bee12ac478bb04d96107770bbc8da 100644 --- a/build/gamesource/textures/decal/decals.ini +++ b/build/engine/config/decals/decals.cfg @@ -2,7 +2,7 @@ [decal_0] id=0 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[0,0,64,64] tex1=[64,0,128,64] tex2=[0,64,64,128] @@ -12,7 +12,7 @@ tex3=[64,64,128,128] [decal_1] id=1 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[128,0,192,64] tex1=[192,0,256,64] tex2=[128,64,192,128] @@ -20,9 +20,9 @@ tex3=[192,64,256,128] ;wood [decal_2] -id=2 +id=4 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[256,0,320,64] tex1=[320,0,384,64] tex2=[256,64,320,128] @@ -31,9 +31,9 @@ tex4=[384,0,448,64] ;glass [decal_3] -id=3 +id=2 base_scale=2.0 -tex=decal_test.dds +tex=decal_test tex0=[0,128,128,256] tex1=[128,128,256,256] tex2=[256,128,384,256] @@ -44,7 +44,7 @@ tex4=[128,256,256,384] [decal_4] id=7 base_scale=8.0 -tex=decal_test.dds +tex=decal_test tex0=[256,256,384,384] tex1=[384,256,512,384] tex2=[384,128,512,256] diff --git a/build/gamesource/textures/decal/decal_test.dds b/build/engine/textures/decal/decal_test.dds similarity index 100% rename from build/gamesource/textures/decal/decal_test.dds rename to build/engine/textures/decal/decal_test.dds diff --git a/build/engine/textures/decal/decal_test2.dds b/build/engine/textures/decal/decal_test2.dds new file mode 100644 index 0000000000000000000000000000000000000000..497ec0a645bbe369790dd9d2d78db10214bb2d5e Binary files /dev/null and b/build/engine/textures/decal/decal_test2.dds differ diff --git a/build/engine/textures/decal/decal_test3.dds b/build/engine/textures/decal/decal_test3.dds new file mode 100644 index 0000000000000000000000000000000000000000..b068b6500463898c431ee6069992113c5d9bc5de Binary files /dev/null and b/build/engine/textures/decal/decal_test3.dds differ diff --git a/docs/gen-entity.js b/docs/gen-entity.js index 95ba19a9209b1a6bffd67fe2eb9df10021af47ee..6641926e08179d5d94270d297b48a0a3cea78f36 100644 --- a/docs/gen-entity.js +++ b/docs/gen-entity.js @@ -19,6 +19,7 @@ g_mFieldTypes = { "DEFINE_FIELD_STRING": "string", "DEFINE_FIELD_ANGLES": "angles", "DEFINE_FIELD_INT": "int", + "DEFINE_FIELD_UINT": "uint", "DEFINE_FIELD_ENUM": null, "DEFINE_FIELD_FLOAT": "float", "DEFINE_FIELD_BOOL": "bool", @@ -29,6 +30,7 @@ g_mFieldTypes = { g_mInputTypes = { "PDF_NONE": "none", "PDF_INT": "int", + "PDF_UINT": "uint", "PDF_FLOAT": "float", "PDF_VECTOR": "float3", "PDF_VECTOR4": "float4", diff --git a/proj/SkyXEngine/vs2013/SkyXEngine.sln b/proj/SkyXEngine/vs2013/SkyXEngine.sln index 4cf1c805fac086c50d43701e9f488031d4b3b861..7bf5b3d524ba84aadc75a4a31ec74f515e256973 100644 --- a/proj/SkyXEngine/vs2013/SkyXEngine.sln +++ b/proj/SkyXEngine/vs2013/SkyXEngine.sln @@ -8,6 +8,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SkyXEngine", "SkyXEngine.vc {7C0C8205-BDD3-44A3-AA3A-7855C7EFC88E} = {7C0C8205-BDD3-44A3-AA3A-7855C7EFC88E} {349DE810-592A-42AF-9440-A0B3F46A9229} = {349DE810-592A-42AF-9440-A0B3F46A9229} {236F4A16-78D8-42E4-86C0-30265CA2D84D} = {236F4A16-78D8-42E4-86C0-30265CA2D84D} + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E} = {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E} {99BF8838-DB9E-4CFE-83F7-ACAA8A97304B} = {99BF8838-DB9E-4CFE-83F7-ACAA8A97304B} {CD470440-2CC2-42BA-8B94-2B1C72125FE3} = {CD470440-2CC2-42BA-8B94-2B1C72125FE3} {B9656841-7734-4D0B-8619-1BED5E2ED7AE} = {B9656841-7734-4D0B-8619-1BED5E2ED7AE} @@ -370,6 +371,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pngplugin", "..\..\pngplugi EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xParticles", "..\..\xParticles\vs2013\xParticles.vcxproj", "{6CB8C7D4-F3F9-418D-8939-BB07C488FF01}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fbxplugin", "..\..\fbxplugin\vs2013\fbxplugin.vcxproj", "{8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -780,6 +783,14 @@ Global {6CB8C7D4-F3F9-418D-8939-BB07C488FF01}.Release|Win32.Build.0 = Release|Win32 {6CB8C7D4-F3F9-418D-8939-BB07C488FF01}.Release|x64.ActiveCfg = Release|x64 {6CB8C7D4-F3F9-418D-8939-BB07C488FF01}.Release|x64.Build.0 = Release|x64 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Debug|Win32.ActiveCfg = Debug|Win32 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Debug|Win32.Build.0 = Debug|Win32 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Debug|x64.ActiveCfg = Debug|x64 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Debug|x64.Build.0 = Debug|x64 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Release|Win32.ActiveCfg = Release|Win32 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Release|Win32.Build.0 = Release|Win32 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Release|x64.ActiveCfg = Release|x64 + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -855,6 +866,7 @@ Global {D61CDBAB-DD14-4380-B400-12268DB975E0} = {7C1F0E50-7A19-4AB4-B559-11EF078F4787} {99BF8838-DB9E-4CFE-83F7-ACAA8A97304B} = {D61CDBAB-DD14-4380-B400-12268DB975E0} {6CB8C7D4-F3F9-418D-8939-BB07C488FF01} = {E6B16854-D4A4-4B56-8E1C-482DD523F205} + {8B15BF1C-323B-4F4E-86D4-C65986FEDD3E} = {7C1F0E50-7A19-4AB4-B559-11EF078F4787} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B3433050-1AA4-4375-8CC4-42BB0954D80A} diff --git a/proj/fbxplugin/vs2013/fbxplugin.vcxproj b/proj/fbxplugin/vs2013/fbxplugin.vcxproj new file mode 100644 index 0000000000000000000000000000000000000000..7c6ccb034f19e316a93ab2e63598b13ab00cd26a --- /dev/null +++ b/proj/fbxplugin/vs2013/fbxplugin.vcxproj @@ -0,0 +1,192 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\source\fbxplugin\dllmain.cpp" /> + <ClCompile Include="..\..\..\source\fbxplugin\libdeflate.c" /> + <ClCompile Include="..\..\..\source\fbxplugin\ModelLoader.cpp" /> + <ClCompile Include="..\..\..\source\fbxplugin\plugin_main.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\source\fbxplugin\fbx.h" /> + <ClInclude Include="..\..\..\source\fbxplugin\libdeflate.h" /> + <ClInclude Include="..\..\..\source\fbxplugin\ModelLoader.h" /> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{8B15BF1C-323B-4F4E-86D4-C65986FEDD3E}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>fbxplugin</RootNamespace> + <ProjectName>fbxplugin</ProjectName> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v120_xp</PlatformToolset> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v120_xp</PlatformToolset> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v120_xp</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v120_xp</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <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|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <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|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>../../../build/bin/plugins/</OutDir> + <IncludePath>$(IncludePath);../../../sdks/;$(WindowsSdk_IncludePath);../../../source;</IncludePath> + <LibraryPath>;../../../libs;$(LibraryPath)</LibraryPath> + <SourcePath>../../../source;$(SourcePath)</SourcePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <IncludePath>$(IncludePath);../../../sdks/;$(WindowsSdk_IncludePath);../../../source;</IncludePath> + <LibraryPath>;../../../libs64;$(LibraryPath)</LibraryPath> + <SourcePath>../../../source;$(SourcePath)</SourcePath> + <LinkIncremental>false</LinkIncremental> + <OutDir>../../../build/bin64/plugins/</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>../../../build/bin/plugins/</OutDir> + <IncludePath>$(IncludePath);../../../sdks/;$(WindowsSdk_IncludePath);../../../source;</IncludePath> + <LibraryPath>;../../../libs;$(LibraryPath)</LibraryPath> + <SourcePath>../../../source;$(SourcePath)</SourcePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <IncludePath>$(IncludePath);../../../sdks/;$(WindowsSdk_IncludePath);../../../source;</IncludePath> + <LibraryPath>;../../../libs64;$(LibraryPath)</LibraryPath> + <SourcePath>../../../source;$(SourcePath)</SourcePath> + <LinkIncremental>false</LinkIncremental> + <OutDir>../../../build/bin64/plugins/</OutDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;SX_LIB_NAME="FBX";%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <TreatSpecificWarningsAsErrors>4316</TreatSpecificWarningsAsErrors> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ImportLibrary>../../../libs/$(TargetName).lib</ImportLibrary> + <ProgramDatabaseFile>$(ProjectDir)../../../pdb/$(TargetName).pdb</ProgramDatabaseFile> + <Profile>true</Profile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN64;_CRT_SECURE_NO_WARNINGS;_DEBUG;_WINDOWS;SX_LIB_NAME="FBX";%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <TreatSpecificWarningsAsErrors>4316</TreatSpecificWarningsAsErrors> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ImportLibrary>../../../libs/$(TargetName).lib</ImportLibrary> + <ProgramDatabaseFile>$(ProjectDir)../../../pdb64/$(TargetName).pdb</ProgramDatabaseFile> + <Profile>true</Profile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;SX_LIB_NAME="FBX";%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <TreatSpecificWarningsAsErrors>4316</TreatSpecificWarningsAsErrors> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <ProgramDatabaseFile>$(ProjectDir)../../../pdb/$(TargetName).pdb</ProgramDatabaseFile> + <ImportLibrary>../../../libs/$(TargetName).lib</ImportLibrary> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN64;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;SX_LIB_NAME="FBX";%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <TreatSpecificWarningsAsErrors>4316</TreatSpecificWarningsAsErrors> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <ProgramDatabaseFile>$(ProjectDir)../../../pdb64/$(TargetName).pdb</ProgramDatabaseFile> + <ImportLibrary>../../../libs/$(TargetName).lib</ImportLibrary> + </Link> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/proj/fbxplugin/vs2013/fbxplugin.vcxproj.filters b/proj/fbxplugin/vs2013/fbxplugin.vcxproj.filters new file mode 100644 index 0000000000000000000000000000000000000000..598161164eb6a2b11261644a4e688e14f0ab9ff9 --- /dev/null +++ b/proj/fbxplugin/vs2013/fbxplugin.vcxproj.filters @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4F9F3B4E-F189-4263-BB44-FD10D756B9AB}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{3CE894A3-1008-4F79-80BB-FD47268A2DF7}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{52E3E84A-363D-49C5-ADE7-E24E930CEB53}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\..\source\fbxplugin\dllmain.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\source\fbxplugin\ModelLoader.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\source\fbxplugin\plugin_main.cpp" /> + <ClCompile Include="..\..\..\source\fbxplugin\libdeflate.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\source\fbxplugin\ModelLoader.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\fbxplugin\fbx.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\fbxplugin\libdeflate.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj b/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj index 0dac5ef96e6c519fb1c3f7150a6c402168ccc6db..4b40fd426bed100e74fd160e21ec1b7174f2e13f 100644 --- a/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj +++ b/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj @@ -19,12 +19,6 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> - </ClCompile> <ClCompile Include="..\..\..\source\exporter_base\dllmain.cpp" /> <ClCompile Include="..\..\..\source\exporter_base\Exporter.cpp" /> <ClCompile Include="..\..\..\source\exporter_base\Extended.cpp" /> diff --git a/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj.filters b/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj.filters index acedeb6a4c88463f61e8b2361083c36eb5a24ffa..ecdf20a8dd791b5aa33ea3fd297ce776efc901d0 100644 --- a/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj.filters +++ b/proj/msDSEExporter/vs2013/msDSEExporter.vcxproj.filters @@ -27,9 +27,6 @@ <ClCompile Include="..\..\..\source\msDSEExporter\Plugin.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\msDSEExporter\Provider.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/oggplugin/vs2013/oggplugin.vcxproj b/proj/oggplugin/vs2013/oggplugin.vcxproj index f6a2a648c33f2523b8556fe55c0be7e5e4e3de57..f279cf87cff38e3bf9deb34224b8a7dcc3488450 100644 --- a/proj/oggplugin/vs2013/oggplugin.vcxproj +++ b/proj/oggplugin/vs2013/oggplugin.vcxproj @@ -174,7 +174,6 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\oggplugin\AudioCodecOgg.cpp" /> <ClCompile Include="..\..\..\source\oggplugin\dllmain.cpp" /> <ClCompile Include="..\..\..\source\oggplugin\plugin_main.cpp" /> diff --git a/proj/oggplugin/vs2013/oggplugin.vcxproj.filters b/proj/oggplugin/vs2013/oggplugin.vcxproj.filters index 6811cfc3b9cc4bd1581c9f7ef7e21703e0f942e5..2c8cb6fb66b79b802bbd2acf17ab351a3b38c1c8 100644 --- a/proj/oggplugin/vs2013/oggplugin.vcxproj.filters +++ b/proj/oggplugin/vs2013/oggplugin.vcxproj.filters @@ -22,9 +22,6 @@ <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\source\oggplugin\plugin_main.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\oggplugin\AudioCodecOgg.h"> diff --git a/proj/sxae/vs2013/sxae.vcxproj b/proj/sxae/vs2013/sxae.vcxproj index bbc11f73bfccf06ac6d7055b0a799dc83ec23c16..dc75b53c244946c011681a7ca9c26ebaf96237e3 100644 --- a/proj/sxae/vs2013/sxae.vcxproj +++ b/proj/sxae/vs2013/sxae.vcxproj @@ -96,7 +96,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\skyxengine.cpp"> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild> diff --git a/proj/sxae/vs2013/sxae.vcxproj.filters b/proj/sxae/vs2013/sxae.vcxproj.filters index 3c5b935d6c7164cdeaf3e484c093209403ffd38e..0b9f9cf9ebb2c393ea804c239b927a3e369f4005 100644 --- a/proj/sxae/vs2013/sxae.vcxproj.filters +++ b/proj/sxae/vs2013/sxae.vcxproj.filters @@ -57,9 +57,6 @@ <ClCompile Include="..\..\..\source\sxae\TabHitboxes.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\skyxengine.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxambient/vs2013/sxambient.vcxproj b/proj/sxambient/vs2013/sxambient.vcxproj index 49c13dc6fb4431addb1c7056e8f38054b6ebd3aa..dc8ccf2c8b57c350b9e924218831963f767ef2da 100644 --- a/proj/sxambient/vs2013/sxambient.vcxproj +++ b/proj/sxambient/vs2013/sxambient.vcxproj @@ -186,12 +186,6 @@ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> - <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> - </ClCompile> <ClCompile Include="..\..\..\source\common\string_utils.cpp"> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> diff --git a/proj/sxambient/vs2013/sxambient.vcxproj.filters b/proj/sxambient/vs2013/sxambient.vcxproj.filters index 35b20d5a991f78d4a1e714122c148f5e2185af71..69dfb63bc45c337ce29bd993d822e06e6661a177 100644 --- a/proj/sxambient/vs2013/sxambient.vcxproj.filters +++ b/proj/sxambient/vs2013/sxambient.vcxproj.filters @@ -30,9 +30,6 @@ <ClCompile Include="..\..\..\source\level\sxlevel.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxanim/vs2013/sxanim.vcxproj b/proj/sxanim/vs2013/sxanim.vcxproj index 5db828bb30b68066bab323134d8852248abe2bd3..e20becf019bca210c7f7fcd838fbbad66df5363a 100644 --- a/proj/sxanim/vs2013/sxanim.vcxproj +++ b/proj/sxanim/vs2013/sxanim.vcxproj @@ -181,27 +181,36 @@ <ClCompile Include="..\..\..\source\anim\AnimatedModel.cpp" /> <ClCompile Include="..\..\..\source\anim\AnimatedModelProvider.cpp" /> <ClCompile Include="..\..\..\source\anim\AnimatedModelShared.cpp" /> + <ClCompile Include="..\..\..\source\anim\Decal.cpp" /> + <ClCompile Include="..\..\..\source\anim\DecalProvider.cpp" /> <ClCompile Include="..\..\..\source\anim\dllmain.cpp" /> <ClCompile Include="..\..\..\source\anim\DynamicModel.cpp" /> <ClCompile Include="..\..\..\source\anim\DynamicModelProvider.cpp" /> <ClCompile Include="..\..\..\source\anim\DynamicModelShared.cpp" /> + <ClCompile Include="..\..\..\source\anim\ModelOverlay.cpp" /> <ClCompile Include="..\..\..\source\anim\plugin_main.cpp" /> <ClCompile Include="..\..\..\source\anim\Renderable.cpp" /> <ClCompile Include="..\..\..\source\anim\RenderableVisibility.cpp" /> + <ClCompile Include="..\..\..\source\anim\StaticModelProvider.cpp" /> <ClCompile Include="..\..\..\source\anim\Updatable.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\anim\AnimatedModel.h" /> <ClInclude Include="..\..\..\source\anim\AnimatedModelProvider.h" /> <ClInclude Include="..\..\..\source\anim\AnimatedModelShared.h" /> + <ClInclude Include="..\..\..\source\anim\Decal.h" /> + <ClInclude Include="..\..\..\source\anim\DecalProvider.h" /> <ClInclude Include="..\..\..\source\anim\DynamicModel.h" /> <ClInclude Include="..\..\..\source\anim\DynamicModelProvider.h" /> <ClInclude Include="..\..\..\source\anim\DynamicModelShared.h" /> + <ClInclude Include="..\..\..\source\anim\ModelOverlay.h" /> <ClInclude Include="..\..\..\source\anim\Renderable.h" /> <ClInclude Include="..\..\..\source\anim\RenderableVisibility.h" /> + <ClInclude Include="..\..\..\source\anim\StaticModelProvider.h" /> <ClInclude Include="..\..\..\source\anim\Updatable.h" /> <ClInclude Include="..\..\..\source\xcommon\IXScene.h" /> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecal.h" /> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecalProvider.h" /> <ClInclude Include="..\..\..\source\xcommon\resource\IXModelProvider.h" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> diff --git a/proj/sxanim/vs2013/sxanim.vcxproj.filters b/proj/sxanim/vs2013/sxanim.vcxproj.filters index 54b74f26893854001719bc2841ccf10a4ee93e18..e907c6c64e5b8546f8410d5adccef4efb1c75ec3 100644 --- a/proj/sxanim/vs2013/sxanim.vcxproj.filters +++ b/proj/sxanim/vs2013/sxanim.vcxproj.filters @@ -18,9 +18,6 @@ </Filter> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\anim\Renderable.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -52,6 +49,18 @@ <ClCompile Include="..\..\..\source\anim\dllmain.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\anim\DecalProvider.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\source\anim\ModelOverlay.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\source\anim\Decal.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\..\source\anim\StaticModelProvider.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\anim\Renderable.h"> @@ -87,5 +96,23 @@ <ClInclude Include="..\..\..\source\xcommon\IXScene.h"> <Filter>Header Files\xcommon</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecalProvider.h"> + <Filter>Header Files\xcommon</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\xcommon\resource\IXDecal.h"> + <Filter>Header Files\xcommon</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\anim\DecalProvider.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\anim\ModelOverlay.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\anim\Decal.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\source\anim\StaticModelProvider.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> </Project> \ No newline at end of file diff --git a/proj/sxcore/vs2013/sxcore.vcxproj b/proj/sxcore/vs2013/sxcore.vcxproj index 44886dbddb45c65b3f818ae0d822c7444be8ec85..76c5c2bbd0d4a0be8d2b811f7abb7563e6af5e31 100644 --- a/proj/sxcore/vs2013/sxcore.vcxproj +++ b/proj/sxcore/vs2013/sxcore.vcxproj @@ -21,7 +21,6 @@ <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> <ClCompile Include="..\..\..\source\common\guid.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\core\AsyncFileReader.cpp" /> <ClCompile Include="..\..\..\source\core\AsyncTaskRunner.cpp" /> diff --git a/proj/sxcore/vs2013/sxcore.vcxproj.filters b/proj/sxcore/vs2013/sxcore.vcxproj.filters index 9faae7aa26d1072dcd4f4ca0899f5578841cb7fe..7a2f4723f5d59147eebf1bd6a9253f413c71b672 100644 --- a/proj/sxcore/vs2013/sxcore.vcxproj.filters +++ b/proj/sxcore/vs2013/sxcore.vcxproj.filters @@ -25,9 +25,6 @@ <ClCompile Include="..\..\..\source\core\Config.cpp"> <Filter>Sources</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Sources</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Sources</Filter> </ClCompile> diff --git a/proj/sxgame/vs2013/sxgame.vcxproj b/proj/sxgame/vs2013/sxgame.vcxproj index 8aa785a032493670f5738a00d3c180ec7ba75c9a..719b07cf19316d384ea0279356325d1deea0ab73 100644 --- a/proj/sxgame/vs2013/sxgame.vcxproj +++ b/proj/sxgame/vs2013/sxgame.vcxproj @@ -177,7 +177,6 @@ <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> <ClCompile Include="..\..\..\source\common\guid.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\game\BaseHandle.cpp" /> <ClCompile Include="..\..\..\source\game\BaseLight.cpp" /> @@ -213,6 +212,7 @@ <ClCompile Include="..\..\..\source\game\GUICraftController.cpp" /> <ClCompile Include="..\..\..\source\game\GUIInventoryController.cpp" /> <ClCompile Include="..\..\..\source\game\HUDcontroller.cpp" /> + <ClCompile Include="..\..\..\source\game\InfoOverlay.cpp" /> <ClCompile Include="..\..\..\source\game\InfoParticlePlayer.cpp" /> <ClCompile Include="..\..\..\source\game\LadderMovementController.cpp" /> <ClCompile Include="..\..\..\source\game\LightDirectional.cpp" /> @@ -302,6 +302,7 @@ <ClInclude Include="..\..\..\source\game\HUDcontroller.h" /> <ClInclude Include="..\..\..\source\game\IGameState.h" /> <ClInclude Include="..\..\..\source\game\IMovementController.h" /> + <ClInclude Include="..\..\..\source\game\InfoOverlay.h" /> <ClInclude Include="..\..\..\source\game\InfoParticlePlayer.h" /> <ClInclude Include="..\..\..\source\game\LadderMovementController.h" /> <ClInclude Include="..\..\..\source\game\LightSun.h" /> diff --git a/proj/sxgame/vs2013/sxgame.vcxproj.filters b/proj/sxgame/vs2013/sxgame.vcxproj.filters index 9a6014e06dfe003fc72058e0290c008fab2d51ca..f3634cd064470e2b0836ab30f2e7f289d71bef05 100644 --- a/proj/sxgame/vs2013/sxgame.vcxproj.filters +++ b/proj/sxgame/vs2013/sxgame.vcxproj.filters @@ -138,9 +138,6 @@ <ClCompile Include="..\..\..\source\game\EntityFactory.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\game\EntityManager.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -390,6 +387,9 @@ <ClCompile Include="..\..\..\source\game\DogholeMovementController.cpp"> <Filter>Source Files\character_movement</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\game\InfoOverlay.cpp"> + <Filter>Source Files\ents\info</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\game\sxgame.h"> @@ -680,6 +680,9 @@ <ClInclude Include="..\..\..\source\game\DogholeMovementController.h"> <Filter>Header Files\character_movement</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\game\InfoOverlay.h"> + <Filter>Header Files\ents\info</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\..\source\game\sxgame.rc"> diff --git a/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj b/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj index 7f122d3621569fe301cef423e7034c54410888d9..ab005f3e7f5d66103b5e21ff0eb1eb528610511a 100644 --- a/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj +++ b/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj @@ -94,7 +94,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\skyxengine.cpp" /> <ClCompile Include="..\..\..\source\sxgenpreview\sxgenpreview.cpp" /> diff --git a/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj.filters b/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj.filters index a5742b1b70341c23aef02f681aa3f9f712a015a8..a8dd1d5fc23cbf0237f88190495660ab527db1e3 100644 --- a/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj.filters +++ b/proj/sxgenpreview/vs2013/sxgenpreview.vcxproj.filters @@ -21,9 +21,6 @@ <ClCompile Include="..\..\..\source\skyxengine.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxgreen/sxgreen/sxgreen.vcxproj b/proj/sxgreen/sxgreen/sxgreen.vcxproj index 2eda957b846263c65e6cb8bac30e74b871f2ef4e..ec21b341c0d7433680f2b6cb50305c195a402820 100644 --- a/proj/sxgreen/sxgreen/sxgreen.vcxproj +++ b/proj/sxgreen/sxgreen/sxgreen.vcxproj @@ -178,7 +178,6 @@ <ClInclude Include="..\..\..\source\green\sxgreen.h" /> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\green\green.cpp" /> <ClCompile Include="..\..\..\source\green\sxgreen.cpp" /> <ClCompile Include="..\..\..\source\green\sxgreen_dll.cpp" /> diff --git a/proj/sxgreen/sxgreen/sxgreen.vcxproj.filters b/proj/sxgreen/sxgreen/sxgreen.vcxproj.filters index 7d086b127ffbbab6271208a016119431e18a6694..3cd9959933e57417d1361f033ef6d51cba1b557f 100644 --- a/proj/sxgreen/sxgreen/sxgreen.vcxproj.filters +++ b/proj/sxgreen/sxgreen/sxgreen.vcxproj.filters @@ -35,8 +35,5 @@ <ClCompile Include="..\..\..\source\green\sxgreen.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> </Project> \ No newline at end of file diff --git a/proj/sxgui/vs2013/sxgui.vcxproj b/proj/sxgui/vs2013/sxgui.vcxproj index 20a5301f425a9c32355ab91a0027d9be8697c584..79c65b41f962166be7a1dac4290f838ffb58e207 100644 --- a/proj/sxgui/vs2013/sxgui.vcxproj +++ b/proj/sxgui/vs2013/sxgui.vcxproj @@ -241,7 +241,6 @@ <ClInclude Include="..\..\..\source\gui\ITD.h" /> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\gui\CSSstyle.cpp" /> <ClCompile Include="..\..\..\source\gui\IBODY.cpp" /> <ClCompile Include="..\..\..\source\gui\IBR.cpp" /> diff --git a/proj/sxgui/vs2013/sxgui.vcxproj.filters b/proj/sxgui/vs2013/sxgui.vcxproj.filters index e742664b72bf81314a9ef98e9d50d83b04fb9b4a..2e3bc8f4b214a279cc0794838ed0d10535611e32 100644 --- a/proj/sxgui/vs2013/sxgui.vcxproj.filters +++ b/proj/sxgui/vs2013/sxgui.vcxproj.filters @@ -308,9 +308,6 @@ <ClCompile Include="..\..\..\source\gui\VideoRenderer.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\gui\ICSSstyle.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxgui2/vs2013/sxgui2.vcxproj b/proj/sxgui2/vs2013/sxgui2.vcxproj index be80b15a6962ba50b12f80faf7dafd25e3dd3416..4a60a42afaf36331691553762f13f693126daf1d 100644 --- a/proj/sxgui2/vs2013/sxgui2.vcxproj +++ b/proj/sxgui2/vs2013/sxgui2.vcxproj @@ -19,7 +19,6 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\gui2\CSSLexer.cpp" /> <ClCompile Include="..\..\..\source\gui2\CSSProperty.cpp" /> <ClCompile Include="..\..\..\source\gui2\Desktop.cpp" /> diff --git a/proj/sxgui2/vs2013/sxgui2.vcxproj.filters b/proj/sxgui2/vs2013/sxgui2.vcxproj.filters index 147e6315d56037744af0ea984a2ef01e62d7d420..d91712645e1df7be0fd982e715f5cf7ae87da1cf 100644 --- a/proj/sxgui2/vs2013/sxgui2.vcxproj.filters +++ b/proj/sxgui2/vs2013/sxgui2.vcxproj.filters @@ -49,9 +49,6 @@ <ClCompile Include="..\..\..\source\gui2\FontManager.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\xcommon\gui\IXDesktop.h"> diff --git a/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj b/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj index d5b37e03af67340b1980fa2482d0c9d2c281af6e..d9702a8d08c44dc30716d06adc2caf1290da215d 100644 --- a/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj +++ b/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj @@ -12,7 +12,6 @@ </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\SXGUIWinApi\base.cpp" /> <ClCompile Include="..\..\..\source\SXGUIWinApi\base_wnd.cpp" /> diff --git a/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj.filters b/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj.filters index 346c7ffdd25cb87feb4a2aeefb7544f1feb33b85..f4bd08eb752d7a9999cc36f61138b62724b3b1ad 100644 --- a/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj.filters +++ b/proj/sxguiwinapi/vs2013/sxguiwinapi.vcxproj.filters @@ -84,9 +84,6 @@ <ClCompile Include="..\..\..\source\SXGUIWinApi\dialog_select_dir.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\SXGUIWinApi\dialog_select_file.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj b/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj index 54637a3266e5d534c6d9610df14fc78ccf4b3f5d..b761091d68ce1c7a42c6ae3037bc016fa3d2c693 100644 --- a/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj +++ b/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj @@ -94,7 +94,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\editors_utils\axes_helper.cpp" /> <ClCompile Include="..\..\..\source\skyxengine.cpp" /> diff --git a/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj.filters b/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj.filters index 2f1709240240c769f6ba82ee927746722f75048b..a280298b7a9b10aee3a5929938c6843aa5ac79d0 100644 --- a/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj.filters +++ b/proj/sxleveleditor/vs2013/sxleveleditor.vcxproj.filters @@ -42,9 +42,6 @@ <ClCompile Include="..\..\..\source\editors_utils\axes_helper.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj b/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj index 4ba27d1f2e7a1d62207bef5384024f6cfd5985fb..54d8769a14cb6ce0ca8c55ec59fdea8eb26b5e87 100644 --- a/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj +++ b/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj @@ -94,7 +94,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\skyxengine.cpp" /> <ClCompile Include="..\..\..\source\sxmaterialeditor\buttons_callback.cpp"> diff --git a/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj.filters b/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj.filters index 47a31e8a5fe410f28aca68eebb98daf36e1c510f..2e82289cd82e1e122a27ce48fb392b06731e4b6e 100644 --- a/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj.filters +++ b/proj/sxmaterialeditor/vs2013/sxmaterialeditor.vcxproj.filters @@ -42,9 +42,6 @@ <ClCompile Include="..\..\..\source\skyxengine.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxmtrl/vs2013/sxmtrl.vcxproj b/proj/sxmtrl/vs2013/sxmtrl.vcxproj index 2defc0c718b37879a513f90bb9e6e160a3391f6b..126b3c9978385cdbf59f6538583b5989e9b9d66b 100644 --- a/proj/sxmtrl/vs2013/sxmtrl.vcxproj +++ b/proj/sxmtrl/vs2013/sxmtrl.vcxproj @@ -171,7 +171,6 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\mtrl\LogicExpression.cpp" /> <ClCompile Include="..\..\..\source\mtrl\MaterialSystem.cpp" /> <ClCompile Include="..\..\..\source\mtrl\plugin_main.cpp" /> diff --git a/proj/sxmtrl/vs2013/sxmtrl.vcxproj.filters b/proj/sxmtrl/vs2013/sxmtrl.vcxproj.filters index 556583a8857c31bf00d8b6c851c7af52b894ebb6..a2a53a7ee64ba10ed2267e94f501bd990291a2af 100644 --- a/proj/sxmtrl/vs2013/sxmtrl.vcxproj.filters +++ b/proj/sxmtrl/vs2013/sxmtrl.vcxproj.filters @@ -18,9 +18,6 @@ </Filter> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\mtrl\MaterialSystem.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxparticles/vs2013/sxparticles.vcxproj b/proj/sxparticles/vs2013/sxparticles.vcxproj index db6a76ae81b48065d7f2f9b58698e8c10a036d1b..af0ac95fe58206fea125c6d5b7b3b9490c9a33be 100644 --- a/proj/sxparticles/vs2013/sxparticles.vcxproj +++ b/proj/sxparticles/vs2013/sxparticles.vcxproj @@ -178,7 +178,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\particles\effect.cpp"> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild> diff --git a/proj/sxparticles/vs2013/sxparticles.vcxproj.filters b/proj/sxparticles/vs2013/sxparticles.vcxproj.filters index c5fa3d4d23452be16bddf05e3390dfb05c6278e6..0759bd9e4c9d24258d7fe596d9b744e76861575e 100644 --- a/proj/sxparticles/vs2013/sxparticles.vcxproj.filters +++ b/proj/sxparticles/vs2013/sxparticles.vcxproj.filters @@ -27,9 +27,6 @@ <ClCompile Include="..\..\..\source\particles\effect.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj b/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj index 0a8a4b7329bbf3988677cc1ded3d0470851e907a..4a34bbcefcac880f5656c5cf9b5bf1f0435b34a4 100644 --- a/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj +++ b/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj @@ -94,7 +94,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\skyxengine.cpp" /> <ClCompile Include="..\..\..\source\sxparticleseditor\callback_common.cpp"> diff --git a/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj.filters b/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj.filters index 9f8f4897b5e8f8c5c0e46d8d06d05fe249b669a9..6cac98a6e83d43a2229ed9b0aba58f918b1a8d66 100644 --- a/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj.filters +++ b/proj/sxparticleseditor/vs2013/sxparticleseditor.vcxproj.filters @@ -33,9 +33,6 @@ <ClCompile Include="..\..\..\source\sxparticleseditor\callback_list.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\skyxengine.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxphysics/vs2013/sxphysics.vcxproj b/proj/sxphysics/vs2013/sxphysics.vcxproj index c063e34acc98f937e4ee88fbfa1db8cf2f33fd86..e327650bbc2b9b720baab939ebd0143a64feddaf 100644 --- a/proj/sxphysics/vs2013/sxphysics.vcxproj +++ b/proj/sxphysics/vs2013/sxphysics.vcxproj @@ -174,7 +174,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\physics\CharacterController.cpp" /> <ClCompile Include="..\..\..\source\physics\CollisionObject.cpp" /> diff --git a/proj/sxphysics/vs2013/sxphysics.vcxproj.filters b/proj/sxphysics/vs2013/sxphysics.vcxproj.filters index 9d75b2bfebf68daf795f56ef25a725b4f4e3aa6c..cb4b3c0159fa69f91530cdab25993550b707889a 100644 --- a/proj/sxphysics/vs2013/sxphysics.vcxproj.filters +++ b/proj/sxphysics/vs2013/sxphysics.vcxproj.filters @@ -24,9 +24,6 @@ <ClCompile Include="..\..\..\source\physics\PhyWorld.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/sxrender/vs2013/sxrender.vcxproj b/proj/sxrender/vs2013/sxrender.vcxproj index 20a0cf87981bda29025bc776cf49e70128096b7e..9a60037fa2eb809a7dac88820c18c209c52db90e 100644 --- a/proj/sxrender/vs2013/sxrender.vcxproj +++ b/proj/sxrender/vs2013/sxrender.vcxproj @@ -224,7 +224,6 @@ </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\guid.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\render\BaseTarget.cpp" /> <ClCompile Include="..\..\..\source\render\Camera.cpp" /> diff --git a/proj/sxrender/vs2013/sxrender.vcxproj.filters b/proj/sxrender/vs2013/sxrender.vcxproj.filters index 94b82a6504b8b9370cb1ed598cb52e58f4441274..48693c71cca4da1427a1733da49964c1a0b4a135 100644 --- a/proj/sxrender/vs2013/sxrender.vcxproj.filters +++ b/proj/sxrender/vs2013/sxrender.vcxproj.filters @@ -116,9 +116,6 @@ </ClInclude> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\render\RenderableVisibility.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/terrax/vs2013/terrax.vcxproj b/proj/terrax/vs2013/terrax.vcxproj index 3714e7454c5446f0fc1fa17a452affecb38d0674..461cd97712f84454376cb5f6e7a277ec2993bd4f 100644 --- a/proj/terrax/vs2013/terrax.vcxproj +++ b/proj/terrax/vs2013/terrax.vcxproj @@ -195,7 +195,6 @@ <ItemGroup> <ClCompile Include="..\..\..\source\common\file_utils.cpp" /> <ClCompile Include="..\..\..\source\common\guid.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\common\string_utils.cpp" /> <ClCompile Include="..\..\..\source\skyxengine.cpp"> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> @@ -245,6 +244,7 @@ <ClCompile Include="..\..\..\source\terrax\MaterialEditor.cpp" /> <ClCompile Include="..\..\..\source\terrax\PropertyWindow.cpp" /> <ClCompile Include="..\..\..\source\terrax\ProxyObject.cpp" /> + <ClCompile Include="..\..\..\source\terrax\ResourceBrowser.cpp" /> <ClCompile Include="..\..\..\source\terrax\SceneTreeWindow.cpp" /> <ClCompile Include="..\..\..\source\terrax\ScrollBar.cpp" /> <ClCompile Include="..\..\..\source\terrax\terrax.cpp" /> @@ -304,6 +304,7 @@ <ClInclude Include="..\..\..\source\terrax\MaterialEditor.h" /> <ClInclude Include="..\..\..\source\terrax\PropertyWindow.h" /> <ClInclude Include="..\..\..\source\terrax\ProxyObject.h" /> + <ClInclude Include="..\..\..\source\terrax\ResourceBrowser.h" /> <ClInclude Include="..\..\..\source\terrax\SceneTreeWindow.h" /> <ClInclude Include="..\..\..\source\terrax\ScrollBar.h" /> <ClInclude Include="..\..\..\source\terrax\TextureViewGraphNode.h" /> diff --git a/proj/terrax/vs2013/terrax.vcxproj.filters b/proj/terrax/vs2013/terrax.vcxproj.filters index c7f15f1fc8863e186cc481aa6a876468a8a27a9d..71fef577fe56b1114d70f818b5cdfa5b198c4d30 100644 --- a/proj/terrax/vs2013/terrax.vcxproj.filters +++ b/proj/terrax/vs2013/terrax.vcxproj.filters @@ -57,9 +57,6 @@ <ClCompile Include="..\..\..\source\skyxengine.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\common\file_utils.cpp"> <Filter>Source Files</Filter> </ClCompile> @@ -216,6 +213,9 @@ <ClCompile Include="..\..\..\source\terrax\CommandUngroup.cpp"> <Filter>Source Files\cmd</Filter> </ClCompile> + <ClCompile Include="..\..\..\source\terrax\ResourceBrowser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\..\source\terrax\terrax.rc"> @@ -418,6 +418,9 @@ <ClInclude Include="..\..\..\source\terrax\CommandUngroup.h"> <Filter>Header Files\cmd</Filter> </ClInclude> + <ClInclude Include="..\..\..\source\terrax\ResourceBrowser.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Image Include="..\..\..\source\terrax\resource\new.bmp"> diff --git a/proj/wadplugin/vs2013/wadplugin.vcxproj b/proj/wadplugin/vs2013/wadplugin.vcxproj index 9d6ee1bb6a89ff9b9d535b00baa28fcb7fcf2811..ee4f5c663f7f3a674c83bc2096d8a27f5af8ac68 100644 --- a/proj/wadplugin/vs2013/wadplugin.vcxproj +++ b/proj/wadplugin/vs2013/wadplugin.vcxproj @@ -19,7 +19,6 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\wadplugin\dllmain.cpp" /> <ClCompile Include="..\..\..\source\wadplugin\plugin_main.cpp" /> <ClCompile Include="..\..\..\source\wadplugin\TextureLoader.cpp" /> diff --git a/proj/wadplugin/vs2013/wadplugin.vcxproj.filters b/proj/wadplugin/vs2013/wadplugin.vcxproj.filters index 8ac55cf8a9ebc7c1e31108fc3297b1e848c49f39..b6a8ceb9fe868a17d7c796e73c04c43b065a742e 100644 --- a/proj/wadplugin/vs2013/wadplugin.vcxproj.filters +++ b/proj/wadplugin/vs2013/wadplugin.vcxproj.filters @@ -25,9 +25,6 @@ <ClCompile Include="..\..\..\source\wadplugin\TextureProxy.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\wadplugin\TextureLoader.h"> diff --git a/proj/wavplugin/vs2013/wavplugin.vcxproj b/proj/wavplugin/vs2013/wavplugin.vcxproj index fe9aa5dc348677274e033ecd7ceee81c298c6dc2..4c34641e261a1b8f9d166b8d87ac6abcfdf4ecee 100644 --- a/proj/wavplugin/vs2013/wavplugin.vcxproj +++ b/proj/wavplugin/vs2013/wavplugin.vcxproj @@ -159,7 +159,6 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\wavplugin\AudioCodecWave.cpp" /> <ClCompile Include="..\..\..\source\wavplugin\dllmain.cpp" /> <ClCompile Include="..\..\..\source\wavplugin\plugin_main.cpp" /> diff --git a/proj/wavplugin/vs2013/wavplugin.vcxproj.filters b/proj/wavplugin/vs2013/wavplugin.vcxproj.filters index d92acb38921c4cfda2b03602b2fffb7d23a3c857..7a0ba1c846bc88201af3eb514cd4b420626bbc0b 100644 --- a/proj/wavplugin/vs2013/wavplugin.vcxproj.filters +++ b/proj/wavplugin/vs2013/wavplugin.vcxproj.filters @@ -22,9 +22,6 @@ <Filter>Source Files</Filter> </ClCompile> <ClCompile Include="..\..\..\source\wavplugin\plugin_main.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\wavplugin\AudioCodecWave.h"> diff --git a/proj/xCSG/vs2013/xCSG.vcxproj b/proj/xCSG/vs2013/xCSG.vcxproj index 446f41f63324bf7344c0a9fa6bea21a7822c3eea..7b9eef946222811d2a8b593eb20e42d5646f1642 100644 --- a/proj/xCSG/vs2013/xCSG.vcxproj +++ b/proj/xCSG/vs2013/xCSG.vcxproj @@ -20,7 +20,6 @@ </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\guid.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\xcsg\BrushCreatorBox.cpp" /> <ClCompile Include="..\..\..\source\xcsg\BrushCreatorFree.cpp" /> <ClCompile Include="..\..\..\source\xcsg\BrushMesh.cpp" /> diff --git a/proj/xCSG/vs2013/xCSG.vcxproj.filters b/proj/xCSG/vs2013/xCSG.vcxproj.filters index 85c4409a86facd4e7ef75266a0b099bb9e6ea545..a8202fa58e86b78b7e2e72e743c3b0423d02786d 100644 --- a/proj/xCSG/vs2013/xCSG.vcxproj.filters +++ b/proj/xCSG/vs2013/xCSG.vcxproj.filters @@ -37,9 +37,6 @@ <ClCompile Include="..\..\..\source\xcsg\Editable.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\xcsg\Outline.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/xEngine/vs2013/xEngine.vcxproj b/proj/xEngine/vs2013/xEngine.vcxproj index 2595ac7211d9f1f8e67f69753504403fb29c3263..94f72003f741e0d4ce6b04a0f9e94a5058cf18b4 100644 --- a/proj/xEngine/vs2013/xEngine.vcxproj +++ b/proj/xEngine/vs2013/xEngine.vcxproj @@ -179,7 +179,6 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\xEngine\CommandLineToArgvA.cpp" /> <ClCompile Include="..\..\..\source\xEngine\dllmain.cpp" /> <ClCompile Include="..\..\..\source\xEngine\Engine.cpp" /> diff --git a/proj/xEngine/vs2013/xEngine.vcxproj.filters b/proj/xEngine/vs2013/xEngine.vcxproj.filters index 67b9b20d6e90515e4f392356200d8a411bbd61dc..56c14a0234483508abc5b4ff860c2aef9d465cc5 100644 --- a/proj/xEngine/vs2013/xEngine.vcxproj.filters +++ b/proj/xEngine/vs2013/xEngine.vcxproj.filters @@ -24,9 +24,6 @@ <ClCompile Include="..\..\..\source\xEngine\CommandLineToArgvA.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\xEngine\Engine.h"> diff --git a/proj/xParticles/vs2013/xParticles.vcxproj b/proj/xParticles/vs2013/xParticles.vcxproj index 3e9a04dee83388d9f52c60c677b38cfb7dc5ce65..6735b27297947599759b328aa81284464185e0cf 100644 --- a/proj/xParticles/vs2013/xParticles.vcxproj +++ b/proj/xParticles/vs2013/xParticles.vcxproj @@ -19,7 +19,6 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\xParticles\dllmain.cpp" /> <ClCompile Include="..\..\..\source\xParticles\Editable.cpp" /> <ClCompile Include="..\..\..\source\xParticles\EditorExtension.cpp" /> diff --git a/proj/xParticles/vs2013/xParticles.vcxproj.filters b/proj/xParticles/vs2013/xParticles.vcxproj.filters index 8eebf1833b0a49d001f79604922a70f5d887a2d4..be26e103897bdab0e0b6a1a615ac18bf78fd2b1d 100644 --- a/proj/xParticles/vs2013/xParticles.vcxproj.filters +++ b/proj/xParticles/vs2013/xParticles.vcxproj.filters @@ -46,9 +46,6 @@ <ClCompile Include="..\..\..\source\xParticles\EffectLoader.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\xParticles\Updatable.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/proj/xSpecs/vs2013/xSpecs.vcxproj b/proj/xSpecs/vs2013/xSpecs.vcxproj index d27e69f1fc24204e7fd7a9f912f0388df061d431..c3d94c88d558374ac95d70ee08f2eab8aeed3250 100644 --- a/proj/xSpecs/vs2013/xSpecs.vcxproj +++ b/proj/xSpecs/vs2013/xSpecs.vcxproj @@ -19,7 +19,6 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\xSpecs\dllmain.cpp" /> <ClCompile Include="..\..\..\source\xSpecs\MaterialLoader.cpp" /> <ClCompile Include="..\..\..\source\xSpecs\MaterialProxy.cpp" /> diff --git a/proj/xSpecs/vs2013/xSpecs.vcxproj.filters b/proj/xSpecs/vs2013/xSpecs.vcxproj.filters index 4db0e36bc9a014bb36631418704927b4717f659e..e438dc681ca5d4f2465dd9b0ffbf25010cc3383f 100644 --- a/proj/xSpecs/vs2013/xSpecs.vcxproj.filters +++ b/proj/xSpecs/vs2013/xSpecs.vcxproj.filters @@ -28,9 +28,6 @@ <ClCompile Include="..\..\..\source\xSpecs\MaterialLoader.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\..\source\xSpecs\TextureProxy.h"> diff --git a/proj/xUI/vs2013/xUI.vcxproj b/proj/xUI/vs2013/xUI.vcxproj index f925267b00c5fe54af197a4c64cbce3fa361d9e1..033aaf44d7457d0418dee7c9befc1db2a3e167b4 100644 --- a/proj/xUI/vs2013/xUI.vcxproj +++ b/proj/xUI/vs2013/xUI.vcxproj @@ -69,7 +69,6 @@ </ItemGroup> <ItemGroup> <ClCompile Include="..\..\..\source\common\guid.cpp" /> - <ClCompile Include="..\..\..\source\common\string.cpp" /> <ClCompile Include="..\..\..\source\xUI\CurvePreviewGraphNode.cpp" /> <ClCompile Include="..\..\..\source\xUI\CurvePreviewGraphNodeData.cpp" /> <ClCompile Include="..\..\..\source\xUI\GradientPreviewGraphNodeData.cpp" /> diff --git a/proj/xUI/vs2013/xUI.vcxproj.filters b/proj/xUI/vs2013/xUI.vcxproj.filters index 531c6e555bc477605ec655dbce043a12ab5d2cde..5912ed89b5b765a42e2e110c41bb4a2a1375a16f 100644 --- a/proj/xUI/vs2013/xUI.vcxproj.filters +++ b/proj/xUI/vs2013/xUI.vcxproj.filters @@ -173,9 +173,6 @@ <ClCompile Include="..\..\..\source\xUI\UIButton.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="..\..\..\source\common\string.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="..\..\..\source\xUI\UITextBox.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/sdks/graphix b/sdks/graphix index 27fedecbe4e3921dd1da215c65a6886755c1a498..d8eeb9ae81fae1256034680abeb5b3dc14608770 160000 --- a/sdks/graphix +++ b/sdks/graphix @@ -1 +1 @@ -Subproject commit 27fedecbe4e3921dd1da215c65a6886755c1a498 +Subproject commit d8eeb9ae81fae1256034680abeb5b3dc14608770 diff --git a/sdks/mital b/sdks/mital index c814073bb3bce76b111684d7afb7d74d303f55bf..384aa665b3e46cb12a4cc2f2e0b418f925b8bbd1 160000 --- a/sdks/mital +++ b/sdks/mital @@ -1 +1 @@ -Subproject commit c814073bb3bce76b111684d7afb7d74d303f55bf +Subproject commit 384aa665b3e46cb12a4cc2f2e0b418f925b8bbd1 diff --git a/source/SkyXEngine_Build/SkyXEngine_Build.cpp b/source/SkyXEngine_Build/SkyXEngine_Build.cpp index 27dd74683de49304a6a630046cc34335b1fa2241..f5c616e0d03a0b3f46aa4a41f3a2c6012d946f0b 100644 --- a/source/SkyXEngine_Build/SkyXEngine_Build.cpp +++ b/source/SkyXEngine_Build/SkyXEngine_Build.cpp @@ -154,7 +154,7 @@ int main(int argc, char **argv) pEngine->getCore()->getConsole()->execCommand("exec ../config_game.cfg"); pEngine->getCore()->getConsole()->execCommand("exec ../config_game_user.cfg"); -#if 1 +#if 0 IFileSystem *pFS = pEngine->getCore()->getFileSystem(); IFile *pFile1 = pFS->openFile("dir/test.txt", FILE_MODE_READ); diff --git a/source/anim/AnimatedModelProvider.cpp b/source/anim/AnimatedModelProvider.cpp index 7265f05afe21f7cc46a3741aacdbd90febda6034..9147e9e896e73633729ddec5f844894268343fb8 100644 --- a/source/anim/AnimatedModelProvider.cpp +++ b/source/anim/AnimatedModelProvider.cpp @@ -202,6 +202,11 @@ void CAnimatedModelProvider::bindVertexFormat() m_pMaterialSystem->bindVS(m_pVertexShaderHandler); } +bool CAnimatedModelProvider::hasPendingOps() +{ + return(!m_queueGPUinitModel.empty() || !m_queueGPUinitShared.empty()); +} + void CAnimatedModelProvider::render(CRenderableVisibility *pVisibility) { XPROFILE_FUNCTION(); diff --git a/source/anim/AnimatedModelProvider.h b/source/anim/AnimatedModelProvider.h index a9242e6352a19a3fd0edb65edaa17fee6974dfb9..4789f1b6483a20fd8d9f5ca619c67c5886ce0af3 100644 --- a/source/anim/AnimatedModelProvider.h +++ b/source/anim/AnimatedModelProvider.h @@ -37,6 +37,8 @@ public: void bindVertexFormat(); + bool hasPendingOps(); + protected: AssotiativeArray<IXResourceModelAnimated*, Array<CAnimatedModelShared*>> m_mModels; diff --git a/source/anim/Decal.cpp b/source/anim/Decal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18c808f591f1dc7419b80d62252a1c0d9c76d326 --- /dev/null +++ b/source/anim/Decal.cpp @@ -0,0 +1,657 @@ +#include "Decal.h" +#include "DynamicModel.h" +#include "DecalProvider.h" + +CDecal::CDecal(IXSceneQuery *pSceneQuery, IXRender *pRender, CDecalProvider *pProvider): + m_pSceneQuery(pSceneQuery), + m_pRender(pRender), + m_pProvider(pProvider) +{ +} + +CDecal::~CDecal() +{ + removeOverlays(); + mem_release(m_pMaterial); +} + +bool XMETHODCALLTYPE CDecal::isEnabled() const +{ + return(m_isEnabled); +} +void XMETHODCALLTYPE CDecal::enable(bool yesNo) +{ + m_isEnabled = yesNo; + TODO("Change state"); +} + +float3 XMETHODCALLTYPE CDecal::getPosition() const +{ + return(m_vPos); +} +void XMETHODCALLTYPE CDecal::setPosition(const float3 &vPos) +{ + m_vPos = vPos; + setDirty(); +} + +SMQuaternion XMETHODCALLTYPE CDecal::getOrientation() const +{ + return(m_qRot); +} +void XMETHODCALLTYPE CDecal::setOrientation(const SMQuaternion &qRot) +{ + m_qRot = qRot; + setDirty(); +} + +float3 XMETHODCALLTYPE CDecal::getLocalBoundMin() const +{ + float3 vBound; + for(UINT i = 0; i < DECAL_POINTS; ++i) + { + if(i == 0) + { + vBound = m_qRot * float3(m_avCorners[0], 0.0f); + } + else + { + vBound = SMVectorMin(vBound, m_qRot * float3(m_avCorners[0], 0.0f)); + } + vBound = SMVectorMin(vBound, m_qRot * float3(m_avCorners[0], m_fHeight)); + } + + return(vBound); +} +float3 XMETHODCALLTYPE CDecal::getLocalBoundMax() const +{ + float3 vBound; + for(UINT i = 0; i < DECAL_POINTS; ++i) + { + if(i == 0) + { + vBound = m_qRot * float3(m_avCorners[0], 0.0f); + } + else + { + vBound = SMVectorMax(vBound, m_qRot * float3(m_avCorners[0], 0.0f)); + } + vBound = SMVectorMax(vBound, m_qRot * float3(m_avCorners[0], m_fHeight)); + } + + return(vBound); +} +SMAABB XMETHODCALLTYPE CDecal::getLocalBound() const +{ + return(SMAABB(getLocalBoundMin(), getLocalBoundMax())); +} + +bool XMETHODCALLTYPE CDecal::rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut, float3 *pvNormal) +{ + fora(i, m_aOverlays) + { + const Overlay &overlay = m_aOverlays[i]; + const Array<XResourceModelStaticVertex> &aVertices = overlay.pOverlay->getVertices(); + + float3 vRayStart = vStart; + float3 vRayEnd = vEnd; + + if(!overlay.pModel->isStatic()) + { + // transform ray to local space + float3 vPos = overlay.pModel->getPosition(); + SMQuaternion qRot = overlay.pModel->getOrientation(); + + vRayStart = qRot.Conjugate() * vRayStart - vPos; + vRayEnd = qRot.Conjugate() * vRayEnd - vPos; + } + + for(UINT j = 0, jl = aVertices.size(); j < jl; j += 4) + { + // 012 213 + if(SMTriangleIntersectLine(aVertices[j].vPos, aVertices[j + 1].vPos, aVertices[j + 2].vPos, vRayStart, vRayEnd, pvOut)) + { + if(pvNormal) + { + *pvNormal = SMVector3Normalize(SMVector3Cross(aVertices[j + 1].vPos - aVertices[j].vPos, aVertices[j + 2].vPos - aVertices[j].vPos)); + } + return(true); + } + + if(!SMIsZero(SMVector3Length2(aVertices[j + 3].vPos - aVertices[j + 2].vPos)) && SMTriangleIntersectLine(aVertices[j + 2].vPos, aVertices[j + 1].vPos, aVertices[j + 3].vPos, vRayStart, vRayEnd, pvOut)) + { + if(pvNormal) + { + *pvNormal = SMVector3Normalize(SMVector3Cross(aVertices[j + 1].vPos - aVertices[j + 2].vPos, aVertices[j + 3].vPos - aVertices[j + 2].vPos)); + } + return(true); + } + } + } + return(false); +} + +void XMETHODCALLTYPE CDecal::setLayer(UINT uLayer) +{ + m_uLayer = uLayer; + setDirty(); +} +UINT XMETHODCALLTYPE CDecal::getLayer() +{ + return(m_uLayer); +} + +void XMETHODCALLTYPE CDecal::setHeight(float fHeight) +{ + m_fHeight = fHeight; + setDirty(); +} +float XMETHODCALLTYPE CDecal::getHeight() +{ + return(m_fHeight); +} + +void XMETHODCALLTYPE CDecal::setTextureRangeU(const float2_t &vRange) +{ + m_vTexRangeU = vRange; +} +float2_t XMETHODCALLTYPE CDecal::getTextureRangeU() +{ + return(m_vTexRangeU); +} + +void XMETHODCALLTYPE CDecal::setTextureRangeV(const float2_t &vRange) +{ + m_vTexRangeV = vRange; +} +float2_t XMETHODCALLTYPE CDecal::getTextureRangeV() +{ + return(m_vTexRangeV); +} + +void XMETHODCALLTYPE CDecal::setMaterial(const char *szMaterial) +{ + mem_release(m_pMaterial); + m_pProvider->getMaterialSystem()->loadMaterial(szMaterial, &m_pMaterial); + + setDirty(); +} + +void CDecal::setMaterial(IXMaterial *pMaterial) +{ + mem_release(m_pMaterial); + m_pMaterial = pMaterial; + add_ref(m_pMaterial); + + setDirty(); +} +void XMETHODCALLTYPE CDecal::setCorner(UINT uCorner, const float2_t &vCorner) +{ + assert(uCorner < DECAL_POINTS); + if(uCorner < DECAL_POINTS) + { + m_avCorners[uCorner] = vCorner; + setDirty(); + } +} + +void CDecal::update( +#ifdef DECAL_DEBUG_DRAW + IXGizmoRenderer *pDebugRenderer +#endif +) +{ + if(m_isDirty) + { + // cleanup old overlays + removeOverlays(); + + // query for objects + + SMPLANE aPlanes[] = { + SMPlaneNormalize(SMPLANE(float3(m_avCorners[0], 0.0f), float3(m_avCorners[1], 0.0f), float3(m_avCorners[1], m_fHeight))), + SMPlaneNormalize(SMPLANE(float3(m_avCorners[2], 0.0f), float3(m_avCorners[3], 0.0f), float3(m_avCorners[3], m_fHeight))), + + SMPlaneNormalize(SMPLANE(float3(m_avCorners[1], 0.0f), float3(m_avCorners[2], 0.0f), float3(m_avCorners[2], m_fHeight))), + SMPlaneNormalize(SMPLANE(float3(m_avCorners[3], 0.0f), float3(m_avCorners[0], 0.0f), float3(m_avCorners[0], m_fHeight))), + + SMPLANE(0.0f, 0.0f, 1.0f, m_fHeight), + SMPLANE(0.0f, 0.0f, -1.0f, m_fHeight), + }; + + //SMMATRIX mWorld = SMMatrixTranspose(m_qRot.GetMatrix() * SMMatrixTranslation(m_vPos)); + //SMMATRIX mWorld = m_qRot.GetMatrix(); + for(UINT i = 0; i < ARRAYSIZE(aPlanes); ++i) + { + //aPlanes[i] = SMPlaneTransformTI(aPlanes[i], mWorld); + aPlanes[i] = SMPLANE(m_qRot * float3(aPlanes[i]), aPlanes[i].w); + aPlanes[i].w -= SMVector3Dot(aPlanes[i], m_vPos); + } + + IXFrustum *pFrustum; + m_pRender->newFrustum(&pFrustum); + + pFrustum->update(aPlanes, false); + +#ifdef DECAL_DEBUG_DRAW + pDebugRenderer->setColor(float4(1.0f, 1.0f, 0.0f, 1.0f)); + for(UINT i = 0; i < 8; ++i) + { + pDebugRenderer->drawPoint(pFrustum->getPoint(i)); + } + pDebugRenderer->setColor(float4(1.0f, 1.0f, 0.0f, 0.2f)); +#endif + + CDynamicModel **ppObjects = NULL; + m_pSceneQuery->setLayerMask(1 << m_uLayer); + UINT uCount = m_pSceneQuery->execute(pFrustum, (void***)&ppObjects); + + SMPLANE aPlanesObjectSpace[ARRAYSIZE(aPlanes)]; + + // build overlays + for(UINT i = 0; i < uCount; ++i) + { + TODO("Check ppObjects[i]->receivingDecals()"); + + for(UINT j = 0; j < ARRAYSIZE(aPlanes); ++j) + { + aPlanesObjectSpace[j] = SMPLANE(ppObjects[i]->getOrientation().Conjugate() * float3(aPlanes[j]), aPlanes[j].w + SMVector3Dot(aPlanes[j], ppObjects[i]->getPosition())); + } + pFrustum->update(aPlanesObjectSpace, false); + + spawnOverlayForModel(ppObjects[i], pFrustum +#ifdef DECAL_DEBUG_DRAW + , pDebugRenderer +#endif + ); + } + + mem_release(pFrustum); + + m_isDirty = false; + } +} + +void CDecal::onOverlayRemoved(CModelOverlay *pOverlay) +{ + int idx = m_aOverlays.indexOf(pOverlay, [](const Overlay &a, CModelOverlay *pB){ + return(a.pOverlay == pB); + }); + assert(idx >= 0); + if(idx >= 0) + { + m_aOverlays.erase(idx); + m_pProvider->freeOverlay(pOverlay); + if(!m_aOverlays.size()) + { + m_pProvider->onDecalEmptied(this); + } + } +} + +template<typename T> +static T ArrGet(T *pArr, UINT uSize, UINT uIdx, int iStartOffset = 0) +{ + return(pArr[((int)uIdx + iStartOffset) % uSize]); +} + +template<typename T> +static void ArrDel(T *pArr, UINT &uSize, UINT uIdx, int &iStartOffset) +{ + UINT uStartIndex = ((int)uIdx + iStartOffset) % uSize; + --uSize; + if(uStartIndex != uSize) + { + for(UINT i = uStartIndex; i < uSize; ++i) + { + pArr[i] = pArr[i + 1]; + } + } + if(uStartIndex < iStartOffset) + { + --iStartOffset; + } +} + +template<typename T> +static void ArrIns(const T &val, T *pArr, UINT &uSize, UINT uIdx, int &iStartOffset) +{ + UINT uStartIndex = ((int)uIdx + iStartOffset) % uSize; + if(uStartIndex == 0) + { + uStartIndex = uSize; + } + else + { + for(UINT i = uSize; i > uStartIndex; --i) + { + pArr[i] = pArr[i - 1]; + } + } + assert(uStartIndex < 10); + pArr[uStartIndex] = val; + ++uSize; + + if(uStartIndex < iStartOffset) + { + ++iStartOffset; + } +} + +template<typename T> +static void ArrSet(const T &val, T *pArr, UINT &uSize, UINT uIdx, int iStartOffset = 0) +{ + pArr[((int)uIdx + iStartOffset) % uSize] = val; +} + +static bool IsInside(const SMPLANE &plane, const float3_t &vPos) +{ + return(SMVector4Dot(plane, float4(vPos, 1.0f)) > 0.0f); +} + +void XMETHODCALLTYPE CDecal::FinalRelease() +{ + m_pProvider->onDecalReleased(this); +} + +void CDecal::spawnOverlayForModel(CDynamicModel *pModel, IXFrustum *pFrustum +#ifdef DECAL_DEBUG_DRAW + , IXGizmoRenderer *pDebugRenderer +#endif +) +{ + const IXResourceModelStatic *pResource = pModel->getResource()->asStatic(); + if(!pResource) + { + return; + } + + if(pResource->getPrimitiveTopology() != XPT_TRIANGLELIST) + { + LogError("CDecal::spawnOverlayForModel(): Unsupported primitive topology"); + } + + SMQuaternion qDecalToModel = m_qRot * pModel->getOrientation().Conjugate(); + float3 vS = qDecalToModel * float3(1.0f, 0.0f, 0.0f); + float3 vT = qDecalToModel * float3(0.0f, 1.0f, 0.0f); + float3 vN = qDecalToModel * float3(0.0f, 0.0f, 1.0f); + + float2_t sBound; + float2_t tBound; + // center point + float3 vModelSpaceDecalCenter = pModel->getOrientation().Conjugate() * (m_vPos - pModel->getPosition()); + + for(UINT j = 0; j < DECAL_POINTS; ++j) + { + float s = m_avCorners[j].x; + float t = m_avCorners[j].y; + if(j == 0) + { + sBound.x = s; + sBound.y = s; + tBound.x = t; + tBound.y = t; + } + else + { + if(sBound.x > s) + { + sBound.x = s; + } + if(sBound.y < s) + { + sBound.y = s; + } + if(tBound.x > t) + { + tBound.x = t; + } + if(tBound.y < t) + { + tBound.y = t; + } + } + } + float2_t vInvBoundRange = float2(1.0f, 1.0f) / float2(sBound.y - sBound.x, tBound.y - tBound.x); + XResourceModelStaticVertex vtx; + vtx.vTangent = vS; + vtx.vBinorm = vT; + vtx.vNorm = vN; + + float3_t aTempVertices[10]; // 9 - max vertex count of clipped triangle + 1 temp vertex + UINT uVertexCount; + + Array<XResourceModelStaticVertex> aOverlayVertices; + + float fCosThreshold = m_isLeakAllowed ? -0.1f : 0.2f; + + UINT uSubsets = pResource->getSubsetCount(0); + for(UINT uSubset = 0; uSubset < uSubsets; ++uSubset) + { + TODO("Separate transparent subsets"); + const XResourceModelStaticSubset *pSubset = pResource->getSubset(0, uSubset); + for(UINT i = 0; i < pSubset->iIndexCount; i += 3) + { + float3_t &a = pSubset->pVertices[pSubset->pIndices[i]].vPos; + float3_t &b = pSubset->pVertices[pSubset->pIndices[i + 1]].vPos; + float3_t &c = pSubset->pVertices[pSubset->pIndices[i + 2]].vPos; + + float3 vTriN = SMVector3Normalize(SMVector3Cross(b - a, c - a)); + float fCos = SMVector3Dot(vN, vTriN); + + if(fCos > fCosThreshold && pFrustum->polyInFrustum(a, b, c)) + { + aTempVertices[0] = a; + aTempVertices[1] = b; + aTempVertices[2] = c; + uVertexCount = 3; + + //pDebugRenderer->drawPoly(a, b, c); + + // clip by frustum planes + for(UINT j = 0, jl = pFrustum->getPlaneCount(); j < jl && uVertexCount >= 3/* && j < 4*/; ++j) + { + const SMPLANE &plane = pFrustum->getPlaneAt(j); + int iInsideVertex = -1; + // - find inside vertex + for(UINT k = 0; k < uVertexCount; ++k) + { + if(IsInside(plane, aTempVertices[k])) + { + iInsideVertex = (int)k; + break; + } + } + + if(iInsideVertex < 0) + { + // all vertices outside clipping plane + uVertexCount = 0; + break; + } + // - rotate array to make inside vertex first + // - clip + + for(UINT k = 0; k < uVertexCount; ++k) + { + float3_t vCur = ArrGet(aTempVertices, uVertexCount, k, iInsideVertex); + float3_t vNext = ArrGet(aTempVertices, uVertexCount, k + 1, iInsideVertex); + bool bCurInside = IsInside(plane, vCur); + bool bNextInside = IsInside(plane, vNext); + if(bCurInside) + { + if(bNextInside) + { + continue; + } + else + { + // clip + float3 vPt; + bool b = plane.intersectLine(&vPt, vCur, vNext); + assert(b); + // insert after cur + ArrIns((float3_t)vPt, aTempVertices, uVertexCount, k + 1, iInsideVertex); + ++k; + } + } + else + { + if(bNextInside) + { + // clip + float3 vPt; + bool b = plane.intersectLine(&vPt, vCur, vNext); + assert(b); + // replace cur + ArrSet((float3_t)vPt, aTempVertices, uVertexCount, k, iInsideVertex); + } + else + { + // drop cur + ArrDel(aTempVertices, uVertexCount, k, iInsideVertex); + --k; + } + } + } + } + + // put into overlay + if(uVertexCount >= 3) + { + if(pModel->isStatic()) + { + // transform to world pos + float3 vPos = pModel->getPosition(); + SMQuaternion qRot = pModel->getOrientation(); + for(UINT j = 0; j < uVertexCount; ++j) + { + aTempVertices[j] = qRot * aTempVertices[j] + vPos; + } + } + + UINT uStartVtx = aOverlayVertices.size(); + for(UINT begin = 0, end = uVertexCount - 1; begin + 1 <= end - 1; ++begin, --end) + { + vtx.vPos = aTempVertices[end]; + aOverlayVertices.push_back(vtx); + + vtx.vPos = aTempVertices[begin]; + aOverlayVertices.push_back(vtx); + + vtx.vPos = aTempVertices[end - 1]; + aOverlayVertices.push_back(vtx); + + vtx.vPos = aTempVertices[begin + 1]; + aOverlayVertices.push_back(vtx); + } + + float3 vTriS = vS; + float3 vTriT = vT; + + if(fCos < 0.2f) + { + float3 vRotAxis = SMVector3Cross(vTriN, vN); + + float fTempS = SMVector3Dot(vRotAxis, vS); + float fTempT = SMVector3Dot(vRotAxis, vT); + + float fSign = max(fTempS, fTempT); + float fAngle = safe_acosf(fCos); + if(fSign < 0.0f) + { + fAngle = SM_2PI - fAngle; + } + + SMQuaternion q(vRotAxis, fAngle); + vTriS = q * vS; + vTriT = q * vT; + } + + for(UINT j = uStartVtx, jl = aOverlayVertices.size(); j < jl; ++j) + { + XResourceModelStaticVertex &vt = aOverlayVertices[j]; + + float3 vPos = vt.vPos - vModelSpaceDecalCenter; + float s = SMVector3Dot(vPos, vTriS); + float t = SMVector3Dot(vPos, vTriT); + vt.vPos = vt.vPos + vTriN * 0.0001f; + + vt.vTex.x = lerpf(m_vTexRangeU.x, m_vTexRangeU.y, clampf((s - sBound.x) * vInvBoundRange.x, 0.0f, 1.0f)); + vt.vTex.y = lerpf(m_vTexRangeV.x, m_vTexRangeV.y, clampf((t - tBound.x) * vInvBoundRange.y, 0.0f, 1.0f)); + } + } + } + } + } + + // aOverlayVertices + CModelOverlay *pOverlay = m_pProvider->allocOverlay(this, m_pMaterial, aOverlayVertices, vN); + m_aOverlays.push_back({pModel, pOverlay}); + + CModelOverlay *pLastOverlay = pModel->getOverlay(); + if(!pLastOverlay) + { + pModel->setOverlay(pOverlay); + } + else + { + while(pLastOverlay->getNextOverlay()) + { + pLastOverlay = pLastOverlay->getNextOverlay(); + } + pLastOverlay->setNextOverlay(pOverlay); + } +} + +void CDecal::removeOverlays() +{ + fora(i, m_aOverlays) + { + Overlay &ovl = m_aOverlays[i]; + + CModelOverlay *pPrev = NULL; + CModelOverlay *pCur = ovl.pModel->getOverlay(); + while(pCur) + { + if(pCur == ovl.pOverlay) + { + if(pPrev) + { + pPrev->setNextOverlay(pCur->getNextOverlay()); + } + else + { + ovl.pModel->setOverlay(pCur->getNextOverlay()); + } + + break; + } + + pPrev = pCur; + pCur = pCur->getNextOverlay(); + } + + m_pProvider->freeOverlay(ovl.pOverlay); + } + + m_aOverlays.clearFast(); +} + +void CDecal::setDirty() +{ + if(!m_isDirty) + { + m_isDirty = true; + m_pProvider->updateDecal(this); + } +} + +void XMETHODCALLTYPE CDecal::setLeakAllowed(bool yesNo) +{ + if(!!yesNo != !!m_isLeakAllowed) + { + m_isLeakAllowed = yesNo; + setDirty(); + } +} diff --git a/source/anim/Decal.h b/source/anim/Decal.h new file mode 100644 index 0000000000000000000000000000000000000000..da5016ff7958b6f6d8c081341235125a3d3e55eb --- /dev/null +++ b/source/anim/Decal.h @@ -0,0 +1,110 @@ +#ifndef __DECAL_H +#define __DECAL_H + +#include <xcommon/resource/IXDecal.h> +#include <xcommon/IXScene.h> +#include <xcommon/render/IXRender.h> +#include "ModelOverlay.h" + +#define DECAL_POINTS 4 + +// #define DECAL_DEBUG_DRAW + +class CDynamicModel; +class CDecalProvider; + +class CDecal final: public IXUnknownImplementation<IXDecal> +{ +public: + CDecal(IXSceneQuery *pSceneQuery, IXRender *pRender, CDecalProvider *pProvider); + ~CDecal(); + + bool XMETHODCALLTYPE isEnabled() const override; + void XMETHODCALLTYPE enable(bool yesNo) override; + + float3 XMETHODCALLTYPE getPosition() const override; + void XMETHODCALLTYPE setPosition(const float3 &vPos) override; + + SMQuaternion XMETHODCALLTYPE getOrientation() const override; + void XMETHODCALLTYPE setOrientation(const SMQuaternion &qRot) override; + + float3 XMETHODCALLTYPE getLocalBoundMin() const override; + float3 XMETHODCALLTYPE getLocalBoundMax() const override; + SMAABB XMETHODCALLTYPE getLocalBound() const override; + + bool XMETHODCALLTYPE rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut = NULL, float3 *pvNormal = NULL) override; + + void XMETHODCALLTYPE setLayer(UINT uLayer) override; + UINT XMETHODCALLTYPE getLayer() override; + + void XMETHODCALLTYPE setHeight(float fHeight) override; + float XMETHODCALLTYPE getHeight() override; + + void XMETHODCALLTYPE setTextureRangeU(const float2_t &vRange) override; + float2_t XMETHODCALLTYPE getTextureRangeU() override; + + void XMETHODCALLTYPE setTextureRangeV(const float2_t &vRange) override; + float2_t XMETHODCALLTYPE getTextureRangeV() override; + + void XMETHODCALLTYPE setMaterial(const char *szMaterial) override; + + void setMaterial(IXMaterial *pMaterial); + void XMETHODCALLTYPE setCorner(UINT uCorner, const float2_t &vCorner) override; + + void XMETHODCALLTYPE setLeakAllowed(bool yesNo) override; + + void update( +#ifdef DECAL_DEBUG_DRAW + IXGizmoRenderer *pDebugRenderer +#endif + ); + + // Must be called only for overlays removed with model + void onOverlayRemoved(CModelOverlay *pOverlay); + +private: + IXSceneQuery *m_pSceneQuery = NULL; + IXRender *m_pRender = NULL; + CDecalProvider *m_pProvider = NULL; + + IXMaterial *m_pMaterial = NULL; + + float2_t m_avCorners[4]; + + SMQuaternion m_qRot; + float3_t m_vPos; + + float m_fHeight = 0.1f; + + UINT m_uLayer = 0; + + float2_t m_vTexRangeU = float2_t(0.0f, 1.0f); + float2_t m_vTexRangeV = float2_t(0.0f, 1.0f); + + struct Overlay + { + CDynamicModel *pModel; + CModelOverlay *pOverlay; + }; + Array<Overlay> m_aOverlays; + + bool m_isEnabled = true; + + bool m_isDirty = false; + + bool m_isLeakAllowed = true; + +private: + void XMETHODCALLTYPE FinalRelease() override; + void spawnOverlayForModel(CDynamicModel *pModel, IXFrustum *pFrustum +#ifdef DECAL_DEBUG_DRAW + , IXGizmoRenderer *pDebugRenderer +#endif + ); + + void removeOverlays(); + + void setDirty(); +}; + +#endif diff --git a/source/anim/DecalProvider.cpp b/source/anim/DecalProvider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1aaec25f9fe51e9aaccd002b98c58724e9f0e827 --- /dev/null +++ b/source/anim/DecalProvider.cpp @@ -0,0 +1,619 @@ +#include "DecalProvider.h" +//#include <xcommon/IPluginManager.h> +//#include <core/sxcore.h> +#include "ModelOverlay.h" +#include "DynamicModel.h" +#include "DynamicModelProvider.h" +#include <xcommon/IPluginManager.h> +#include "Decal.h" + +CDecalProvider::CDecalProvider(IXCore *pCore, CDynamicModelProvider *pProviderDynamic): + m_pCore(pCore), + m_pProviderDynamic(pProviderDynamic) +{ + m_pMaterialSystem = (IXMaterialSystem*)pCore->getPluginManager()->getInterface(IXMATERIALSYSTEM_GUID); + m_pRenderUtils = (IXRenderUtils*)pCore->getPluginManager()->getInterface(IXRENDERUTILS_GUID); + m_pRender = (IXRender*)pCore->getPluginManager()->getInterface(IXRENDER_GUID); + + IXScene *pScene = (IXScene*)pCore->getPluginManager()->getInterface(IXSCENE_GUID); + IXSceneObjectType *pType = pScene->getObjectType("xDynamic"); + m_pQuery = pType->newQuery(); + mem_release(pType); + + pCore->getConsole()->registerCVar("r_maxdecals", 300, "Max temp decals"); + //pCore->getConsole()->registerCVar("r_max_overlapped_decals", 4, "Max overlapped decals"); + +#ifdef DECAL_DEBUG_DRAW + m_pRenderUtils->newGizmoRenderer(&m_pDebugRenderer); +#endif + + m_pMaterialSystem->loadMaterial("dev_null", &m_pNullMaterial); + + loadDecals(); +} + +CDecalProvider::~CDecalProvider() +{ + mem_release(m_pNullMaterial); + fora(i, m_aTempDecals) + { + mem_release(m_aTempDecals[i]); + } + for(UINT i = 0; i < XDT__COUNT; ++i) + { + mem_release(m_aDecalTypes[i].pMaterial); + } + mem_release(m_pQuery); + mem_release(m_pBlendState); + mem_release(m_pDSState); + mem_release(m_pWorldBuffer); +#ifdef DECAL_DEBUG_DRAW + mem_release(m_pDebugRenderer); +#endif +} + +void CDecalProvider::render(CRenderableVisibility *pVisibility) +{ + XPROFILE_FUNCTION(); + + CRenderableVisibility::OverlayData &overlayData = pVisibility->getOverlayData(); + + UINT uVertexCount = overlayData.aVertices.size(); + + if(uVertexCount) + { + if(overlayData.uVertexBufferAllocSize < uVertexCount) + { + overlayData.uVertexBufferAllocSize = uVertexCount; + mem_release(overlayData.pRB); + mem_release(overlayData.pVB); + mem_release(overlayData.pIB); + + overlayData.pVB = m_pDevice->createVertexBuffer(sizeof(XResourceModelStaticVertexGPU) * uVertexCount, GXBUFFER_USAGE_STREAM); + overlayData.pRB = m_pDevice->createRenderBuffer(1, &overlayData.pVB, m_pProviderDynamic->getVertexDeclaration()); + m_pRenderUtils->getQuadIndexBuffer(uVertexCount / 4, &overlayData.pIB); + } + + XResourceModelStaticVertexGPU *pVertices; + if(overlayData.pVB->lock((void**)&pVertices, GXBL_WRITE)) + { + memcpy(pVertices, overlayData.aVertices, sizeof(XResourceModelStaticVertexGPU) * uVertexCount); + overlayData.pVB->unlock(); + } + + m_pProviderDynamic->bindVertexFormat(); + + IGXContext *pCtx = m_pDevice->getThreadContext(); + pCtx->setRenderBuffer(overlayData.pRB); + pCtx->setIndexBuffer(overlayData.pIB); + pCtx->setPrimitiveTopology(GXPT_TRIANGLELIST); + IGXBlendState *pOldBlendState = pCtx->getBlendState(); + pCtx->setBlendState(m_pBlendState); + m_pDevice->getThreadContext()->setVSConstant(m_pWorldBuffer, 1 /* SCR_OBJECT */); + IGXDepthStencilState *pOldDSState = pCtx->getDepthStencilState(); + pCtx->setDepthStencilState(m_pDSState); + + fora(i, overlayData.aSubsets) + { + auto &ss = overlayData.aSubsets[i]; + m_pMaterialSystem->bindMaterial(ss.pMaterial); + pCtx->drawIndexed(ss.uQuadCount * 4, ss.uQuadCount * 2, ss.uStartIndex); + } + + pCtx->setBlendState(pOldBlendState); + mem_release(pOldBlendState); + + pCtx->setDepthStencilState(pOldDSState); + mem_release(pOldDSState); + } + +#ifdef DECAL_DEBUG_DRAW + m_pDebugRenderer->render(false, false); +#endif +} + +bool XMETHODCALLTYPE CDecalProvider::newDecal(IXDecal **ppDecal) +{ + CDecal *pDecal = allocDecal(); + pDecal->setMaterial(m_pNullMaterial); + + *ppDecal = pDecal; + + return(true); +} + +void XMETHODCALLTYPE CDecalProvider::shootDecal(XDECAL_TYPE type, const float3 &vWorldPos, const float3 &vNormal, float fScale, const float3 *pvSAxis) +{ + //type = XDT_GLASS; + const DecalType *pType = getDecalType(type); + if(!pType || !pType->pMaterial) + { + return; + } + + fScale *= pType->fBaseScale * 0.0008f; + + IXTexture *pTex = pType->pMaterial->getTexture("txBase"); + if(!pTex) + { + IKeyIterator *pIter = pType->pMaterial->getTexturesIterator(); + if(pIter) + { + pTex = pType->pMaterial->getTexture(pIter->getCurrent()); + } + mem_release(pIter); + } + + UINT uTexWidth = pTex->getWidth(); + UINT uTexHeight = pTex->getHeight(); + + DecalTexRange texRange = {0, 0, uTexWidth, uTexHeight}; + if(pType->aTextures.size()) + { + texRange = pType->aTextures[rand() % pType->aTextures.size()]; + } + + float2_t vSize((float)(texRange.xmax - texRange.xmin) * fScale, (float)(texRange.ymax - texRange.ymin) * fScale); + + float2_t sBound(-vSize.x * 0.5f, vSize.x * 0.5f); + float2_t tBound(-vSize.y * 0.5f, vSize.y * 0.5f); + + //compute basis + SMMATRIX mBasis; + computeBasis(&mBasis, SMVector3Normalize(vNormal), pvSAxis); + + SMQuaternion qOrient = SMQuaternion(mBasis.r[0], mBasis.r[1], mBasis.r[2]).Normalize(); + + //vWorldPos; + //qOrient; + //pType->pMaterial; + float2_t avCorners[] = { + float2_t(sBound.x, tBound.x), + float2_t(sBound.x, tBound.y), + float2_t(sBound.y, tBound.y), + float2_t(sBound.y, tBound.x), + }; + float fHeight = min(vSize.x, vSize.y) * 0.5f + 0.1f; + +#ifdef DECAL_DEBUG_DRAW + m_pDebugRenderer->setPointSize(0.01f); + m_pDebugRenderer->setLineWidth(0.01f); + m_pDebugRenderer->setColor(float4(1.0f, 0.0f, 0.0f, 1.0f)); + m_pDebugRenderer->jumpTo(vWorldPos); + m_pDebugRenderer->lineTo(vWorldPos + mBasis.r[0]); + m_pDebugRenderer->setColor(float4(0.0f, 1.0f, 0.0f, 1.0f)); + m_pDebugRenderer->jumpTo(vWorldPos); + m_pDebugRenderer->lineTo(vWorldPos + mBasis.r[1]); + m_pDebugRenderer->setColor(float4(0.0f, 0.0f, 1.0f, 1.0f)); + m_pDebugRenderer->jumpTo(vWorldPos); + m_pDebugRenderer->lineTo(vWorldPos + mBasis.r[2]); + + m_pDebugRenderer->setColor(float4(1.0f, 1.0f, 1.0f, 1.0f)); + /*for(UINT i = 0; i < ARRAYSIZE(avCorners); ++i) + { + m_pDebugRenderer->drawPoint(qOrient * float3(avCorners[i], -fHeight) + vWorldPos); + m_pDebugRenderer->drawPoint(qOrient * float3(avCorners[i], fHeight) + vWorldPos); + }*/ +#endif + + // create CDecal + CDecal *pDecal = allocDecal(); + pDecal->setMaterial(pType->pMaterial); + for(UINT i = 0; i < DECAL_POINTS; ++i) + { + pDecal->setCorner(i, avCorners[i]); + } + pDecal->setPosition(vWorldPos); + pDecal->setOrientation(qOrient); + pDecal->setHeight(fHeight); + pDecal->setTextureRangeU(float2((float)texRange.xmin, (float)texRange.xmax) / (float)uTexWidth); + pDecal->setTextureRangeV(float2((float)texRange.ymin, (float)texRange.ymax) / (float)uTexHeight); + + addTempDecal(pDecal); +} + +void CDecalProvider::computeVisibility(const float3 &vHintDir, CRenderableVisibility *pVisibility) +{ + Array<CDynamicModel*> &aRenderList = pVisibility->getRenderList(); + + struct TmpOverlay + { + CDynamicModel *pModel; + CModelOverlay *pOverlay; + }; + + Array<TmpOverlay> aOverlays; + + bool useHintDir = !SMIsZero(SMVector3Length2(vHintDir)); + + UINT uVertices = 0; + fora(i, aRenderList) + { + CDynamicModel *pMdl = aRenderList[i]; + CModelOverlay *pOverlay = pMdl->getOverlay(); + while(pOverlay) + { + // skip backface overlays + bool bRender = !useHintDir || pOverlay->getMaterial()->isTwoSided(); + if(!bRender) + { + float3 vNormal = pOverlay->getNormal(); + if(!pMdl->isStatic()) + { + vNormal = pMdl->getOrientation() * vNormal; + } + bRender = SMVector3Dot(vNormal, vHintDir) < 0.0f; + } + if(bRender) + { + aOverlays.push_back({pMdl, pOverlay}); + + uVertices += pOverlay->getVertices().size(); + } + + pOverlay = pOverlay->getNextOverlay(); + } + } + + CRenderableVisibility::OverlayData &overlayData = pVisibility->getOverlayData(); + + Array<XResourceModelStaticVertexGPU> &aVertices = overlayData.aVertices; + aVertices.clearFast(); + aVertices.reserve(uVertices); + Array<CRenderableVisibility::OverlaySubset>& aSubsets = overlayData.aSubsets; + aSubsets.clearFast(); + + aOverlays.quickSort([](const TmpOverlay &a, const TmpOverlay &b){ + return(a.pOverlay->getMaterial() < b.pOverlay->getMaterial()); + }); + + XResourceModelStaticVertex tempVertex; + XResourceModelStaticVertexGPU tempGpuVertex; + CRenderableVisibility::OverlaySubset curSubset = {}; + fora(i, aOverlays) + { + TmpOverlay &overlay = aOverlays[i]; + + if(curSubset.pMaterial != overlay.pOverlay->getMaterial()) + { + if(curSubset.uStartVertex != aVertices.size()) + { + curSubset.uQuadCount = (aVertices.size() - curSubset.uStartVertex) / 4; + aSubsets.push_back(curSubset); + } + curSubset.pMaterial = overlay.pOverlay->getMaterial(); + + curSubset.uStartVertex = aVertices.size(); + curSubset.uStartIndex = aVertices.size() / 4 * 6; + } + + const Array<XResourceModelStaticVertex>& aOverlayVertices = overlay.pOverlay->getVertices(); + bool bNeedTransform = !overlay.pModel->isStatic(); + + float3 vPos = overlay.pModel->getPosition(); + SMQuaternion qRot = overlay.pModel->getOrientation(); + + fora(j, aOverlayVertices) + { + tempVertex = aOverlayVertices[j]; + if(bNeedTransform) + { + // transform vertices + tempVertex.vPos = qRot * tempVertex.vPos + vPos; + tempVertex.vNorm = qRot * tempVertex.vNorm; + tempVertex.vTangent = qRot * tempVertex.vTangent; + tempVertex.vBinorm = qRot * tempVertex.vBinorm; + } + +#define TO_SHORT(v) ((short)((v) * 32767.0f)) + auto &dst = tempGpuVertex; + auto &src = tempVertex; + dst.vPos = src.vPos; + dst.vTex = src.vTex; + dst.vNorm[0] = TO_SHORT(src.vNorm.x); + dst.vNorm[1] = TO_SHORT(src.vNorm.y); + dst.vNorm[2] = TO_SHORT(src.vNorm.z); + dst.vTangent[0] = TO_SHORT(src.vTangent.x); + dst.vTangent[1] = TO_SHORT(src.vTangent.y); + dst.vTangent[2] = TO_SHORT(src.vTangent.z); + dst.vBinorm[0] = TO_SHORT(src.vBinorm.x); + dst.vBinorm[1] = TO_SHORT(src.vBinorm.y); + dst.vBinorm[2] = TO_SHORT(src.vBinorm.z); +#undef TO_SHORT + + aVertices.push_back(tempGpuVertex); + } + } + + if(curSubset.uStartVertex != aVertices.size()) + { + curSubset.uQuadCount = (aVertices.size() - curSubset.uStartVertex) / 4; + aSubsets.push_back(curSubset); + } +} + +void CDecalProvider::setDevice(IGXDevice *pDevice) +{ + m_pDevice = pDevice; + + GXBlendDesc blendDesc; + memset(&blendDesc, 0, sizeof(blendDesc)); + blendDesc.renderTarget[0].u8RenderTargetWriteMask = GXCOLOR_WRITE_ENABLE_RED | GXCOLOR_WRITE_ENABLE_GREEN | GXCOLOR_WRITE_ENABLE_BLUE; + blendDesc.renderTarget[0].blendSrcColor = GXBLEND_DEST_COLOR; + blendDesc.renderTarget[0].blendDestColor = GXBLEND_SRC_COLOR; + blendDesc.renderTarget[0].blendSrcAlpha = GXBLEND_SRC_ALPHA; + blendDesc.renderTarget[0].blendDestAlpha = GXBLEND_INV_SRC_ALPHA; + blendDesc.renderTarget[0].blendOpColor = GXBLEND_OP_ADD; + blendDesc.renderTarget[0].blendOpAlpha = GXBLEND_OP_ADD; + + /*blendDesc.renderTarget[0].blendSrcColor = GXBLEND_DEST_COLOR; + blendDesc.renderTarget[0].blendDestColor = GXBLEND_SRC_COLOR; + blendDesc.renderTarget[0].blendSrcAlpha = GXBLEND_DEST_ALPHA; + blendDesc.renderTarget[0].blendDestAlpha = GXBLEND_SRC_ALPHA; + blendDesc.renderTarget[0].blendOpColor = GXBLEND_OP_ADD; + blendDesc.renderTarget[0].blendOpAlpha = GXBLEND_OP_ADD;*/ + + blendDesc.renderTarget[0].useBlend = TRUE; + + blendDesc.renderTarget[1].blendSrcColor = GXBLEND_ZERO; + blendDesc.renderTarget[1].blendDestColor = GXBLEND_ONE; + + blendDesc.renderTarget[2].blendSrcColor = GXBLEND_ONE; + blendDesc.renderTarget[2].blendDestColor = GXBLEND_ZERO; + + blendDesc.renderTarget[3].blendSrcColor = GXBLEND_ONE; + blendDesc.renderTarget[3].blendDestColor = GXBLEND_ZERO; + + blendDesc.renderTarget[1].useBlend = FALSE; + blendDesc.renderTarget[2].useBlend = FALSE; + blendDesc.renderTarget[3].useBlend = FALSE; + + blendDesc.useIndependentBlend = TRUE; + + m_pBlendState = pDevice->createBlendState(&blendDesc); + + + GXDepthStencilDesc dsDesc = {}; + dsDesc.cmpFuncDepth = GXCMP_GREATER_EQUAL; + dsDesc.useDepthWrite = FALSE; + m_pDSState = pDevice->createDepthStencilState(&dsDesc); + + + m_pWorldBuffer = pDevice->createConstantBuffer(sizeof(SMMATRIX)); + m_pWorldBuffer->update(&SMMatrixIdentity()); +} + +void CDecalProvider::onDecalReleased(CDecal *pDecal) +{ + ScopedSpinLock lock(m_slMemDecals); + m_memDecals.Delete(pDecal); +} + +void CDecalProvider::onDecalEmptied(CDecal *pDecal) +{ + int idx = -1; + { + ScopedSpinLock lock(m_slTempDecals); + + idx = m_aTempDecals.indexOf(pDecal); + if(idx >= 0) + { + m_aTempDecals.erase(idx); + } + } + + if(idx >= 0) + { + mem_release(pDecal); + } +} + +void CDecalProvider::updateDecal(CDecal *pDecal) +{ + add_ref(pDecal); + m_qDecalsForUpdate.push(pDecal); +} + +void CDecalProvider::update() +{ + if(!m_isEnabled) + { + return; + } + + CDecal *pDecal; + while(m_qDecalsForUpdate.pop(&pDecal)) + { + pDecal->update( +#ifdef DECAL_DEBUG_DRAW + m_pDebugRenderer +#endif + ); + mem_release(pDecal); + } +} + +CDecal* CDecalProvider::allocDecal() +{ + ScopedSpinLock lock(m_slMemDecals); + return(m_memDecals.Alloc(m_pQuery, m_pRender, this)); +} + +CModelOverlay* CDecalProvider::allocOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal) +{ + ScopedSpinLock lock(m_slMemOverlays); + return(m_memOverlays.Alloc(pDecal, pMaterial, aVertices, vNormal)); +} +void CDecalProvider::freeOverlay(CModelOverlay *pOverlay) +{ + ScopedSpinLock lock(m_slMemOverlays); + m_memOverlays.Delete(pOverlay); +} + +IXMaterialSystem* CDecalProvider::getMaterialSystem() +{ + return(m_pMaterialSystem); +} + +void CDecalProvider::setEnabled(bool yesNo) +{ + m_isEnabled = yesNo; +} + +void CDecalProvider::loadDecals() +{ + IXConfig *pConfig = m_pCore->newConfig(); + if(pConfig->open("config/decals/decals.cfg")) + { + UINT uSections = pConfig->getSectionCount(); + for(UINT i = 0; i < uSections; ++i) + { + const char *szSection = pConfig->getSectionName(i); + + int id; + if(pConfig->keyExists(szSection, "id") && sscanf(pConfig->getKey(szSection, "id"), "%d", &id)) + { + if(id < 0 || id >= XDT__COUNT) + { + LogError("Incorrect decal type id '%s'\n", szSection); + continue; + } + } + else + { + LogError("Unable to read decal id '%s'\n", szSection); + continue; + } + + const char *szTex = pConfig->getKey(szSection, "tex"); + if(!szTex) + { + LogError("Unable to read decal tex '%s'\n", szSection); + continue; + } + + const char *szScale = pConfig->getKey(szSection, "base_scale"); + if(szScale) + { + sscanf(szScale, "%f", &m_aDecalTypes[id].fBaseScale); + } + + + int j = 0; + + DecalTexRange rng; + char key[64]; + while(sprintf(key, "tex%d", j) && pConfig->keyExists(szSection, key)) + { + if(sscanf(pConfig->getKey(szSection, key), "[%d,%d,%d,%d]", &rng.xmin, &rng.ymin, &rng.xmax, &rng.ymax) != 4) + { + LogError("Unable to read decal tex coords \"%s\" \"%s\"\n", pConfig->getKey(szSection, key), szSection); + } + else + { + m_aDecalTypes[id].aTextures.push_back(rng); + } + j++; + } + + m_pMaterialSystem->loadMaterial(szTex, &m_aDecalTypes[id].pMaterial); + } + } + + mem_release(pConfig); +} + +const CDecalProvider::DecalType* CDecalProvider::getDecalType(XDECAL_TYPE type) +{ + if(type < 0 || type >= XDT__COUNT) + { + LogError("Incorrect decal type %d\n", type); + return(NULL); + } + return(&m_aDecalTypes[type]); +} + +void CDecalProvider::computeBasis(SMMATRIX *pmOut, const float3_t &vSurfaceNormal, const float3 *pvSAxis) +{ + // s, t, textureSpaceNormal (S cross T = textureSpaceNormal(N)) + // + // +---->S + // /| + // / | + // N |T + // + // S = textureSpaceBasis[0] + // T = textureSpaceBasis[1] + // N = textureSpaceBasis[2] + + // Get the surface normal. + pmOut->r[2] = vSurfaceNormal; + + if(pvSAxis) + { + // T = N cross S + pmOut->r[1] = SMVector3Cross(pmOut->r[2], *pvSAxis); + + // Name sure they aren't parallel or antiparallel + // In that case, fall back to the normal algorithm. + if(SMVector3Dot(pmOut->r[1], pmOut->r[1]) > 1e-6) + { + // S = T cross N + pmOut->r[0] = SMVector3Cross(pmOut->r[1], pmOut->r[2]); + + pmOut->r[0] = SMVector3Normalize(pmOut->r[0]); + pmOut->r[1] = SMVector3Normalize(pmOut->r[1]); + return; + } + + // Fall through to the standard algorithm for parallel or antiparallel + } + + // floor/ceiling? + if(fabs(vSurfaceNormal.y) > SIN_45_DEGREES) + { + pmOut->r[0] = float4(1.0f, 0.0f, 0.0f, 0.0f); + + // T = N cross S + pmOut->r[1] = SMVector3Cross(pmOut->r[2], pmOut->r[0]); + + // S = T cross N + pmOut->r[0] = SMVector3Cross(pmOut->r[1], pmOut->r[2]); + } + // wall + else + { + pmOut->r[1] = float4(0.0f, -1.0f, 0.0f, 0.0f); + + // S = T cross N + pmOut->r[0] = SMVector3Cross(pmOut->r[1], pmOut->r[2]); + // T = N cross S + pmOut->r[1] = SMVector3Cross(pmOut->r[2], pmOut->r[0]); + } + + pmOut->r[0] = SMVector3Normalize(pmOut->r[0]); + pmOut->r[1] = SMVector3Normalize(pmOut->r[1]); +} + +void CDecalProvider::addTempDecal(CDecal *pDecal) +{ + ScopedSpinLock lock(m_slTempDecals); + + static const int *r_maxdecals = m_pCore->getConsole()->getPCVarInt("r_maxdecals"); + if(m_aTempDecals.size() > *r_maxdecals) + { + for(UINT i = (UINT)*r_maxdecals, l = m_aTempDecals.size(); i < l; ++i) + { + mem_release(m_aTempDecals[i]); + } + } + m_aTempDecals.reserve(*r_maxdecals); + + TODO("Use ring array for that"); + if(m_aTempDecals.size() == *r_maxdecals) + { + mem_release(m_aTempDecals[0]); + m_aTempDecals.erase(0); + } + m_aTempDecals.push_back(pDecal); +} diff --git a/source/anim/DecalProvider.h b/source/anim/DecalProvider.h new file mode 100644 index 0000000000000000000000000000000000000000..58580748fa70f2bdb7161c697fee0b133a41d625 --- /dev/null +++ b/source/anim/DecalProvider.h @@ -0,0 +1,114 @@ +#ifndef __DECALPROVIDER_H +#define __DECALPROVIDER_H + +#include <xcommon/resource/IXDecalProvider.h> +#include <xcommon/IXCore.h> +#include "RenderableVisibility.h" +//#include <mtrl/IXMaterialSystem.h> +//#include <common/ConcurrentQueue.h> +//#include <xcommon/XEvents.h> +#include <xcommon/IXScene.h> +#include <common/queue.h> +#include "Decal.h" + +#define SIN_45_DEGREES 0.70710678118654752440084436210485f + +class CDecalProvider: public IXUnknownImplementation<IXDecalProvider> +{ +public: + CDecalProvider(IXCore *pCore, CDynamicModelProvider *pProviderDynamic); + ~CDecalProvider(); + + void render(CRenderableVisibility *pVisibility); + + bool XMETHODCALLTYPE newDecal(IXDecal **ppDecal) override; + + void XMETHODCALLTYPE shootDecal(XDECAL_TYPE type, const float3 &vWorldPos, const float3 &vNormal, float fScale = 1.0f, const float3 *pvSAxis = NULL) override; + + void computeVisibility(const float3 &vHintDir, CRenderableVisibility *pVisibility); + + void setDevice(IGXDevice *pDevice); + + void onDecalReleased(CDecal *pDecal); + + void onDecalEmptied(CDecal *pDecal); + + void updateDecal(CDecal *pDecal); + + void update(); + + CModelOverlay* allocOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal); + void freeOverlay(CModelOverlay *pOverlay); + + IXMaterialSystem* getMaterialSystem(); + + void setEnabled(bool yesNo); + +private: + struct DecalTexRange + { + int xmin; + int ymin; + int xmax; + int ymax; + }; + + struct DecalType + { + Array<DecalTexRange> aTextures; + float fBaseScale = 1.0f; + IXMaterial *pMaterial = NULL; + }; + + /*struct TempDecal + { + CDecal *pDecal; + };*/ + +private: + IXCore *m_pCore = NULL; + CDynamicModelProvider *m_pProviderDynamic = NULL; + IGXDevice *m_pDevice = NULL; + IXMaterialSystem *m_pMaterialSystem = NULL; + IXRender *m_pRender = NULL; + IXRenderUtils *m_pRenderUtils = NULL; + IXSceneQuery *m_pQuery = NULL; + + IXMaterial *m_pNullMaterial = NULL; + + IGXConstantBuffer *m_pWorldBuffer = NULL; + + IGXBlendState *m_pBlendState = NULL; + IGXDepthStencilState *m_pDSState = NULL; + + DecalType m_aDecalTypes[XDT__COUNT]; + +#ifdef DECAL_DEBUG_DRAW + IXGizmoRenderer *m_pDebugRenderer = NULL; +#endif + + MemAlloc<CDecal> m_memDecals; + MemAlloc<CModelOverlay> m_memOverlays; + SpinLock m_slMemDecals; + SpinLock m_slMemOverlays; + + Array<CDecal*> m_aTempDecals; + SpinLock m_slTempDecals; + + Queue<CDecal*> m_qDecalsForUpdate; + + bool m_isEnabled = true; + +private: + void loadDecals(); + + const DecalType* getDecalType(XDECAL_TYPE type); + + void computeBasis(SMMATRIX *pmOut, const float3_t &vSurfaceNormal, const float3 *pvSAxis = NULL); + + CDecal* allocDecal(); + + void addTempDecal(CDecal *pDecal); +}; + +#endif diff --git a/source/anim/DynamicModel.cpp b/source/anim/DynamicModel.cpp index 0a79dbe82ff68b47fe0a024e1a8761ada11a8a6d..85ad2e562d43aec535b0cc217b183e07db86198f 100644 --- a/source/anim/DynamicModel.cpp +++ b/source/anim/DynamicModel.cpp @@ -31,6 +31,8 @@ CDynamicModel::~CDynamicModel() mem_release(m_pWorldBuffer); mem_release(m_pColorBuffer); mem_release(m_pSceneObject); + + SAFE_CALL(m_pOverlay, onModelRemoved); } void CDynamicModel::initGPUresources() @@ -80,7 +82,7 @@ IXDynamicModel * XMETHODCALLTYPE CDynamicModel::asDynamicModel() } IXStaticModel * XMETHODCALLTYPE CDynamicModel::asStaticModel() { - return(NULL); + return(isStatic() ? this : NULL); } float3 XMETHODCALLTYPE CDynamicModel::getPosition() const @@ -209,11 +211,11 @@ void CDynamicModel::_updateAABB() const m_vLocalMin = (float3)SMVectorMin( SMVectorMin(SMVectorMin(vCurrent[0], vCurrent[1]), SMVectorMin(vCurrent[2], vCurrent[3])), SMVectorMin(SMVectorMin(vCurrent[4], vCurrent[5]), SMVectorMin(vCurrent[6], vCurrent[7])) - ); + ); m_vLocalMax = (float3)SMVectorMax( SMVectorMax(SMVectorMax(vCurrent[0], vCurrent[1]), SMVectorMax(vCurrent[2], vCurrent[3])), SMVectorMax(SMVectorMax(vCurrent[4], vCurrent[5]), SMVectorMax(vCurrent[6], vCurrent[7])) - ); + ); m_isLocalAABBvalid = true; } @@ -466,3 +468,23 @@ UINT XMETHODCALLTYPE CDynamicModel::getLayer() { return(m_uLayer); } + +CModelOverlay* CDynamicModel::getOverlay() +{ + return(m_pOverlay); +} + +void CDynamicModel::setOverlay(CModelOverlay *pOverlay) +{ + m_pOverlay = pOverlay; +} + +void CDynamicModel::setStatic(bool yesNo) +{ + m_isStatic = yesNo; +} + +bool CDynamicModel::isStatic() +{ + return(m_isStatic); +} diff --git a/source/anim/DynamicModel.h b/source/anim/DynamicModel.h index 836ebdda06dc00c7aafa6efa8f93dd7fe363cd42..97612f2543b3fec1422acfc786f337b60bd9aeea 100644 --- a/source/anim/DynamicModel.h +++ b/source/anim/DynamicModel.h @@ -4,6 +4,7 @@ #include <xcommon/resource/IXModel.h> #include <xcommon/IXScene.h> #include "DynamicModelShared.h" +#include "ModelOverlay.h" class CDynamicModel final: public IXUnknownImplementation<IXDynamicModel> { @@ -58,7 +59,14 @@ public: void XMETHODCALLTYPE setLayer(UINT uLayer) override; UINT XMETHODCALLTYPE getLayer() override; -protected: + + CModelOverlay* getOverlay(); + void setOverlay(CModelOverlay *pOverlay); + + void setStatic(bool yesNo); + bool isStatic(); + +private: CDynamicModelProvider *m_pProvider; CDynamicModelShared *m_pShared; IGXDevice *m_pDevice; @@ -75,17 +83,21 @@ protected: UINT m_uSkin = 0; float4_t m_vColor{1.0f, 1.0f, 1.0f, 1.0f}; bool m_isEnabled = true; + bool m_isStatic = false; float m_fScale = 1.0f; mutable bool m_isLocalAABBvalid = false; mutable float3_t m_vLocalMin; mutable float3_t m_vLocalMax; + UINT m_uLayer = 0; + + CModelOverlay *m_pOverlay = NULL; + +private: void _updateAABB() const; void XMETHODCALLTYPE FinalRelease() override; - - UINT m_uLayer = 0; }; #endif diff --git a/source/anim/DynamicModelProvider.cpp b/source/anim/DynamicModelProvider.cpp index 4ce0af11d6c8855f0963e177aa8165be14fbcbe7..36af3b00e4a7f2d4c05454888f9b305f7f49e4cf 100644 --- a/source/anim/DynamicModelProvider.cpp +++ b/source/anim/DynamicModelProvider.cpp @@ -541,6 +541,8 @@ void CDynamicModelProvider::computeVisibility(const IXFrustum *pFrustum, const f { XPROFILE_FUNCTION(); + TODO("Make some hints to not to compute stages is not required for render. eg selfillum stage for shadows"); + if(pCamera && pCamera->getProjectionMode() == XCPM_PERSPECTIVE) { FIXME("Use actual target width!"); @@ -806,3 +808,8 @@ void CDynamicModelProvider::enqueueModelDelete(CDynamicModel* pModel) { m_qModelDelete.push(pModel); } + +bool CDynamicModelProvider::hasPendingOps() +{ + return(!m_queueGPUinitModel.empty() || !m_queueGPUinitShared.empty()); +} diff --git a/source/anim/DynamicModelProvider.h b/source/anim/DynamicModelProvider.h index 3b126cae9b99e10c9e4de2f924fea8fa1b8af757..ce66cf3ea02d6d24e9fea5c88a0ffa2df4b4b933 100644 --- a/source/anim/DynamicModelProvider.h +++ b/source/anim/DynamicModelProvider.h @@ -70,6 +70,9 @@ public: IXSceneFeature* getFeature(XMODEL_FEATURE bmFeature); void enqueueModelDelete(CDynamicModel* pModel); + + bool hasPendingOps(); + protected: void onMaterialEmissivityChanged(const IXMaterial *pMaterial); void onMaterialTransparencyChanged(const IXMaterial *pMaterial); diff --git a/source/anim/ModelOverlay.cpp b/source/anim/ModelOverlay.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ead2048997a2aea32fff6996aa5b413880429bf5 --- /dev/null +++ b/source/anim/ModelOverlay.cpp @@ -0,0 +1,49 @@ +#include "ModelOverlay.h" +#include "Decal.h" + +CModelOverlay::CModelOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal): + m_pDecal(pDecal), + m_pMaterial(pMaterial), + m_vNormal(vNormal) +{ + m_aVertices.swap(aVertices); + add_ref(m_pMaterial); +} + +CModelOverlay::~CModelOverlay() +{ + mem_release(m_pMaterial); +} + + +const Array<XResourceModelStaticVertex>& CModelOverlay::getVertices() +{ + return(m_aVertices); +} + +CModelOverlay* CModelOverlay::getNextOverlay() +{ + return(m_pNextOverlay); +} +void CModelOverlay::setNextOverlay(CModelOverlay *pOverlay) +{ + m_pNextOverlay = pOverlay; +} + +IXMaterial* CModelOverlay::getMaterial() +{ + return(m_pMaterial); +} + +const float3_t& CModelOverlay::getNormal() +{ + return(m_vNormal); +} + +void CModelOverlay::onModelRemoved() +{ + SAFE_CALL(m_pNextOverlay, onModelRemoved); + + //notify decal + m_pDecal->onOverlayRemoved(this); +} diff --git a/source/anim/ModelOverlay.h b/source/anim/ModelOverlay.h new file mode 100644 index 0000000000000000000000000000000000000000..4404a20c1a65f57d4472ef95f4bb4bf5efea15ef --- /dev/null +++ b/source/anim/ModelOverlay.h @@ -0,0 +1,43 @@ +#ifndef __MODELOVERLAY_H +#define __MODELOVERLAY_H + +#include <xcommon/resource/IXModel.h> +#include <graphix/graphix.h> +#include <mtrl/IXMaterial.h> + +class IXMaterialSystem; +class CDecal; + +class CModelOverlay final +{ +public: + CModelOverlay(CDecal *pDecal, IXMaterial *pMaterial, Array<XResourceModelStaticVertex> &aVertices, const float3_t &vNormal); + ~CModelOverlay(); + + const Array<XResourceModelStaticVertex>& getVertices(); + + CModelOverlay* getNextOverlay(); + void setNextOverlay(CModelOverlay *pOverlay); + + IXMaterial* getMaterial(); + + const float3_t& getNormal(); + + void onModelRemoved(); + +private: + // Model's overlays stored as linked list + CModelOverlay *m_pNextOverlay = NULL; + CDecal *m_pDecal; + + TODO("Use memory pool"); + Array<XResourceModelStaticVertex> m_aVertices; + + IXMaterial *m_pMaterial = NULL; + + float3_t m_vNormal; + + bool m_isTransparent = false; +}; + +#endif diff --git a/source/anim/Renderable.cpp b/source/anim/Renderable.cpp index 81ac9ab42dbb1d20f0597036e2df28da16bcfc76..52ad958bccb76178cbb80d6718cf70d4f22c2fed 100644 --- a/source/anim/Renderable.cpp +++ b/source/anim/Renderable.cpp @@ -1,10 +1,12 @@ #include "Renderable.h" #include "RenderableVisibility.h" +#include "DecalProvider.h" -CRenderable::CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic): +CRenderable::CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal): m_idPlugin(idPlugin), m_pAnimatedModelProvider(pProviderAnimated), - m_pDynamicModelProvider(pProviderDynamic) + m_pDynamicModelProvider(pProviderDynamic), + m_pDecalProvider(pProviderDecal) { } @@ -40,6 +42,7 @@ void XMETHODCALLTYPE CRenderable::renderStage(X_RENDER_STAGE stage, IXRenderable case XRS_GBUFFER: m_pAnimatedModelProvider->render(pVis); m_pDynamicModelProvider->render(false, pVis); + m_pDecalProvider->render(pVis); break; case XRS_SHADOWS: m_pAnimatedModelProvider->render(pVis); @@ -89,6 +92,7 @@ void XMETHODCALLTYPE CRenderable::startup(IXRender *pRender, IXMaterialSystem *p m_pAnimatedModelProvider->setDevice(m_pDevice); m_pDynamicModelProvider->setDevice(m_pDevice); + m_pDecalProvider->setDevice(m_pDevice); } void XMETHODCALLTYPE CRenderable::shutdown() { @@ -96,7 +100,7 @@ void XMETHODCALLTYPE CRenderable::shutdown() void XMETHODCALLTYPE CRenderable::newVisData(IXRenderableVisibility **ppVisibility) { - *ppVisibility = new CRenderableVisibility(m_idPlugin, m_pAnimatedModelProvider, m_pDynamicModelProvider); + *ppVisibility = new CRenderableVisibility(m_idPlugin, m_pAnimatedModelProvider, m_pDynamicModelProvider, m_pDecalProvider); } IXMaterialSystem* CRenderable::getMaterialSystem() diff --git a/source/anim/Renderable.h b/source/anim/Renderable.h index cafcc2fbfc260553f1c8f3cf5f857b0b7919a2e8..85f03cc5139e0e8d2d3a4b34f849466849376c73 100644 --- a/source/anim/Renderable.h +++ b/source/anim/Renderable.h @@ -9,7 +9,7 @@ class CRenderable: public IXUnknownImplementation<IXRenderable> { public: - CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic); + CRenderable(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal); XIMPLEMENT_VERSION(IXRENDERABLE_VERSION); @@ -56,6 +56,7 @@ protected: CAnimatedModelProvider *m_pAnimatedModelProvider = NULL; CDynamicModelProvider *m_pDynamicModelProvider = NULL; + CDecalProvider *m_pDecalProvider = NULL; }; #endif diff --git a/source/anim/RenderableVisibility.cpp b/source/anim/RenderableVisibility.cpp index d37373b47f114ba2fe3b7f3101020a0965ec53a3..f012c9831787a31e0f85b2e09e058b7df230044b 100644 --- a/source/anim/RenderableVisibility.cpp +++ b/source/anim/RenderableVisibility.cpp @@ -1,11 +1,13 @@ #include "RenderableVisibility.h" #include "AnimatedModelProvider.h" #include "DynamicModelProvider.h" +#include "DecalProvider.h" -CRenderableVisibility::CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic): +CRenderableVisibility::CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal): m_idPlugin(idPlugin), m_pProviderAnimated(pProviderAnimated), - m_pProviderDynamic(pProviderDynamic) + m_pProviderDynamic(pProviderDynamic), + m_pProviderDecal(pProviderDecal) { } @@ -38,6 +40,7 @@ void CRenderableVisibility::updateForCamera(IXCamera *pCamera, const IXRenderabl IXFrustum *pFrustum = pCamera->getFrustum(); m_pProviderAnimated->computeVisibility(pFrustum, this, pCamera->getLayerMask(), pRef); m_pProviderDynamic->computeVisibility(pFrustum, pCamera->getLook(), this, pCamera->getLayerMask(), pRef, pCamera); + m_pProviderDecal->computeVisibility(pCamera->getLook(), this); mem_release(pFrustum); } @@ -52,6 +55,7 @@ void CRenderableVisibility::updateForFrustum(const IXFrustum *pFrustum, UINT bmL m_pProviderAnimated->computeVisibility(pFrustum, this, bmLayers, pRef); m_pProviderDynamic->computeVisibility(pFrustum, float3(), this, bmLayers, pRef); + m_pProviderDecal->computeVisibility(float3(), this); } static void SortRenderList(Array<CDynamicModel*> &aList) @@ -139,7 +143,8 @@ void CRenderableVisibility::append(const IXRenderableVisibility *pOther_) MergeArrays(m_aSelfillumList, pOther->m_aSelfillumList); SortRenderList(m_aSelfillumList); - //! @todo implement for transparency too! + TODO("Implement for transparency too!"); + TODO("Implement for decals too!"); } void CRenderableVisibility::substract(const IXRenderableVisibility *pOther_) @@ -159,7 +164,8 @@ void CRenderableVisibility::substract(const IXRenderableVisibility *pOther_) } } - //! @todo implement for transparency too! + TODO("Implement for transparency too!"); + TODO("Implement for decals too!"); } void CRenderableVisibility::setItemCount(UINT uCount) @@ -257,3 +263,8 @@ Array<CDynamicModel*>& CRenderableVisibility::getTransparentList() { return(m_aTransparentList); } + +CRenderableVisibility::OverlayData& CRenderableVisibility::getOverlayData() +{ + return(m_overlayData); +} diff --git a/source/anim/RenderableVisibility.h b/source/anim/RenderableVisibility.h index 6d96b4998ce3e48771a923a55da214e68c183b2e..8032e469b6fd1bdb59f61f1b95513719956feac8 100644 --- a/source/anim/RenderableVisibility.h +++ b/source/anim/RenderableVisibility.h @@ -2,14 +2,16 @@ #define __RENDERABLE_VISIBILITY_H #include <xcommon/IXRenderable.h> +#include <xcommon/resource/IXResourceModel.h> class CAnimatedModelProvider; class CDynamicModelProvider; +class CDecalProvider; class CDynamicModel; class CRenderableVisibility final: public IXUnknownImplementation<IXRenderableVisibility> { public: - CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *m_pProviderAnimated, CDynamicModelProvider *m_pProviderDynamic); + CRenderableVisibility(ID idPlugin, CAnimatedModelProvider *pProviderAnimated, CDynamicModelProvider *pProviderDynamic, CDecalProvider *pProviderDecal); ~CRenderableVisibility(); ID getPluginId() const override; @@ -40,6 +42,24 @@ public: UINT uLod; IXMaterial *pMaterial; }; + + struct OverlaySubset + { + IXMaterial *pMaterial; + UINT uStartVertex; + UINT uStartIndex; + UINT uQuadCount; + }; + + struct OverlayData + { + Array<XResourceModelStaticVertexGPU> aVertices; + Array<OverlaySubset> aSubsets; + IGXVertexBuffer *pVB = NULL; + IGXRenderBuffer *pRB = NULL; + IGXIndexBuffer *pIB = NULL; + UINT uVertexBufferAllocSize = 0; + }; void setItemCount(UINT uCount); item_s* getItem(UINT uIndex); @@ -57,16 +77,18 @@ public: Array<CDynamicModel*>& getRenderList(); Array<CDynamicModel*>& getTransparentList(); Array<CDynamicModel*>& getSelfillumList(); + OverlayData& getOverlayData(); IXOcclusionCuller* getCuller() { return(m_pOcclusionCuller); } -protected: +private: ID m_idPlugin; CAnimatedModelProvider *m_pProviderAnimated; CDynamicModelProvider *m_pProviderDynamic; + CDecalProvider *m_pProviderDecal; IXOcclusionCuller *m_pOcclusionCuller = NULL; Array<item_s> m_aItems; @@ -75,6 +97,8 @@ protected: Array<CDynamicModel*> m_aRenderList; Array<CDynamicModel*> m_aTransparentList; Array<CDynamicModel*> m_aSelfillumList; + + OverlayData m_overlayData; }; #endif diff --git a/source/anim/StaticModelProvider.cpp b/source/anim/StaticModelProvider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..99cbb517abaa174b2d4e455bbe092bf52944648f --- /dev/null +++ b/source/anim/StaticModelProvider.cpp @@ -0,0 +1,20 @@ +#include "StaticModelProvider.h" + +CStaticModelProvider::CStaticModelProvider(CDynamicModelProvider *pDynamicModelProvider): + m_pDynamicModelProvider(pDynamicModelProvider) +{ +} + +bool XMETHODCALLTYPE CStaticModelProvider::createModel(IXResourceModelStatic *pResource, IXStaticModel **ppModel) +{ + IXDynamicModel *pModel; + if(m_pDynamicModelProvider->createModel(pResource, &pModel)) + { + ((CDynamicModel*)pModel)->setStatic(true); + + *ppModel = pModel; + return(true); + } + + return(false); +} diff --git a/source/anim/StaticModelProvider.h b/source/anim/StaticModelProvider.h new file mode 100644 index 0000000000000000000000000000000000000000..37fd0b31938fa825400c5cf9bb52e85f312e9d8d --- /dev/null +++ b/source/anim/StaticModelProvider.h @@ -0,0 +1,18 @@ +#ifndef __STATICMODELPROVIDER_H +#define __STATICMODELPROVIDER_H + +#include <xcommon/resource/IXModelProvider.h> +#include "DynamicModelProvider.h" + +class CStaticModelProvider final: public IXUnknownImplementation<IXStaticModelProvider> +{ +public: + CStaticModelProvider(CDynamicModelProvider *pDynamicModelProvider); + + bool XMETHODCALLTYPE createModel(IXResourceModelStatic *pResource, IXStaticModel **ppModel) override; + +private: + CDynamicModelProvider *m_pDynamicModelProvider; +}; + +#endif diff --git a/source/anim/Updatable.cpp b/source/anim/Updatable.cpp index d04c08cd13b194c8bac23e7493ff8084328d2471..2a48fd286fed63e6c019b14c508c3ed413305dd1 100644 --- a/source/anim/Updatable.cpp +++ b/source/anim/Updatable.cpp @@ -1,8 +1,9 @@ #include "Updatable.h" -CUpdatable::CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider): +CUpdatable::CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider, CDecalProvider *pDecalProvider): m_pAnimatedModelProvider(pAnimatedModelProvider), - m_pDynamicModelProvider(pDynamicModelProvider) + m_pDynamicModelProvider(pDynamicModelProvider), + m_pDecalProvider(pDecalProvider) { } @@ -23,6 +24,8 @@ ID CUpdatable::run(float fDelta) m_pDynamicModelProvider->update(); + m_pDecalProvider->update(); + return(-1); } @@ -30,4 +33,5 @@ void CUpdatable::sync() { m_pAnimatedModelProvider->sync(); m_pDynamicModelProvider->sync(); + //m_pDecalProvider->update(); } diff --git a/source/anim/Updatable.h b/source/anim/Updatable.h index 7123026acff06ca4a1edf9f3efeabeeec7ecc1e1..54089837a4775ebad5c966dc60b1d1c316b9545d 100644 --- a/source/anim/Updatable.h +++ b/source/anim/Updatable.h @@ -4,11 +4,12 @@ #include <xcommon/IXUpdatable.h> #include "AnimatedModelProvider.h" #include "DynamicModelProvider.h" +#include "DecalProvider.h" class CUpdatable: public IXUnknownImplementation<IXUpdatable> { public: - CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider); + CUpdatable(CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider, CDecalProvider *pDecalProvider); UINT startup() override; void shutdown() override; @@ -19,6 +20,7 @@ public: protected: CAnimatedModelProvider *m_pAnimatedModelProvider; CDynamicModelProvider *m_pDynamicModelProvider; + CDecalProvider *m_pDecalProvider; }; #endif diff --git a/source/anim/plugin_main.cpp b/source/anim/plugin_main.cpp index 307928b5c6c0bcc1ad61feda2a988a7dda710a94..c0c068c21cb63c94a496f3b9c67af7987c57fcca 100644 --- a/source/anim/plugin_main.cpp +++ b/source/anim/plugin_main.cpp @@ -4,6 +4,8 @@ #include "Updatable.h" #include "AnimatedModelProvider.h" #include "DynamicModelProvider.h" +#include "StaticModelProvider.h" +#include "DecalProvider.h" class CLevelSizeEventListener final: public IEventListener<XEventLevelSize> { @@ -27,8 +29,11 @@ protected: class CLoadLevelEventListener final: public IEventListener<XEventLevel> { public: - CLoadLevelEventListener(CRenderable *pRenderable): - m_pRenderable(pRenderable) + CLoadLevelEventListener(CRenderable *pRenderable, CAnimatedModelProvider *pAnimatedModelProvider, CDynamicModelProvider *pDynamicModelProvider, CDecalProvider *pDecalProvider): + m_pRenderable(pRenderable), + m_pAnimatedModelProvider(pAnimatedModelProvider), + m_pDynamicModelProvider(pDynamicModelProvider), + m_pDecalProvider(pDecalProvider) {} void onEvent(const XEventLevel *pData) { @@ -36,9 +41,18 @@ public: { case XEventLevel::TYPE_LOAD_BEGIN: m_pRenderable->setEnabled(false); + m_pDecalProvider->setEnabled(false); break; case XEventLevel::TYPE_LOAD_END: m_pRenderable->setEnabled(true); + m_pDecalProvider->setEnabled(true); + break; + + case XEventLevel::TYPE_LOAD_WAIT_ASYNC_TASKS: + while(m_pAnimatedModelProvider->hasPendingOps() && m_pDynamicModelProvider->hasPendingOps()) + { + Sleep(100); + } break; default: @@ -48,6 +62,9 @@ public: protected: CRenderable *m_pRenderable; + CAnimatedModelProvider *m_pAnimatedModelProvider; + CDynamicModelProvider *m_pDynamicModelProvider; + CDecalProvider *m_pDecalProvider; }; class CDSEPlugin: public IXUnknownImplementation<IXPlugin> @@ -57,10 +74,12 @@ public: { m_pAnimatedModelProvider = new CAnimatedModelProvider(m_pCore); m_pDynamicModelProvider = new CDynamicModelProvider(m_pCore); - m_pRenderable = new CRenderable(getID(), m_pAnimatedModelProvider, m_pDynamicModelProvider); - m_pUpdatable = new CUpdatable(m_pAnimatedModelProvider, m_pDynamicModelProvider); + m_pStaticModelProvider = new CStaticModelProvider(m_pDynamicModelProvider); + m_pDecalProvider = new CDecalProvider(m_pCore, m_pDynamicModelProvider); + m_pRenderable = new CRenderable(getID(), m_pAnimatedModelProvider, m_pDynamicModelProvider, m_pDecalProvider); + m_pUpdatable = new CUpdatable(m_pAnimatedModelProvider, m_pDynamicModelProvider, m_pDecalProvider); m_pLevelSizeEventListener = new CLevelSizeEventListener(m_pAnimatedModelProvider, m_pDynamicModelProvider); - m_pLevelLoadEventListener = new CLoadLevelEventListener(m_pRenderable); + m_pLevelLoadEventListener = new CLoadLevelEventListener(m_pRenderable, m_pAnimatedModelProvider, m_pDynamicModelProvider, m_pDecalProvider); m_pCore->getEventChannel<XEventLevelSize>(EVENT_LEVEL_GET_SIZE_GUID)->addListener(m_pLevelSizeEventListener); m_pCore->getEventChannel<XEventLevel>(EVENT_LEVEL_GUID)->addListener(m_pLevelLoadEventListener); @@ -77,16 +96,22 @@ public: { m_pCore->getEventChannel<XEventLevelSize>(EVENT_LEVEL_GET_SIZE_GUID)->removeListener(m_pLevelSizeEventListener); } + if(m_pLevelLoadEventListener) + { + m_pCore->getEventChannel<XEventLevel>(EVENT_LEVEL_GUID)->removeListener(m_pLevelLoadEventListener); + } mem_delete(m_pLevelSizeEventListener); mem_delete(m_pRenderable); mem_delete(m_pUpdatable); mem_delete(m_pAnimatedModelProvider); + mem_delete(m_pStaticModelProvider); mem_delete(m_pDynamicModelProvider); +// mem_delete(m_pDecalProvider); } UINT XMETHODCALLTYPE getInterfaceCount() override { - return(4); + return(6); } const XGUID* XMETHODCALLTYPE getInterfaceGUID(UINT id) override { @@ -105,6 +130,12 @@ public: case 3: s_guid = IXDYNAMICMODELPROVIDER_GUID; break; + case 4: + s_guid = IXDECALPROVIDER_GUID; + break; + case 5: + s_guid = IXSTATICMODELPROVIDER_GUID; + break; default: return(NULL); } @@ -149,6 +180,24 @@ public: add_ref(m_pDynamicModelProvider); *ppOut = m_pDynamicModelProvider; break; + + case 4: + if(!m_pDecalProvider) + { + init(); + } + add_ref(m_pDecalProvider); + *ppOut = m_pDecalProvider; + break; + + case 5: + if(!m_pStaticModelProvider) + { + init(); + } + add_ref(m_pStaticModelProvider); + *ppOut = m_pStaticModelProvider; + break; default: *ppOut = NULL; @@ -161,6 +210,8 @@ protected: IXCore *m_pCore = NULL; CAnimatedModelProvider *m_pAnimatedModelProvider = NULL; CDynamicModelProvider *m_pDynamicModelProvider = NULL; + CStaticModelProvider *m_pStaticModelProvider = NULL; + CDecalProvider *m_pDecalProvider = NULL; CLevelSizeEventListener *m_pLevelSizeEventListener = NULL; CLoadLevelEventListener *m_pLevelLoadEventListener = NULL; }; diff --git a/source/common b/source/common index fab676418d9c07fcbb558bca8becc9e7b835f62d..6ac114eeb7c088e7560e26475ad41f5aded8a047 160000 --- a/source/common +++ b/source/common @@ -1 +1 @@ -Subproject commit fab676418d9c07fcbb558bca8becc9e7b835f62d +Subproject commit 6ac114eeb7c088e7560e26475ad41f5aded8a047 diff --git a/source/core/BaseFileIterator.cpp b/source/core/BaseFileIterator.cpp index fb4ab2fe15bf608e513f5afb0d4df4ddbc92bad2..97884dc6494e6c8aec8b822984862ec8015bc4dc 100644 --- a/source/core/BaseFileIterator.cpp +++ b/source/core/BaseFileIterator.cpp @@ -1,49 +1,66 @@ #include "BaseFileIterator.h" -void CBaseFileIterator::canonizePath(String &sPath) +void CBaseFileIterator::canonizePath(char *szPath) { - char symbol = sPath[sPath.length() - 1]; + char symbol = szPath[strlen(szPath) - 1]; /*Дело в том что абсолютный путь к файлу может не иметь символ "/" или "\\" на конце строки, и, если его не будет путь будет некорректен*/ - if (symbol != '\\' && symbol != '/') + if(symbol != '\\' && symbol != '/') { - sPath += '/'; + strcat(szPath, "/"); + } +} + +void CBaseFileIterator::canonizePath(String *pPath) +{ + char symbol = (*pPath)[pPath->length() - 1]; + + /*Дело в том что абсолютный путь к файлу может не иметь символ "/" + или "\\" на конце строки, и, если его не будет путь будет некорректен*/ + if(symbol != '\\' && symbol != '/') + { + (*pPath) += '/'; } } void CBaseFileIterator::canonizePaths(Array<String> &paths) { - for (int i = 0, I = paths.size(); i < I; ++i) + for(int i = 0, I = paths.size(); i < I; ++i) { - canonizePath(paths[i]); + canonizePath(&paths[i]); } } -void CBaseFileIterator::fillExtensionsArray(Array<String> &extsArray, const char **exts, int iExtsSize) +void CBaseFileIterator::fillExtensionsArray(Array<AAString> &extsArray, const char **exts, int iExtsSize) { - for (int i = 0; i < iExtsSize; ++i) + for(int i = 0; i < iExtsSize; ++i) { - if (exts[i]) + if(exts[i]) { - extsArray.push_back(exts[i]); + extsArray[i].setName(exts[i]); } } } -bool CBaseFileIterator::findExtensionsInPath(const char *szPath, const Array<String> &exts) +bool CBaseFileIterator::findExtensionsInPath(const char *szPath, const Array<AAString> &exts) { - for (int i = 0, I = exts.size(); i < I; ++i) + size_t len = strlen(szPath), extLen = 0; + char szExt[32]; + + for(int i = 0, I = exts.size(); i < I; ++i) { - if (strstr(szPath, exts[i].c_str()) != NULL) + extLen = strlen(exts[i].getName()) + 1; + sprintf(szExt, ".%s", exts[i].getName()); + if(!strcmp(szPath + len - extLen, szExt)) { - return true; + return(true); } } - return !exts.size(); + return(!exts.size()); } bool CBaseFileIterator::emptyOrRepeatPath(const char * szPath) { - return !strcmp(szPath, "..") || !strcmp(szPath, ".") || !strcmp(szPath, ""); + return(!strcmp(szPath, "..") || !strcmp(szPath, ".") || !strcmp(szPath, "")); } \ No newline at end of file diff --git a/source/core/BaseFileIterator.h b/source/core/BaseFileIterator.h index 0110c448c2307e8a392a571f4f49f3dedca2d468..43fee541479fb8aab75c06f1983fc0fadb3b9a07 100644 --- a/source/core/BaseFileIterator.h +++ b/source/core/BaseFileIterator.h @@ -2,17 +2,20 @@ #define __CBASE_FILE_ITERATOR_ #include "FileSystem.h" +#include <common/AAString.h> class CBaseFileIterator : public IXUnknownImplementation<IFileIterator> { public: - void canonizePath(String &sPath); + void canonizePath(char *szPath); + + void canonizePath(String *pPath); void canonizePaths(Array<String> &paths); - void fillExtensionsArray(Array<String> &extsArray, const char **exts, int iExtsSize); + void fillExtensionsArray(Array<AAString> &extsArray, const char **exts, int iExtsSize); - bool findExtensionsInPath(const char *szPath, const Array<String> &exts); + bool findExtensionsInPath(const char *szPath, const Array<AAString> &exts); bool emptyOrRepeatPath(const char *szPath); }; diff --git a/source/core/Config.cpp b/source/core/Config.cpp index fa9a82e091a16fa29d5966cb2e72efab2459036b..d8a5b37a5d0e1ca78e52a7693f75eeacb31574e2 100644 --- a/source/core/Config.cpp +++ b/source/core/Config.cpp @@ -152,7 +152,7 @@ int CConfig::parse(const char* file) { for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mSections[s3].mValues.begin(); i; ++i) { - m_mSections[s2].mValues[i.first].val = i.second->val; + m_mSections[s2].mValues[*i.first].val = i.second->val; } } } @@ -187,7 +187,7 @@ int CConfig::parse(const char* file) { for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mSections[s3].mValues.begin(); i; ++i) { - m_mSections[s2].mValues[i.first].val = i.second->val; + m_mSections[s2].mValues[*i.first].val = i.second->val; } } } @@ -332,20 +332,20 @@ void CConfig::modify(AssotiativeArray<CConfigString, CSection> & sections, Assot { for(AssotiativeArray<CConfigString, CValue>::Iterator i = m_mFinalValues.begin(); i; ++i) { - values[i.first].val = i.second->val; + values[*i.first].val = i.second->val; } for(AssotiativeArray<CConfigString, CSection>::Iterator i = m_mSections.begin(); i; ++i) { - if(!sections.KeyExists(i.first)) + if(!sections.KeyExists(*i.first)) { - sections[i.first].parent = i.second->parent; - sections[i.first].native = false; - sections[i.first].Include = IncName; + sections[*i.first].parent = i.second->parent; + sections[*i.first].native = false; + sections[*i.first].Include = IncName; } - for(AssotiativeArray<CConfigString, CValue>::Iterator j = m_mSections[i.first].mValues.begin(); j; ++j) + for(AssotiativeArray<CConfigString, CValue>::Iterator j = m_mSections[*i.first].mValues.begin(); j; ++j) { - sections[i.first].mValues[j.first].val = j.second->val; + sections[*i.first].mValues[*j.first].val = j.second->val; } } } @@ -479,275 +479,243 @@ void CConfig::set(const char * sectionp, const char * key, const char * val) int CConfig::save() { - static const bool *s_pbDebug = GET_PCVAR_BOOL("dbg_config_save"); - if(*s_pbDebug) - { - printf(COLOR_GRAY "====== " COLOR_CYAN "CConfig::save() " COLOR_GRAY "======" COLOR_RESET "\n"); - } int terror = 0; for(AssotiativeArray<CConfigString, CSection>::Iterator i = m_mSections.begin(); i; ++i) { - if(*s_pbDebug) - { - printf("Testing section: " COLOR_LGREEN "%s" COLOR_RESET "...", i.first->c_str()); - } if(i.second->isModified) { - if(*s_pbDebug) - { - printf(COLOR_YELLOW " modified" COLOR_RESET "\n"); - } for(AssotiativeArray<CConfigString, CValue>::Iterator j = i.second->mValues.begin(); j; ++j) { - if(*s_pbDebug) - { - printf(" testing key: " COLOR_LGREEN "%s" COLOR_RESET "...", j.first->c_str()); - } if(j.second->isModified) { - if(*s_pbDebug) - { - printf(COLOR_YELLOW " modified" COLOR_RESET "\n"); - } if(i.second->native) // Write to BaseFile { - if(*s_pbDebug) - { - printf(" writing to base file " COLOR_CYAN "%s" COLOR_RESET "...\n", BaseFile.c_str()); - } terror = writeFile(BaseFile, *i.first, *j.first, j.second->val); if(terror != 0) goto end; } else // Write to i.second->Include { - if(*s_pbDebug) - { - printf(" writing to include file " COLOR_CYAN "%s" COLOR_RESET "...\n", i.second->Include.c_str()); - } terror = writeFile(i.second->Include, *i.first, *j.first, j.second->val); if(terror != 0) goto end; } } - else - { - if(*s_pbDebug) - { - printf(COLOR_GRAY " not modified" COLOR_RESET "\n"); - } - } - } - } - else - { - if(*s_pbDebug) - { - printf(COLOR_GRAY " not modified" COLOR_RESET "\n"); } } } + + terror = commitFiles(); end: - if(*s_pbDebug) - { - printf(COLOR_GRAY "=============================" COLOR_RESET "\n"); - } return(terror); } int CConfig::writeFile(const CConfigString & name, CConfigString section, CConfigString key, const CConfigString & val) { - static const bool *s_pbDebug = GET_PCVAR_BOOL("dbg_config_save"); - //printf("W: %s\t[%s]: %s = %s\n", name.c_str(), section.c_str(), key.c_str(), val.c_str()); - FILE * pF = fopen(name.c_str(), "rb"); - if(pF) + const AssotiativeArray<String, String>::Node *pUncommittedNode; + + if(!m_mapUncommitted.KeyExists(name, &pUncommittedNode)) { - if(*s_pbDebug) - { - printf(" file opened\n"); - } + pUncommittedNode = m_mapUncommitted.insert(name, ""); - fseek(pF, 0, SEEK_END); - UINT fl = ftell(pF); - fseek(pF, 0, SEEK_SET); - char * szData = new char[fl + 1]; - if(!szData) + // read file + IFile *pFile = m_pFS->openFile(name.c_str()); + if(pFile) { - printf(COLOR_LRED "Unable to allocate memory (%d) in CConfig::writeFile()" COLOR_RESET "\n", fl + 1); - fclose(pF); - return(-1); + size_t sizeFile = pFile->getSize(); + + pUncommittedNode->Val->resize(sizeFile); + char *szPtr = &((*(pUncommittedNode->Val))[0]); + pFile->readBin(szPtr, sizeFile); + szPtr[sizeFile] = 0; + + mem_release(pFile); } - fread(szData, 1, fl, pF); - szData[fl] = 0; - fclose(pF); - UINT sl = section.length(); - UINT kl = key.length(); - bool sf = false; - bool kf = false; - bool se = false; - UINT sp = 0; - for(UINT i = 0; i < fl; ++i) + } + + String &sFileData = *(pUncommittedNode->Val); + + + UINT fl = sFileData.length(); + UINT sl = section.length(); + UINT kl = key.length(); + bool sf = false; + bool kf = false; + bool se = false; + UINT sp = 0; + const char *szData = sFileData.c_str(); + for(UINT i = 0; i < fl; ++i) + { + if(szData[i] == '[' && ((i > 0 && (szData[i - 1] == '\r' || szData[i - 1] == '\n')) || i == 0)) { - if(szData[i] == '[' && ((i > 0 && (szData[i - 1] == '\r' || szData[i - 1] == '\n')) || i == 0)) + bool cmp = true; + UINT j; + for(j = i + 1; j < fl - 1 && j - i - 1 < sl; ++j) + { + if(szData[j] != section[j - i - 1]) + { + cmp = false; + break; + } + } + if(cmp && szData[j] == ']')//Section Found! { - bool cmp = true; - UINT j; - for(j = i + 1; j < fl - 1 && j - i - 1 < sl; ++j) + sf = true; + i = j; + for(; i < fl; ++i) { - if(szData[j] != section[j - i - 1]) + if(szData[i] == '\r' || szData[i] == '\n') { - cmp = false; break; } } - if(cmp && szData[j] == ']')//Section Found! + while(i < fl && (szData[i] == '\r' || szData[i] == '\n')) { - sf = true; - i = j; - for(; i < fl; ++i) + ++i; + } + sp = i; + //We are inside the section + //So, let's find the key + while(i < fl) + { + if(szData[i] == '[') // New section begin. There is no our key, so, add it! { - if(szData[i] == '\r' || szData[i] == '\n') - { - break; - } + kf = false; + se = true; + break; } - while(i < fl && (szData[i] == '\r' || szData[i] == '\n')) + while(i < fl && (szData[i] == ' ' || szData[i] == '\t')) { ++i; } - sp = i; - //We are inside the section - //So, let's find the key - while(i < fl) + bool f = true; + for(j = i; j < fl && j - i < kl; ++j) { - if(szData[i] == '[') // New section begin. There is no our key, so, add it! + if(szData[j] != key[j - i]) { - kf = false; - se = true; + f = false; break; } - while(i < fl && (szData[i] == ' ' || szData[i] == '\t')) - { - ++i; - } - bool f = true; - for(j = i; j < fl && j - i < kl; ++j) + } + if(f && (isspace((unsigned char)szData[j]) || szData[j] == '='))//KeyFound! + { + i = j; + kf = true; + for(j = i; j < fl; ++j) { - if(szData[j] != key[j - i]) + if(szData[j] == '\n' || szData[j] == '\r' || szData[j] == ';') { - f = false; + //f = false; break; } } - if(f && (isspace((unsigned char)szData[j]) || szData[j] == '='))//KeyFound! + //while(szData[j] == '\r' || szData[j] == '\n') + //{ + // ++j; + //} + //Let's write the file + + if(i != j) { - i = j; - kf = true; - for(j = i; j < fl; ++j) - { - if(szData[j] == '\n' || szData[j] == '\r' || szData[j] == ';') - { - //f = false; - break; - } - } - //while(szData[j] == '\r' || szData[j] == '\n') - //{ - // ++j; - //} - //Let's write the file - FILE * pF = fopen(name.c_str(), "wb"); - if(!pF) - { - ErrorFile = name; - mem_delete_a(szData); - return -1; - } - fwrite(szData, 1, i, pF); // First file part, including key - fwrite(" = ", 1, 3, pF); - fwrite(val.c_str(), 1, val.length(), pF); - //fwrite("\n", 1, 1, pF); - fwrite(&szData[j], 1, fl - j, pF); - fclose(pF); + sFileData.remove(i, j - i); + } + + sFileData.insert(i, String(" = ") + val); + + /* + FILE * pF = fopen(name.c_str(), "wb"); + if(!pF) + { + ErrorFile = name; mem_delete_a(szData); - return 0; + return -1; } - else // Skip current row + fwrite(szData, 1, i, pF); // First file part, including key + fwrite(" = ", 1, 3, pF); + fwrite(val.c_str(), 1, val.length(), pF); + //fwrite("\n", 1, 1, pF); + fwrite(&szData[j], 1, fl - j, pF); + fclose(pF); + mem_delete_a(szData);*/ + return 0; + } + else // Skip current row + { + for(; i < fl; ++i) { - for(; i < fl; ++i) - { - if(szData[i] == '\n' || szData[i] == '\r') - { - //f = false; - break; - } - } - while(i < fl && (szData[i] == '\r' || szData[i] == '\n')) + if(szData[i] == '\n' || szData[i] == '\r') { - ++i; + //f = false; + break; } } - } - // - if(se) - { - break; + while(i < fl && (szData[i] == '\r' || szData[i] == '\n')) + { + ++i; + } } } + // + if(se) + { + break; + } } } - if(sf && !kf) - { - if(*s_pbDebug) - { - printf(" adding key to section(sp=" COLOR_LCYAN "%d" COLOR_RESET ")\n", sp); - } - FILE * pF = fopen(name.c_str(), "wb"); - if(!pF) - { - ErrorFile = name; - mem_delete_a(szData); - return -1; - } - fwrite(szData, 1, sp, pF); // First file part - fwrite(key.c_str(), 1, key.length(), pF); - fwrite(" = ", 1, 3, pF); - fwrite(val.c_str(), 1, val.length(), pF); - fwrite("\n", 1, 1, pF); - fwrite(&szData[sp], 1, fl - sp, pF); - fclose(pF); - mem_delete_a(szData); - return 0; - } - if(!sf)//!(Section found) == Add new - { - FILE * pF = fopen(name.c_str(), "ab"); - if(!pF) - { - ErrorFile = name; - return -1; - } - fwrite((CConfigString("\n[") + section + "]\n" + key + " = " + val + "\n").c_str(), sizeof(char), section.length() + key.length() + val.length() + 8, pF); - fclose(pF); - mem_delete_a(szData); - return 0; - } } - else + if(sf && !kf) { - FILE * pF = fopen(name.c_str(), "wb"); - if(!pF) - { - ErrorFile = name; - return -1; - } - fwrite((CConfigString("[") + section + "]\n" + key + " = " + val + "\n").c_str(), sizeof(char), section.length() + key.length() + val.length() + 7, pF); - fclose(pF); + sFileData.insert(sp, key + " = " + val + '\n'); + + return 0; + } + if(!sf)//!(Section found) == Add new + { + sFileData += '\n'; + sFileData += '['; + sFileData += section; + sFileData += ']'; + sFileData += '\n'; + sFileData += key; + sFileData += " = "; + sFileData += val; + sFileData += '\n'; + return 0; } + return 0; } +int CConfig::commitFiles() +{ + for(AssotiativeArray<String, String>::Iterator i = m_mapUncommitted.begin(); i; ++i) + { + //String sTempName = (*i.first) + ".new"; + const String &sName = (*i.first); + IFile *pFile = m_pFS->openFile(sName.c_str(), FILE_MODE_WRITE); + if(!pFile) + { + ErrorFile = sName; + return(-1); + } + + if(i.second->length() != pFile->writeBin(i.second->c_str(), i.second->length())) + { + ErrorFile = sName; + pFile->close(); + return(-2); + } + pFile->close(); + + //m_pFS->rename + } + + m_mapUncommitted.clear(); + + return(0); +} + int CConfig::getSectionCount() { return(m_mSections.Size()); @@ -804,7 +772,6 @@ void CConfig::clear() for(int i = 0; i < size; ++i) { mem_release(m_vIncludes[i].pParser); - mem_delete(m_vIncludes[i].pParser); } m_vIncludes.clear(); m_mFinalValues.clear(); diff --git a/source/core/Config.h b/source/core/Config.h index 8b5587560ac1b3ee0487703f08c19646ecf65853..a2455e8582c2d464e8a3d23a6672e757511c07be 100644 --- a/source/core/Config.h +++ b/source/core/Config.h @@ -18,60 +18,27 @@ See the license in LICENSE class CConfigString: public String { public: - CConfigString() : String(){} - CConfigString(const char * str) : String(str){} - CConfigString(const String & str) : String(str){} - CConfigString(const CConfigString & str) : String(str){} - CConfigString(const CConfigString * str) : String(str){} - CConfigString(const char sym) : String(sym){} - ~CConfigString() {} - - const char & operator[](const unsigned long & num) const - { - return(m_szString[num]); - } - - char & operator[](const unsigned long & num) - { - return(m_szString[num]); - } + CConfigString(): String(){} + CConfigString(const char *str): String(str){} + CConfigString(const String &str): String(str){} + CConfigString(const CConfigString &str): String(str){} + CConfigString(char sym): String(sym){} - CConfigString & operator=(const CConfigString & str) + CConfigString& operator=(const CConfigString &str) { - release(); - m_szString = new char[str.length() + 1]; - memcpy(m_szString, str.c_str(), str.length() + 1); - return(*this); - } + String::operator=(str); - CConfigString & operator=(const CConfigString * str) - { - release(); - m_szString = new char[str->length() + 1]; - memcpy(m_szString, str->c_str(), str->length() + 1); return(*this); } bool operator==(const CConfigString &str) const { - return (strcasecmp(str.m_szString, m_szString) == 0); - } - - bool operator==(const CConfigString * str) const - { - return (strcasecmp(str->m_szString, m_szString) == 0); - } - - CConfigString & operator+=(const char & sym) - { - char * newstring = new char[length() + 2]; - sprintf(newstring, "%s%c", m_szString, sym); - mem_delete_a(m_szString); - m_szString = newstring; - return(*this); + return(strcasecmp(str.c_str(), c_str()) == 0); } }; +//########################################################################## + class CConfig: public ISXConfig { public: @@ -152,8 +119,16 @@ protected: CConfigString baseName(CConfigString dir); void modify(AssotiativeArray<CConfigString, CSection> & sections, AssotiativeArray<CConfigString, CValue> & values, CConfigString IncName); + +private: + int commitFiles(); + +private: + AssotiativeArray<String, String> m_mapUncommitted; }; +//########################################################################## + class CXConfig: public IXUnknownImplementation<IXConfig> { public: diff --git a/source/core/Core.cpp b/source/core/Core.cpp index 701dbefd9fe74d6521b702d63a072f39c1fa7952..567a43d4b214155d1aee509d45d86f0e63e5694e 100644 --- a/source/core/Core.cpp +++ b/source/core/Core.cpp @@ -35,7 +35,6 @@ CCore::CCore(const char *szName) Core_0RegisterCVarBool("g_time_run", true, "Запущено ли игровое время?", FCVAR_NOTIFY_OLD); Core_0RegisterCVarFloat("g_time_speed", 1.f, "Скорость/соотношение течения игрового времени", FCVAR_NOTIFY_OLD); - Core_0RegisterCVarBool("dbg_config_save", false, "Отладочный вывод процесса сохранения конфига"); Core_0RegisterCVarInt("r_stats", 0, "Показывать ли статистику? 0 - нет, 1 - fps и игровое время, 2 - показать полностью"); Core_0RegisterConcmd("on_g_time_run_change", []() diff --git a/source/core/FileExtsIterator.cpp b/source/core/FileExtsIterator.cpp index 6ae242e5005b65422069072861c6f06af2a38b19..67ab1aeefc2b7712e338f8fcf372f3c1ebc385ce 100644 --- a/source/core/FileExtsIterator.cpp +++ b/source/core/FileExtsIterator.cpp @@ -1,13 +1,13 @@ #include "FileExtsIterator.h" -CFileExtsIterator::CFileExtsIterator(Array<String> &paths, String &sBasePath, const char **szExt, int iExtSize) +CFileExtsIterator::CFileExtsIterator(Array<String> &paths, const char *szBasePath, const char **szExt, int iExtSize) { - this->canonizePaths(paths); - this->canonizePath(sBasePath); - this->fillExtensionsArray(m_exts, szExt, iExtSize); + m_paths.swap(paths); + strcpy(m_szBasePath, szBasePath); - this->m_paths = paths; - this->m_sBasePath = sBasePath; + canonizePaths(m_paths); + canonizePath(m_szBasePath); + fillExtensionsArray(m_exts, szExt, iExtSize); } const char *CFileExtsIterator::next() @@ -20,51 +20,55 @@ const char *CFileExtsIterator::next() int size = m_paths.size(); int sizeExt = m_exts.size(); - while (index < size) + char szFileName[SIZE_PATH]; + + while(index < size) { - const String &fileName = (m_paths[index] + "*.*"); + sprintf(szFileName, "%s*.*", m_paths[index].c_str()); //Проверяем указатель, если m_handle пустой, то ищем первый файл с расширением szExts - hf = INVALID_OR_NULL(m_handle) ? FindFirstFileW(CMB2WC(fileName.c_str()), &FindFileData) : m_handle; + hf = INVALID_OR_NULL(m_handle) ? FindFirstFileW(CMB2WC(szFileName), &FindFileData) : m_handle; - if (hf != INVALID_HANDLE_VALUE) + if(hf != INVALID_HANDLE_VALUE) { do { //Сохраняем HANDLE файла, что бы можно было продожлить с того места m_handle = hf; - m_pathStr = m_paths[index] + CWC2MB(FindFileData.cFileName); + sprintf(m_szPathStr, "%s%s", m_paths[index].c_str(), (const char*)CWC2MB(FindFileData.cFileName)); - DWORD flag = GetFileAttributesW(CMB2WC(m_pathStr.c_str())); + DWORD flag = GetFileAttributesW(CMB2WC(m_szPathStr)); - if (flag != INVALID_FILE_ATTRIBUTES && !(flag & FILE_ATTRIBUTE_DIRECTORY)) + if(flag != INVALID_FILE_ATTRIBUTES && !(flag & FILE_ATTRIBUTE_DIRECTORY)) { - if (!findExtensionsInPath(CWC2MB(FindFileData.cFileName), m_exts)) + if(!findExtensionsInPath(CWC2MB(FindFileData.cFileName), m_exts)) { continue; } - m_pathStr = (m_sBasePath + CWC2MB(FindFileData.cFileName)); - if (m_mapExistPath.KeyExists(m_pathStr)) + sprintf(m_szPathStr, "%s%s", m_szBasePath, (const char*)CWC2MB(FindFileData.cFileName)); + + if(m_mapExistPath.KeyExists(m_szPathStr)) { continue; } else { //Возвращаем относительный путь, вместе с именем файла и расширением - m_mapExistPath[m_pathStr] = index; - return m_pathStr.c_str(); + m_mapExistPath[m_szPathStr] = index; + return(m_szPathStr); } } //Если указатель на файл валидный, то проверяем все отфильтрованные файлы по порядку - } while (FindNextFileW(hf, &FindFileData) != 0); + } + while(FindNextFileW(hf, &FindFileData) != 0); } ++index; FIND_CLOSE(m_handle); } //Если вообще не нашли файлов возвращаем nullptr - return nullptr; + return(NULL); } void CFileExtsIterator::reset() diff --git a/source/core/FileExtsIterator.h b/source/core/FileExtsIterator.h index 6fcfc213808ebe18225fedff68bac67450d44b00..bc020156ef7335054963f805fc718563ab15afcd 100644 --- a/source/core/FileExtsIterator.h +++ b/source/core/FileExtsIterator.h @@ -12,9 +12,9 @@ class CFileExtsIterator final : public CBaseFileIterator { private: Array<String> m_paths; - String m_pathStr; - String m_sBasePath; - Array<String> m_exts; + char m_szPathStr[SIZE_PATH]; + char m_szBasePath[SIZE_PATH]; + Array<AAString> m_exts; AssotiativeArray<String, int> m_mapExistPath; int index = 0; @@ -24,7 +24,7 @@ private: int m_currentExt = 0; public: - CFileExtsIterator::CFileExtsIterator(Array<String> &paths, String &sBasePath, const char **szExt, int iExtSize); + CFileExtsIterator::CFileExtsIterator(Array<String> &paths, const char *szBasePath, const char **szExt, int iExtSize); const char* XMETHODCALLTYPE next() override; diff --git a/source/core/FileRecursiveExtPathsIterator.cpp b/source/core/FileRecursiveExtPathsIterator.cpp index 83c61563a51141dd68c334aca38103b99fea833c..ab45a930589d41a75bacba31d031ab7e2d510473 100644 --- a/source/core/FileRecursiveExtPathsIterator.cpp +++ b/source/core/FileRecursiveExtPathsIterator.cpp @@ -1,51 +1,59 @@ #include "FileRecursiveExtPathsIterator.h" -CFileRecursiveExtPathsIterator::CFileRecursiveExtPathsIterator(Array<String> &paths, String &sBasePath, const char **szExts, int iExtSize) +CFileRecursiveExtPathsIterator::CFileRecursiveExtPathsIterator(Array<String> &paths, const char *szBasePath, const char **szExts, int iExtSize) { - this->canonizePaths(paths); - this->canonizePath(sBasePath); - this->fillExtensionsArray(m_exts, szExts, iExtSize); + strcpy(m_szBasePath, szBasePath); + m_aPaths.swap(paths); - this->m_sPaths = paths; - this->m_sBasePath = sBasePath; + canonizePaths(m_aPaths); + canonizePath(m_szBasePath); + + fillExtensionsArray(m_exts, szExts, iExtSize); } const char *CFileRecursiveExtPathsIterator::next() { + char szFileName[SIZE_PATH]; + char szFullName[SIZE_PATH]; + WIN32_FIND_DATAW FindFileData; HANDLE hf; + UINT maxPathIndex = m_aPaths.size(); FindFileData.cFileName[0] = '\0'; - UINT maxPathIndex = m_sPaths.size(); while (pathIndex < maxPathIndex) { - m_currentFullPath = !m_currentFullPath.length() ? m_sPaths[pathIndex] : m_currentFullPath; + if(strlen(m_szCurrentFullPath) > 0) + { + strcpy(m_szCurrentFullPath, m_aPaths[pathIndex].c_str()); + } + do { - String fileName = m_sPaths[pathIndex] + "*.*"; + sprintf(szFileName, "%s*.*", m_aPaths[pathIndex].c_str()); //Проверяем указатель, если m_handle пустой, то ищем первый файл с расширением szExts - hf = INVALID_OR_NULL(m_handle) ? FindFirstFileW(CMB2WC(fileName.c_str()), &FindFileData) : m_handle; + hf = INVALID_OR_NULL(m_handle) ? FindFirstFileW(CMB2WC(szFileName), &FindFileData) : m_handle; if (hf != INVALID_HANDLE_VALUE) { - do { //Сохраняем HANDLE файла, что бы можно было продожлить с того места m_handle = hf; - String fullName = m_sPaths[pathIndex] + CWC2MB(FindFileData.cFileName); - - DWORD flag = GetFileAttributesW(CMB2WC(fullName.c_str())); - if (emptyOrRepeatPath(CWC2MB(FindFileData.cFileName))) { continue; } + sprintf(szFullName, "%s%s", m_aPaths[pathIndex].c_str(), (const char*)CWC2MB(FindFileData.cFileName)); + + DWORD flag = GetFileAttributesW(CMB2WC(szFullName)); + if (flag != INVALID_FILE_ATTRIBUTES && (flag & FILE_ATTRIBUTE_DIRECTORY)) { - m_folderList.push_back(fullName + '/'); + strcat(szFullName, "/"); + m_aFolders.push_back(szFullName); continue; } @@ -56,49 +64,55 @@ const char *CFileRecursiveExtPathsIterator::next() continue; } //Если это файл - получаем относительный путь и ищем его в списке - m_pathStr = strstr(fullName.c_str(), m_sBasePath.c_str()); + char *pos = strstr(szFullName, m_szBasePath); + + if(pos) + { + strcpy(m_szPathStr, pos); + } - if (m_mapExistPath.KeyExists(m_pathStr)) + if(m_mapExistPath.KeyExists(m_szPathStr)) { continue; } else { - m_mapExistPath[m_pathStr] = pathIndex; - return m_pathStr.c_str(); + m_mapExistPath[m_szPathStr] = pathIndex; + return(m_szPathStr); } } //Если указатель на файл валидный, то проверяем все отфильтрованные файлы по порядку } while (FindNextFileW(hf, &FindFileData) != 0); - if (m_folderList.size() != 0) + if (m_aFolders.size() != 0) { UINT index = 0; - m_sPaths[pathIndex] = m_folderList[index]; - m_folderList.erase(index); + m_aPaths[pathIndex] = m_aFolders[index]; + m_aFolders.erase(index); m_handle = NULL; } else { - m_sPaths[pathIndex] = m_currentFullPath; + m_aPaths[pathIndex] = m_szCurrentFullPath; } } - } while (m_sPaths[pathIndex] != m_currentFullPath); + } + while(m_aPaths[pathIndex] != m_szCurrentFullPath); ++pathIndex; - m_currentFullPath.release(); + m_szCurrentFullPath[0] = 0; m_handle = NULL; } //Если вообще не нашли файлов возвращаем nullptr - return nullptr; + return(NULL); } void CFileRecursiveExtPathsIterator::reset() { - if (m_sPaths.size() < pathIndex) - m_sPaths[pathIndex] = m_currentFullPath; + if(m_aPaths.size() < pathIndex) + m_aPaths[pathIndex] = m_szCurrentFullPath; - m_currentFullPath.release(); + m_szCurrentFullPath[0] = 0; m_mapExistPath.clear(); pathIndex = 0; CLOSE_HANDLE(m_handle); diff --git a/source/core/FileRecursiveExtPathsIterator.h b/source/core/FileRecursiveExtPathsIterator.h index 68d855c257d40aaca1d5930f623561740383674e..83a8d83fc13975b2d5480971c4a7b7f5ec4f4b13 100644 --- a/source/core/FileRecursiveExtPathsIterator.h +++ b/source/core/FileRecursiveExtPathsIterator.h @@ -11,14 +11,14 @@ See the license in LICENSE class CFileRecursiveExtPathsIterator final : public CBaseFileIterator { private: - Array<String> m_folderList; - Array<String> m_sPaths; - Array<String> m_exts; + Array<String> m_aFolders; + Array<String> m_aPaths; + Array<AAString> m_exts; AssotiativeArray<String, int> m_mapExistPath; - String m_sBasePath; - String m_currentFullPath; - String m_pathStr; + char m_szBasePath[SIZE_PATH]; + char m_szCurrentFullPath[SIZE_PATH]; + char m_szPathStr[SIZE_PATH]; const char *m_szExt; UINT pathIndex = 0; @@ -26,7 +26,7 @@ private: HANDLE m_handle = nullptr; public: - CFileRecursiveExtPathsIterator(Array<String> &paths, String &sBasePath, const char **szExts, int iExtSize); + CFileRecursiveExtPathsIterator(Array<String> &paths, const char *szBasePath, const char **szExts, int iExtSize); const char* XMETHODCALLTYPE next() override; diff --git a/source/core/FileSystem.cpp b/source/core/FileSystem.cpp index a55a924115a431d9f0be246534152e526084bc39..0d2df98e80c5742d4dd6fde652cc07d2847bb68b 100644 --- a/source/core/FileSystem.cpp +++ b/source/core/FileSystem.cpp @@ -10,63 +10,64 @@ template <typename T> IFileIterator *CFileSystem::getListIterator(const char *szPath, const char **szExts, int extsCount) { Array<String> paths; - String basePath(szPath); + char szBasePath[SIZE_PATH]; - if (!isAbsolutePath(szPath)) + strcpy(szBasePath, szPath); + + if(!isAbsolutePath(szPath)) { - getAllvariantsCanonizePath(szPath, paths); + getAllvariantsCanonizePath(szPath, &paths); } else { paths.push_back(szPath); } - return paths.size() ? new T(paths, basePath, szExts, extsCount) : nullptr; + return paths.size() ? new T(paths, szBasePath, szExts, extsCount) : nullptr; } void CFileSystem::addPathInPriorityArray(int id, int iPriority) { - Pair newElement{ iPriority, id }; + Pair newElement{iPriority, id}; //Если приоритет по умолчанию и нет элементов - задаем значение 0 (потому что первый) //Если элементов больше чем 0 то тогда ставим самый большой приоритет из возможных - if (iPriority == -1) + if(iPriority == -1) { UINT size = m_priorityArray.size(); newElement.priority = size > 0 ? m_priorityArray[0].priority + 1 : 0; } - m_priorityArray.insert(newElement, [](const Pair &obj, const Pair &obj2) -> bool {return obj.priority <= obj2.priority; }); + m_priorityArray.insert(newElement, [](const Pair &obj, const Pair &obj2) -> bool { return obj.priority <= obj2.priority; }); } bool CFileSystem::isFileOrDirectory(const char *szPath, bool isFile) { - char path[SIZE_PATH]; - getAbsoluteCanonizePath(szPath, path, SIZE_PATH); + char path[SIZE_PATH]; + getAbsoluteCanonizePath(szPath, path, SIZE_PATH); - if (CHECK_CORRECT_PATH(path)) - { - return false; - } + if(CHECK_CORRECT_PATH(path)) + { + return false; + } - DWORD flag = GetFileAttributesW(CMB2WC(path)); + DWORD flag = GetFileAttributesW(CMB2WC(path)); - //Проверка на то куда имено ведет путь - к файлу или папке - return (flag != INVALID_FILE_ATTRIBUTES) && (isFile ? !(flag & FILE_ATTRIBUTE_DIRECTORY) : (flag & FILE_ATTRIBUTE_DIRECTORY)); + //Проверка на то куда имено ведет путь - к файлу или папке + return (flag != INVALID_FILE_ATTRIBUTES) && (isFile ? !(flag & FILE_ATTRIBUTE_DIRECTORY) : (flag & FILE_ATTRIBUTE_DIRECTORY)); } -void CFileSystem::getAllvariantsCanonizePath(const char *szPath, Array<String> &container) +void CFileSystem::getAllvariantsCanonizePath(const char *szPath, Array<String> *container) { - for (int i = 0, I = m_filePaths.size(); i < I; ++i) + char szBuff[SIZE_PATH]; + + for(int i = 0, I = m_filePaths.size(); i < I; ++i) { - String buff = m_filePaths[i]; - buff += '/'; - buff += szPath; - buff += '/'; // <- оптимизация buffObj + sprintf(szBuff, "%s/%s/", m_filePaths[i].c_str(), szPath); - if (isDirectory(buff.c_str())) + if(isDirectory(szBuff)) { - container.push_back(buff); + container->push_back(szBuff); } } } @@ -75,86 +76,85 @@ void CFileSystem::getNormalPath(const char *szPath, char *outBuff, int iOutMax) { if(szPath != outBuff) { - size_t len = strlen(szPath) + 1; - - if(iOutMax < len) - { - MEMCCPY_ERROR(outBuff); - return; - } + if(iOutMax < strlen(szPath) + 1) + { + MEMCCPY_ERROR(outBuff); + return; + } - memcpy(outBuff, szPath, len); + strcpy(outBuff, szPath); } - do - { - *outBuff = *outBuff == '/' ? '\\' : *outBuff; - } while (*outBuff++ != '\0'); + do + { + *outBuff = *outBuff == '/' ? '\\' : *outBuff; + } + while(*outBuff++ != '\0'); } bool CFileSystem::isAbsolutePathInRoot(const char *szPath) { - if (!isAbsolutePath(szPath)) - { - return false; - } + if(!isAbsolutePath(szPath)) + { + return false; + } - char rootPath[SIZE_PATH]; - getFullPathToBuild(rootPath, SIZE_PATH); - const char *pos = strstr(szPath, rootPath); + char rootPath[SIZE_PATH]; + getFullPathToBuild(rootPath, SIZE_PATH); + const char *pos = strstr(szPath, rootPath); - return pos != nullptr; + return pos != nullptr; } void CFileSystem::getAbsoluteCanonizePath(const char *szPath, char *outPath, int iOutMax) { - bool absolute = isAbsolutePath(szPath); - bool correctPath = true; - - size_t len = absolute ? strlen(szPath) + 1 : SIZE_PATH; - - if (absolute) - { - memcpy(outPath, szPath, len); - } - else - { - correctPath = resolvePath(szPath, outPath, len); - } - - //Во время поиска пути могут произойти ошибки - путь может быть не найден, или слишком маленький буфер для записи - if (correctPath) - { - //Если все корректно прошло, то путь можно канонизировать - canonize_path(outPath); - } - else - { - //Если что то пошло не так записываем '\0' в память, потом это значение можно проверить - MEMCCPY_ERROR(outPath); - } + bool absolute = isAbsolutePath(szPath); + bool correctPath = true; + + size_t len = absolute ? strlen(szPath) + 1 : SIZE_PATH; + + if(absolute) + { + memcpy(outPath, szPath, len); + } + else + { + correctPath = resolvePath(szPath, outPath, len); + } + + //Во время поиска пути могут произойти ошибки - путь может быть не найден, или слишком маленький буфер для записи + if(correctPath) + { + //Если все корректно прошло, то путь можно канонизировать + canonize_path(outPath); + } + else + { + //Если что то пошло не так записываем '\0' в память, потом это значение можно проверить + MEMCCPY_ERROR(outPath); + } } void CFileSystem::getFullPathToBuild(char *buff, int iSize) { - GetModuleFileName(nullptr, buff, iSize); - dirname(buff); - dirname(buff); - canonize_path(buff); + GetModuleFileName(nullptr, buff, iSize); + dirname(buff); + dirname(buff); + canonize_path(buff); } void CFileSystem::getFileName(const char *name, char *outName, int iOutBuff) { - WIN32_FIND_DATAW wfd; + WIN32_FIND_DATAW wfd; HANDLE hFind = FindFirstFileW(CMB2WC(name), &wfd); - if (hFind != INVALID_HANDLE_VALUE) - { - FIND_CLOSE(hFind); + if(hFind != INVALID_HANDLE_VALUE) + { + FIND_CLOSE(hFind); - //Если размера буфера хватает - то записываем имя файла, если нет то записываем в [0] '\0' + //Если размера буфера хватает - то записываем имя файла, если нет то записываем в [0] '\0' iOutBuff > MAX_PATH ? memcpy(outName, CWC2MB(wfd.cFileName), MAX_PATH) : MEMCCPY_ERROR(outName); - } + } } time_t CFileSystem::filetimeToTime_t(const FILETIME& ft) @@ -163,7 +163,7 @@ time_t CFileSystem::filetimeToTime_t(const FILETIME& ft) ull.LowPart = ft.dwLowDateTime; ull.HighPart = ft.dwHighDateTime; - return ull.QuadPart / 10000000ULL - 11644473600ULL; + return ull.QuadPart / 10000000ULL - 11644473600ULL; } HANDLE CFileSystem::getFileHandle(const char *szPath) @@ -179,135 +179,134 @@ HANDLE CFileSystem::getFileHandle(const char *szPath) bool CFileSystem::isAbsolutePath(const char *szPath) { - while (*szPath != '\0') - { - //Для корректности нужна проверка на разные слеши, ведь на вход может прийти путь не с / - if (*szPath == ':' && (*(szPath + 1) == '/' || *(szPath + 1) == '\\')) - { - return true; - } - ++szPath; - } - return false; + while(*szPath != '\0') + { + //Для корректности нужна проверка на разные слеши, ведь на вход может прийти путь не с / + if(*szPath == ':' && (*(szPath + 1) == '/' || *(szPath + 1) == '\\')) + { + return true; + } + ++szPath; + } + return false; } -String *CFileSystem::copyFile(const char* szPath) +void CFileSystem::copyFile(const char* szPath, char *szOut) { - createDirectory(m_filePaths[m_writableRoot].c_str()); - - char fn[MAX_PATH]; - getFileName(szPath, fn, MAX_PATH); - String *newFilePath = new String(m_filePaths[m_writableRoot] + '/' + fn); - CopyFileW(CMB2WC(szPath), CMB2WC(newFilePath->c_str()), false); + createDirectory(m_filePaths[m_writableRoot].c_str()); - return newFilePath; + char fn[SIZE_PATH]; + getFileName(szPath, fn, SIZE_PATH); + sprintf(szOut, "%s/%s", m_filePaths[m_writableRoot].c_str(), fn); + CopyFileW(CMB2WC(szPath), CMB2WC(szOut), false); } CFileSystem::CFileSystem() { - getFullPathToBuild(m_pathToBuild, SIZE_PATH); + getFullPathToBuild(m_pathToBuild, SIZE_PATH); } UINT CFileSystem::addRoot(const char *szPath, int iPriority) { - String str; - - //Если путь не абсолютный - то прибавляем к нему часть пути к папке build - if (!isAbsolutePath(szPath)) - { - str += m_pathToBuild; - } + char szBuff[SIZE_PATH]; - str += szPath; // <--- Оптимизация для того что бы не создавать временных объектов + //Если путь не абсолютный - то прибавляем к нему часть пути к папке build + if(!isAbsolutePath(szPath)) + { + sprintf(szBuff, "%s%s", m_pathToBuild, szPath); + } + else + { + sprintf(szBuff, "%s", szPath); + } - m_filePaths.push_back(str); - addPathInPriorityArray(m_filePaths.size() - 1, iPriority); + m_filePaths.push_back(szBuff); + addPathInPriorityArray(m_filePaths.size() - 1, iPriority); - //Если у нас некорректный путь для записи и путь не является архивным - if (m_writableRoot == -1 && *szPath != '@') - { - m_writableRoot = m_filePaths.size() - 1; - } + //Если у нас некорректный путь для записи и путь не является архивным + if(m_writableRoot == -1 && *szPath != '@') + { + m_writableRoot = m_filePaths.size() - 1; + } - return m_filePaths.size() - 1; + return m_filePaths.size() - 1; } -UINT CFileSystem::getRootCount() const +UINT CFileSystem::getRootCount() const { - return m_filePaths.size(); + return m_filePaths.size(); } const char *CFileSystem::getRoot(UINT id) const { - FILEID_CHECKED(m_filePaths.size()); + FILEID_CHECKED(m_filePaths.size()); - return m_filePaths[id].c_str(); + return m_filePaths[id].c_str(); } void CFileSystem::setWritableRoot(UINT id) { - FILEID_CHECKED(m_filePaths.size()); + FILEID_CHECKED(m_filePaths.size()); - m_writableRoot = id; + m_writableRoot = id; } bool CFileSystem::resolvePath(const char *szPath, char *szOut, size_t iOutMax) { - size_t len = 0; + size_t len = 0; - if (isAbsolutePath(szPath)) - { - len = strlen(szPath) + 1; + if(isAbsolutePath(szPath)) + { + len = strlen(szPath) + 1; - CHECK_SIZE(len, iOutMax); + CHECK_SIZE(len, iOutMax); - memcpy(szOut, szPath, len); - return true; - } - - String buff; + memcpy(szOut, szPath, len); + return(true); + } - for (UINT i = 0, l = m_priorityArray.size(); i < l; ++i) - { - int id = m_priorityArray[i].pathId; - buff = (m_filePaths[id] + '/' + szPath); + char szBuff[SIZE_PATH]; - if (fileExists(buff.c_str())/* && isFile(buff.c_str())*/) - { - CHECK_SIZE(len, iOutMax); + for(UINT i = 0, l = m_priorityArray.size(); i < l; ++i) + { + int id = m_priorityArray[i].pathId; + len = sprintf(szBuff, "%s/%s", m_filePaths[id].c_str(), szPath) + 1; - len = buff.length() + 1; - memcpy(szOut, buff.c_str(), len); - return true; - } - } + if(fileExists(szBuff)/* && isFile(buff.c_str())*/) + { + CHECK_SIZE(len, iOutMax); - return false; + strcpy(szOut, szBuff); + return(true); + } + } + + return(false); } bool CFileSystem::fileExists(const char *szPath) { - char path[SIZE_PATH]; - getAbsoluteCanonizePath(szPath, path, SIZE_PATH); + char path[SIZE_PATH]; + getAbsoluteCanonizePath(szPath, path, SIZE_PATH); - if (CHECK_CORRECT_PATH(path)) - { - //Если не удалось найти полный путь - на выход - return false; - } + if(CHECK_CORRECT_PATH(path)) + { + //Если не удалось найти полный путь - на выход + return false; + } - return fileGetSize(path) != FILE_NOT_FOUND; + return(fileGetSize(path) != FILE_NOT_FOUND); } size_t CFileSystem::fileGetSize(const char *szPath) { - char path[SIZE_PATH]; - getAbsoluteCanonizePath(szPath, path, SIZE_PATH); + char path[SIZE_PATH]; + getAbsoluteCanonizePath(szPath, path, SIZE_PATH); - if (CHECK_CORRECT_PATH(path)) - { - return FILE_NOT_FOUND; - } + if(CHECK_CORRECT_PATH(path)) + { + return FILE_NOT_FOUND; + } WIN32_FILE_ATTRIBUTE_DATA lpFileInformation; @@ -315,234 +314,211 @@ size_t CFileSystem::fileGetSize(const char *szPath) //Преобразование размера из старших и младших бит ULONGLONG FileSize = (static_cast<ULONGLONG>(lpFileInformation.nFileSizeHigh) << - sizeof(lpFileInformation.nFileSizeLow) * NUM_BITS_IN_BYTE) | + sizeof(lpFileInformation.nFileSizeLow) * NUM_BITS_IN_BYTE) | lpFileInformation.nFileSizeLow; //Если result != 0 то все хорошо, если 0 то файл не найден - return result != 0 ? FileSize : FILE_NOT_FOUND; + return(result != 0 ? FileSize : FILE_NOT_FOUND); } bool CFileSystem::isFile(const char *szPath) { - return isFileOrDirectory(szPath, true); + return(isFileOrDirectory(szPath, true)); } bool CFileSystem::isDirectory(const char *szPath) { - return isFileOrDirectory(szPath, false); + return(isFileOrDirectory(szPath, false)); } time_t CFileSystem::getFileModifyTime(const char *szPath) { - char path[SIZE_PATH]; - getAbsoluteCanonizePath(szPath, path, SIZE_PATH); + char path[SIZE_PATH]; + getAbsoluteCanonizePath(szPath, path, SIZE_PATH); - if (CHECK_CORRECT_PATH(path)) - { - return 0; - } + if(CHECK_CORRECT_PATH(path)) + { + return(0); + } - WIN32_FILE_ATTRIBUTE_DATA lpFileInformation; + WIN32_FILE_ATTRIBUTE_DATA lpFileInformation; GetFileAttributesExW(CMB2WC(path), GetFileExInfoStandard, &lpFileInformation); - return filetimeToTime_t(lpFileInformation.ftLastWriteTime); + return(filetimeToTime_t(lpFileInformation.ftLastWriteTime)); } IFileIterator *CFileSystem::getFolderList(const char *szPath) { Array<String> paths; - String basePath(szPath); + char szBasePath[SIZE_PATH]; + + strcpy(szBasePath, szPath); - if (!isAbsolutePath(szPath)) + if(!isAbsolutePath(szPath)) { - getAllvariantsCanonizePath(szPath, paths); - } + getAllvariantsCanonizePath(szPath, &paths); + } else { paths.push_back(szPath); } - return paths.size() ? new CFolderPathsIterator(paths, basePath) : nullptr; + return(paths.size() ? new CFolderPathsIterator(paths, szBasePath) : NULL); } IFileIterator *CFileSystem::getFileList(const char *szPath, const char *szExt) { - const char *exts[] = { szExt }; - return getFileList(szPath, exts, 1); + const char *exts[] = {szExt}; + return(getFileList(szPath, exts, 1)); } IFileIterator *CFileSystem::getFileList(const char *szPath, const char **szExts, int extsCount) { - return getListIterator<CFileExtsIterator>(szPath, szExts, extsCount); + return(getListIterator<CFileExtsIterator>(szPath, szExts, extsCount)); } IFileIterator *CFileSystem::getFileListRecursive(const char *szPath, const char *szExt) { - const char *exts[] = { szExt }; - return getFileListRecursive(szPath, exts, 1); + const char *exts[] = {szExt}; + return(getFileListRecursive(szPath, exts, 1)); } IFileIterator *CFileSystem::getFileListRecursive(const char *szPath, const char **szExts, int extsCount) { - return getListIterator<CFileRecursiveExtPathsIterator>(szPath, szExts, extsCount); + return(getListIterator<CFileRecursiveExtPathsIterator>(szPath, szExts, extsCount)); } bool CFileSystem::createDirectory(const char *szPath) { - char path[SIZE_PATH]; - getNormalPath(szPath, path, SIZE_PATH); - if(!isAbsolutePath(path)) - { - char szBuf[MAX_PATH]; - szBuf[0] = 0; - strcat(szBuf, m_filePaths[m_writableRoot].c_str()); - strcat(szBuf, "/"); - strcat(szBuf, szPath); - char *tmp = szBuf; - while(*tmp) - { - if(*tmp == '/') - { - *tmp = '\\'; - } - ++tmp; - } - return(SHCreateDirectoryExW(nullptr, CMB2WC(szBuf), nullptr) == NO_ERROR); - } + char path[SIZE_PATH]; + getNormalPath(szPath, path, SIZE_PATH); + if(!isAbsolutePath(path)) + { + char szBuf[SIZE_PATH]; + sprintf(szBuf, "%s/%s", m_filePaths[m_writableRoot].c_str(), szPath); + char *tmp = szBuf; + while(*tmp) + { + if(*tmp == '/') + { + *tmp = '\\'; + } + ++tmp; + } + return(SHCreateDirectoryExW(nullptr, CMB2WC(szBuf), nullptr) == NO_ERROR); + } return(SHCreateDirectoryExW(nullptr, CMB2WC(path), nullptr) == NO_ERROR); } bool CFileSystem::deleteDirectory(const char *szPath) { - char path[SIZE_PATH]; + char path[SIZE_PATH]; getAbsoluteCanonizePath(szPath, path, SIZE_PATH); getNormalPath(path, path, SIZE_PATH); - CMB2WC wszPath(path); - size_t len = wcslen(wszPath); - wchar_t *pBuf = (wchar_t*)alloca(sizeof(wchar_t) * (len + 2)); - memcpy(pBuf, wszPath, sizeof(wchar_t) * (len + 1)); - pBuf[len + 1] = 0; - - SHFILEOPSTRUCTW file_op = { - NULL, - FO_DELETE, - pBuf, - L"", - FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT, - false, - 0, - L"" - }; - - // Если вернуло не 0, то все плохо - return(SHFileOperationW(&file_op) == NO_ERROR); + CMB2WC wszPath(path); + size_t len = wcslen(wszPath); + wchar_t *pBuf = (wchar_t*)alloca(sizeof(wchar_t) * (len + 2)); + memcpy(pBuf, wszPath, sizeof(wchar_t) * (len + 1)); + pBuf[len + 1] = 0; + + SHFILEOPSTRUCTW file_op = { + NULL, + FO_DELETE, + pBuf, + L"", + FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT, + false, + 0, + L"" + }; + + // Если вернуло не 0, то все плохо + return(SHFileOperationW(&file_op) == NO_ERROR); } IFile *CFileSystem::openFile(const char *szPath, FILE_OPEN_MODE mode = FILE_MODE_READ) { - //Выходим если режим открытия - не для чтения и нет пути для записи - if (m_writableRoot == -1 && mode != FILE_MODE_READ) - { - return nullptr; - } - - //Зарезервированная строка, если вдруг не хватит SIZE_PATH - char reserveStr[SIZE_PATH * 2]; - bool useReserveStr = false; - - char fullPath[SIZE_PATH]; - getAbsoluteCanonizePath(szPath, fullPath, SIZE_PATH); - - //Если по каким либо причинам нельзя вернуть полный путь - на выход - if (CHECK_CORRECT_PATH(fullPath) && mode == FILE_MODE_READ) - { - return nullptr; - } - - IFile *file = new CFile; - - if (mode == FILE_MODE_READ) - { - //Если открываем только на чтение - то копирование не нужно (следовательно и выделение памяти тоже лишняя операция) - if (file->open(fullPath, CORE_FILE_BIN) != 0) - { - mem_delete(file); - } - return file; - } - - String *newFileName; - - if (CHECK_CORRECT_PATH(fullPath)) - { - newFileName = new String(m_filePaths[m_writableRoot].c_str()); - *newFileName += '/'; - *newFileName += szPath; - - getAbsoluteCanonizePath(newFileName->c_str(), fullPath, SIZE_PATH); - - mem_delete(newFileName); - } - - bool inRoot = isAbsolutePathInRoot(fullPath); - - //Если путь в корне, и файла не существует - создаем его - if (inRoot && !fileExists(fullPath)) - { - size_t len = strlen(fullPath) + 1; - char dirName[SIZE_PATH]; - - memcpy(dirName, fullPath, len); - dirname(dirName); - len = strlen(dirName); - dirName[len - 1] = '\0'; - createDirectory(dirName); - } - //Если путь не в корне и его не существует - на выход - else if (!fileExists(fullPath)) - { - mem_delete(file); - return nullptr; - } - //Если путь вне корня - тогда копируем в корень - else if (!inRoot) - { - newFileName = copyFile(fullPath); - size_t lenPath = strlen(fullPath) + 1; - - if (lenPath < newFileName->length()) - { - memcpy(reserveStr, newFileName->c_str(), newFileName->length() + 1); - useReserveStr = true; - } - - memcpy(fullPath, newFileName->c_str(), newFileName->length() + 1); - mem_delete(newFileName); - } - - int res = 0; - - //Определяем использован ли дополнительный путь для записи в файловой системе - char* correctPath = useReserveStr ? reserveStr : fullPath; - - switch (mode) - { - case FILE_MODE_WRITE: - res = file->create(correctPath, CORE_FILE_BIN); - break; - - case FILE_MODE_APPEND: - res = file->add(correctPath, CORE_FILE_BIN); - break; - } - - if (res != 0) - { - mem_delete(file); - } - - return file; + //Выходим если режим открытия - не для чтения и нет пути для записи + if(m_writableRoot == -1 && mode != FILE_MODE_READ) + { + return(NULL); + } + + char fullPath[SIZE_PATH]; + getAbsoluteCanonizePath(szPath, fullPath, SIZE_PATH); + + //Если по каким либо причинам нельзя вернуть полный путь - на выход + if(CHECK_CORRECT_PATH(fullPath) && mode == FILE_MODE_READ) + { + return(NULL); + } + + IFile *file = new CFile; + + if(mode == FILE_MODE_READ) + { + //Если открываем только на чтение - то копирование не нужно (следовательно и выделение памяти тоже лишняя операция) + if(file->open(fullPath, CORE_FILE_BIN) != 0) + { + mem_delete(file); + } + return(file); + } + + char szNewFileName[SIZE_PATH]; + + if(CHECK_CORRECT_PATH(fullPath)) + { + sprintf(szNewFileName, "%s/%s", m_filePaths[m_writableRoot].c_str(), szPath); + + getAbsoluteCanonizePath(szNewFileName, fullPath, SIZE_PATH); + } + + bool inRoot = isAbsolutePathInRoot(fullPath); + + //Если путь в корне, и файла не существует - создаем его + if(inRoot && !fileExists(fullPath)) + { + char dirName[SIZE_PATH]; + + strcpy(dirName, fullPath); + dirname(dirName); + createDirectory(dirName); + } + //Если путь не в корне и его не существует - на выход + else if(!fileExists(fullPath)) + { + mem_delete(file); + return(NULL); + } + //Если путь вне корня - тогда копируем в корень + else if(!inRoot) + { + copyFile(fullPath, szNewFileName); + + strcpy(fullPath, szNewFileName); + } + + int res = 0; + + switch(mode) + { + case FILE_MODE_WRITE: + res = file->create(fullPath, CORE_FILE_BIN); + break; + + case FILE_MODE_APPEND: + res = file->add(fullPath, CORE_FILE_BIN); + break; + } + + if(res != 0) + { + mem_delete(file); + } + + return(file); } \ No newline at end of file diff --git a/source/core/FileSystem.h b/source/core/FileSystem.h index 5bcfcb4536f716d7124ef8eed633e424743fa070..fa1942b52f03b124d6839d50a7b50dbe88c192fe 100644 --- a/source/core/FileSystem.h +++ b/source/core/FileSystem.h @@ -43,7 +43,7 @@ See the license in LICENSE #define MEMCCPY_ERROR(buff) memcpy(buff, "\0", 1); -#define SIZE_PATH 4096 +#define SIZE_PATH 2048 #define INVALID_OR_NULL(handle) ((handle) == NULL || (handle) == INVALID_HANDLE_VALUE) @@ -106,7 +106,7 @@ private: //! Метод делает проверку, ведет ли путь к файлу или папке bool isFileOrDirectory(const char *szPath, bool isFile); - void getAllvariantsCanonizePath(const char *szPath, Array<String> &container); + void getAllvariantsCanonizePath(const char *szPath, Array<String> *container); //!Превращает канонизированный путь в неканонизированный void getNormalPath(const char *szPath, char *outBuff, int iOutMax); @@ -128,7 +128,7 @@ private: bool isAbsolutePath(const char* szPath); - String *copyFile(const char* szPath); + void copyFile(const char* szPath, char *szOut); //!корневые пути и приоритет Array<String> m_filePaths; diff --git a/source/core/FolderPathsIterator.cpp b/source/core/FolderPathsIterator.cpp index 54e2b9cc752293849f0cdbe6572269becbdbe4e8..dd271e77cbcebc1dc26ba6f432f3adc5530d35bd 100644 --- a/source/core/FolderPathsIterator.cpp +++ b/source/core/FolderPathsIterator.cpp @@ -1,73 +1,75 @@ #include "FolderPathsIterator.h" -CFolderPathsIterator::CFolderPathsIterator(Array<String> &paths, String &sBasePath) +CFolderPathsIterator::CFolderPathsIterator(Array<String> &paths, const char *szBasePath) { - this->canonizePaths(paths); - this->canonizePath(sBasePath); + m_paths.swap(paths); + strcpy(m_szBasePath, szBasePath); - this->m_paths = paths; - this->m_sBasePath = sBasePath; + canonizePaths(m_paths); + canonizePath(m_szBasePath); } const char *CFolderPathsIterator::next() { - WIN32_FIND_DATAW FindFileData; - HANDLE hf; + WIN32_FIND_DATAW FindFileData; + HANDLE hf; FindFileData.cFileName[0] = '\0'; UINT size = m_paths.size(); - while (index < size) - { + while(index < size) + { hf = INVALID_OR_NULL(m_handle) ? FindFirstFileW(CMB2WC((m_paths[index] + "*.*").c_str()), &FindFileData) : m_handle; - if (hf != INVALID_HANDLE_VALUE) - { - do { - m_handle = hf; + if(hf != INVALID_HANDLE_VALUE) + { + do { + m_handle = hf; - m_pathStr = (m_paths)[index] + CWC2MB(FindFileData.cFileName); + sprintf(m_szPathStr, "%s%s", (m_paths)[index].c_str(), (const char*)CWC2MB(FindFileData.cFileName)); - DWORD flag = GetFileAttributesW(CMB2WC(m_pathStr.c_str())); + DWORD flag = GetFileAttributesW(CMB2WC(m_szPathStr)); - if (emptyOrRepeatPath(CWC2MB(FindFileData.cFileName))) - { - continue; - } + if(emptyOrRepeatPath(CWC2MB(FindFileData.cFileName))) + { + continue; + } - //Берет только имена директорий - if (flag != INVALID_FILE_ATTRIBUTES && flag & FILE_ATTRIBUTE_DIRECTORY) - { - m_pathStr = (m_sBasePath + CWC2MB(FindFileData.cFileName)); - if (m_mapExistPath.KeyExists(m_pathStr)) + //Берет только имена директорий + if(flag != INVALID_FILE_ATTRIBUTES && flag & FILE_ATTRIBUTE_DIRECTORY) + { + sprintf(m_szPathStr, "%s%s", m_szBasePath, (const char*)CWC2MB(FindFileData.cFileName)); + + if(m_mapExistPath.KeyExists(m_szPathStr)) { continue; } else { //Возвращаем относительный путь к директории - m_mapExistPath[m_pathStr] = index; - return m_pathStr.c_str(); + m_mapExistPath[m_szPathStr] = index; + return(m_szPathStr); } - } - } while (FindNextFileW(hf, &FindFileData) != 0); - } + } + } + while(FindNextFileW(hf, &FindFileData) != 0); + } FIND_CLOSE(m_handle); ++index; - } + } - //Если вообще не нашли файлов возвращаем nullptr - return nullptr; + //Если вообще не нашли файлов возвращаем nullptr + return(NULL); } void CFolderPathsIterator::reset() { - index = 0; - FIND_CLOSE(m_handle); + index = 0; + FIND_CLOSE(m_handle); } CFolderPathsIterator::~CFolderPathsIterator() { - FIND_CLOSE(m_handle); + FIND_CLOSE(m_handle); } diff --git a/source/core/FolderPathsIterator.h b/source/core/FolderPathsIterator.h index ae74e5a345dd64722b56c9171d9821faedfdf021..ebf4359fdbbda7887b6e047e04a45e2443bafd62 100644 --- a/source/core/FolderPathsIterator.h +++ b/source/core/FolderPathsIterator.h @@ -13,8 +13,8 @@ class CFolderPathsIterator final : public CBaseFileIterator private: Array<String> m_paths; - String m_sBasePath; - String m_pathStr; + char m_szBasePath[SIZE_PATH]; + char m_szPathStr[SIZE_PATH]; AssotiativeArray<String, int> m_mapExistPath; int index = 0; @@ -22,7 +22,7 @@ private: HANDLE m_handle = nullptr; public: - CFolderPathsIterator(Array<String> &paths, String &sBasePath); + CFolderPathsIterator(Array<String> &paths, const char *szBasePath); const char* XMETHODCALLTYPE next() override; diff --git a/source/core/JSON.cpp b/source/core/JSON.cpp index 01cf57087512654df973e57ae71807a483decf4f..c25acd44e60fe9529a69ea96a0fa640453d535e7 100644 --- a/source/core/JSON.cpp +++ b/source/core/JSON.cpp @@ -1,5 +1,9 @@ #include "JSON.h" +MemAlloc<CJSONValue> CJSON::ms_memValues; +MemAlloc<CJSONArray> CJSON::ms_memArrays; +MemAlloc<CJSONObject> CJSON::ms_memObjects; +SpinLock CJSON::ms_slMem; bool XMETHODCALLTYPE CJSON::parse(const char *szString, IXJSONItem **ppOut, void *pReserved) const { @@ -25,7 +29,7 @@ bool CJSON::Parse(const char **str, IXJSONItem **ppOut) if(**str == '{') { - CJSONObject *o = new CJSONObject(); + CJSONObject *o = AllocObject(); if(!o->load(str)) { mem_release(o); @@ -35,7 +39,7 @@ bool CJSON::Parse(const char **str, IXJSONItem **ppOut) } else if(**str == '[') { - CJSONArray *o = new CJSONArray(); + CJSONArray *o = AllocArray(); if(!o->load(str)) { mem_release(o); @@ -45,7 +49,7 @@ bool CJSON::Parse(const char **str, IXJSONItem **ppOut) } else { - CJSONValue *o = new CJSONValue(); + CJSONValue *o = AllocValue(); if(!o->load(str)) { mem_release(o); @@ -56,16 +60,40 @@ bool CJSON::Parse(const char **str, IXJSONItem **ppOut) return(true); } -//########################################################################## +CJSONValue* CJSON::AllocValue() +{ + ScopedSpinLock lock(ms_slMem); + return(ms_memValues.Alloc()); +} +CJSONArray* CJSON::AllocArray() +{ + ScopedSpinLock lock(ms_slMem); + return(ms_memArrays.Alloc()); +} +CJSONObject* CJSON::AllocObject() +{ + ScopedSpinLock lock(ms_slMem); + return(ms_memObjects.Alloc()); +} -CJSONArray::~CJSONArray() +void CJSON::ReleaseItem(CJSONValue *pItem) { - for(UINT i = 0, l = m_aItems.size(); i < l; ++i) - { - mem_release(m_aItems[i]); - } + ScopedSpinLock lock(ms_slMem); + ms_memValues.Delete(pItem); +} +void CJSON::ReleaseItem(CJSONArray *pItem) +{ + ScopedSpinLock lock(ms_slMem); + ms_memArrays.Delete(pItem); +} +void CJSON::ReleaseItem(CJSONObject *pItem) +{ + ScopedSpinLock lock(ms_slMem); + ms_memObjects.Delete(pItem); } +//########################################################################## + UINT XMETHODCALLTYPE CJSONArray::size() const { return(m_aItems.size()); @@ -122,16 +150,18 @@ bool CJSONArray::loadArr(const char **str) return(true); } -//########################################################################## - -CJSONObject::~CJSONObject() +void XMETHODCALLTYPE CJSONArray::FinalRelease() { - for(UINT i = 0, l = m_aPairs.size(); i < l; ++i) + for(UINT i = 0, l = m_aItems.size(); i < l; ++i) { - mem_release(m_aPairs[i].pVal); + mem_release(m_aItems[i]); } + + CJSON::ReleaseItem(this); } +//########################################################################## + UINT XMETHODCALLTYPE CJSONObject::size() const { return(m_aPairs.size()); @@ -216,3 +246,21 @@ bool CJSONObject::loadObj(const char **str) ++*str; return(true); } + +void XMETHODCALLTYPE CJSONObject::FinalRelease() +{ + for(UINT i = 0, l = m_aPairs.size(); i < l; ++i) + { + mem_release(m_aPairs[i].pVal); + } + + CJSON::ReleaseItem(this); +} + +//########################################################################## + +void XMETHODCALLTYPE CJSONValue::FinalRelease() +{ + CJSON::ReleaseItem(this); +} + diff --git a/source/core/JSON.h b/source/core/JSON.h index 6f72cb87c4856de359c8661a6a542f8ad6602977..92b7d5f534b5ba6f9bf0e38091b4740cff9b80f0 100644 --- a/source/core/JSON.h +++ b/source/core/JSON.h @@ -62,7 +62,7 @@ public: return(true); case XJI_STRING: //! FIXME add some checks! - *pOut = m_sVal.toDouble(); + *pOut = m_sVal.toFloat(); return(true); case XJI_NULL: @@ -83,7 +83,7 @@ public: case XJI_STRING: //! FIXME add some checks! //! FIXME fix type! - *pOut = m_sVal.toLongInt(); + *pOut = m_sVal.toInt64(); return(true); case XJI_NULL: @@ -195,8 +195,10 @@ public: private: bool loadString(const char **prm) { + m_sVal = ""; + m_type = XJI_STRING; - Array<char> tmp; + //Array<char> tmp; ++*prm; while(**prm && **prm != '"') { @@ -208,22 +210,22 @@ private: case L'"': case L'\\': case L'/': - tmp.push_back(**prm); + m_sVal += **prm; break; case L'b': - tmp.push_back('\b'); + m_sVal += '\b'; break; case L'f': - tmp.push_back('\f'); + m_sVal += '\f'; break; case L'n': - tmp.push_back('\n'); + m_sVal += '\n'; break; case L'r': - tmp.push_back('\r'); + m_sVal += '\r'; break; case L't': - tmp.push_back('\t'); + m_sVal += '\t'; break; case L'u': { @@ -249,7 +251,7 @@ private: return(false); } } - writeChar(code, &tmp); + writeChar(code); } break; default: @@ -258,7 +260,7 @@ private: } else { - tmp.push_back(**prm); + m_sVal += **prm; } ++*prm; } @@ -267,8 +269,6 @@ private: return(false); } ++*prm; - tmp.push_back(0); - m_sVal = tmp; return(true); } bool loadNum(const char **prm) @@ -399,7 +399,7 @@ private: return(false); } - void writeChar(UINT c, Array<char> *pOut) + void writeChar(UINT c) { UINT codepoint = 0; short *in = (short*)&c; @@ -422,28 +422,28 @@ private: if(codepoint <= 0x7f) { - pOut->push_back(codepoint); + m_sVal += (char)codepoint; break; } else if(codepoint <= 0x7ff) { - pOut->push_back(0xc0 | ((codepoint >> 6) & 0x1f)); - pOut->push_back(0x80 | (codepoint & 0x3f)); + m_sVal += (char)(0xc0 | ((codepoint >> 6) & 0x1f)); + m_sVal += (char)(0x80 | (codepoint & 0x3f)); break; } else if(codepoint <= 0xffff) { - pOut->push_back(0xe0 | ((codepoint >> 12) & 0x0f)); - pOut->push_back(0x80 | ((codepoint >> 6) & 0x3f)); - pOut->push_back(0x80 | (codepoint & 0x3f)); + m_sVal += (char)(0xe0 | ((codepoint >> 12) & 0x0f)); + m_sVal += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + m_sVal += (char)(0x80 | (codepoint & 0x3f)); break; } else { - pOut->push_back(0xf0 | ((codepoint >> 18) & 0x07)); - pOut->push_back(0x80 | ((codepoint >> 12) & 0x3f)); - pOut->push_back(0x80 | ((codepoint >> 6) & 0x3f)); - pOut->push_back(0x80 | (codepoint & 0x3f)); + m_sVal += (char)(0xf0 | ((codepoint >> 18) & 0x07)); + m_sVal += (char)(0x80 | ((codepoint >> 12) & 0x3f)); + m_sVal += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + m_sVal += (char)(0x80 | (codepoint & 0x3f)); break; } } @@ -458,15 +458,19 @@ protected: mutable String m_sVal; }; +//########################################################################## + class CJSONValue: public CJSONItem<IXJSONItem> { +private: + void XMETHODCALLTYPE FinalRelease() override; }; +//########################################################################## + class CJSONArray: public CJSONItem<IXJSONArray> { public: - ~CJSONArray(); - UINT XMETHODCALLTYPE size() const override; IXJSONItem* XMETHODCALLTYPE at(UINT idx) const override; @@ -481,16 +485,18 @@ public: private: bool loadArr(const char **str) override; + + void XMETHODCALLTYPE FinalRelease() override; private: Array<IXJSONItem*> m_aItems; }; +//########################################################################## + class CJSONObject: public CJSONItem<IXJSONObject> { public: - ~CJSONObject(); - UINT XMETHODCALLTYPE size() const override; IXJSONItem* XMETHODCALLTYPE at(UINT idx) const override; @@ -509,7 +515,10 @@ public: private: bool loadObj(const char **str) override; - + + void XMETHODCALLTYPE FinalRelease() override; + +private: struct KeyValue { String sKey; @@ -519,12 +528,30 @@ private: Array<KeyValue> m_aPairs; }; +//########################################################################## + class CJSON: public IXUnknownImplementation<IXJSON> { public: bool XMETHODCALLTYPE parse(const char *szString, IXJSONItem **ppOut, void *pReserved = NULL) const override; static bool Parse(const char **str, IXJSONItem **ppOut); + + static void ReleaseItem(CJSONValue *pItem); + static void ReleaseItem(CJSONArray *pItem); + static void ReleaseItem(CJSONObject *pItem); + +private: + static MemAlloc<CJSONValue> ms_memValues; + static MemAlloc<CJSONArray> ms_memArrays; + static MemAlloc<CJSONObject> ms_memObjects; + + static SpinLock ms_slMem; + +private: + static CJSONValue* AllocValue(); + static CJSONArray* AllocArray(); + static CJSONObject* AllocObject(); }; #endif diff --git a/source/fbxplugin/ModelLoader.cpp b/source/fbxplugin/ModelLoader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8870422b1f16fd239e0f4b1a8a545bae3691415 --- /dev/null +++ b/source/fbxplugin/ModelLoader.cpp @@ -0,0 +1,1186 @@ +#include "ModelLoader.h" +#include "libdeflate.h" + +CModelLoader::CModelLoader(IFileSystem *pFileSystem): +m_pFileSystem(pFileSystem) +{ +} + +UINT XMETHODCALLTYPE CModelLoader::getExtCount() const +{ + return(1); +} +const char* XMETHODCALLTYPE CModelLoader::getExt(UINT uIndex) const +{ + assert(uIndex < getExtCount()); + switch(uIndex) + { + case 0: + return("fbx"); + } + return(NULL); +} +const char* XMETHODCALLTYPE CModelLoader::getExtText(UINT uIndex) const +{ + assert(uIndex < getExtCount()); + switch(uIndex) + { + case 0: + return("Kaydara model"); + } + return(NULL); +} +const char* XMETHODCALLTYPE CModelLoader::getAuthor() const +{ + return("EyeGuy @ Thunderfront Studio"); +} +const char* XMETHODCALLTYPE CModelLoader::getCopyright() const +{ + return("Copyright © Ivan Dokunov, Evgeny Danilovich, 2025"); +} +const char* XMETHODCALLTYPE CModelLoader::getDescription() const +{ + return("FBX model loader"); +} + +void XMETHODCALLTYPE CModelLoader::getInfo(XModelInfo *pModelInfo) +{ + *pModelInfo = {}; + + Array<FBXNodeRecord*> aNodes; + Array<FBXNodeRecord*> aBufferNodes; + + getNodeByName("Model", &aNodes); + + const size_t lodeSize = 10; + int iBestIndex = -1; + bool aLodExist[lodeSize] = {}; + int32_t aLodIndex[lodeSize] = {}; + + Array<int32_t> aMaterialsIndexes; + Array<FBXNodeRecord*> aMaterials; + + fora(i, aNodes) + { + int index = 0; + if(aNodes[i]->aProps.size() > 1 && aNodes[i]->aProps[1].cType == 'S') + { + FBXPropertyRecord &props = aNodes[i]->aProps[1]; + size_t len = strlen(props.data.pc); + // len > strlen(LOD) word + // после props.data.pc есть еще одна строка (размер strlen(props.data.pc) и props.uArrSize будет разным) + if(len > 5) + { + if( + tolower(props.data.pc[len - 4]) == 'l' && + tolower(props.data.pc[len - 3]) == 'o' && + tolower(props.data.pc[len - 2]) == 'd' && + isdigit(props.data.pc[len - 1])) + { + index = props.data.pc[len - 1] - '0'; + } + } + } + aLodExist[index] = true; + + if(index < iBestIndex || iBestIndex == -1) + { + iBestIndex = index; + + aMaterials.clearFast(); + + pModelInfo->uVertexCount = 0; + pModelInfo->uIndexCount = 0; + } + + if(index == iBestIndex) + { + aBufferNodes.clearFast(); + + FBXNodeRecord *pGeomNode = getChildNode("Geometry", aNodes[i]); + + getNodeByName("Vertices", &aBufferNodes, pGeomNode); + FBXPropertyRecord *pProp = &aBufferNodes[0]->aProps[0]; + + assert(pProp->cType == 'd'); + + pModelInfo->uVertexCount += pProp->uArrSize / 3; + + aBufferNodes.clearFast(); + getNodeByName("PolygonVertexIndex", &aBufferNodes, pGeomNode); + pProp = &aBufferNodes[0]->aProps[0]; + + assert(pProp->cType == 'i'); + + pModelInfo->uIndexCount += getCountIndexes(pProp->data.pi, pProp->uArrSize); + + aBufferNodes.clearFast(); + getNodeByName("LayerElementMaterial", &aBufferNodes, pGeomNode); + pProp = &aBufferNodes[0]->aProps[0]; + + FBXNodeRecord *pLayerMaterial = aBufferNodes[0]; + + aBufferNodes.clearFast(); + getNodeByName("Materials", &aBufferNodes, pLayerMaterial); + pProp = &aBufferNodes[0]->aProps[0]; + + assert(pProp->cType == 'i'); + + uint32_t uSize = pProp->uArrSize; + + for(int j = 0; j < uSize; ++j) + { + if(aMaterialsIndexes.indexOf(pProp->data.pc[j]) < 0) + { + aMaterialsIndexes.push_back(pProp->data.pc[j]); + } + } + + fora(j, aMaterialsIndexes) + { + FBXNodeRecord *pMaterialNode = getChildNode("Material", aNodes[i], aMaterialsIndexes[j]); + + if(aMaterials.indexOf(pMaterialNode) < 0) + { + aMaterials.push_back(pMaterialNode); + } + } + + aMaterialsIndexes.clearFast(); + } + } + + pModelInfo->uSubsetsCount = aMaterials.size(); + + pModelInfo->uLodCount = 0; + for(int i = 0; i < ARRAYSIZE(aLodExist); ++i) + { + if(aLodExist[i]) + { + ++pModelInfo->uLodCount; + } + } + + Array<FBXNodeRecord*> pGeomArray; + FBXPropertyRecord *pProp = NULL; + + float3 vmin, vmax; + + getNodeByName("Geometry", &pGeomArray, &m_rootNode); + + fora(i, pGeomArray) + { + aBufferNodes.clearFast(); + + getNodeByName("Vertices", &aBufferNodes, pGeomArray[i]); + pProp = &aBufferNodes[0]->aProps[0]; + + assert(pProp->cType == 'd'); + + for(int j = 0; j < pProp->uArrSize; j += 3) + { + float3 vertex((float)pProp->data.pd[j], (float)pProp->data.pd[j + 1], (float)pProp->data.pd[j + 2]); + + if(j == 0 && i == 0) + { + vmin = vmax = vertex; + } + else + { + vmin = SMVectorMin(vmin, vertex); + vmax = SMVectorMax(vmax, vertex); + } + } + } + + pGeomArray.clearFast(); + getNodeByName("GlobalSettings", &pGeomArray, &m_rootNode); + + FBXNodeRecord *pGlobal = pGeomArray[0]; + + pGeomArray.clearFast(); + getNodeByName("P", &pGeomArray, pGlobal); + + fora(i, pGeomArray) + { + assert(pGeomArray[i]->aProps[0].cType == 'S'); + + if(strcmp(pGeomArray[i]->aProps[0].data.pc, "UnitScaleFactor") == 0) + { + assert(pGeomArray[i]->aProps[4].cType == 'D'); + m_fUnitScaleFactor = (float)pGeomArray[i]->aProps[4].data.d; + break; + } + } + + pModelInfo->vDimensions = (vmax - vmin) / 100.0f * m_fUnitScaleFactor; +} + +bool XMETHODCALLTYPE CModelLoader::open(const char *szFileName) +{ + assert(!m_pCurrentFile && "File already opened!"); + if(m_pCurrentFile) + { + return(false); + } + + m_pCurrentFile = m_pFileSystem->openFile(szFileName); + if(!m_pCurrentFile) + { + return(false); + } + + m_sFileName = szFileName; + + FBXHeader hdr = {}; + + m_pCurrentFile->readBin(&hdr, sizeof(hdr)); + + if(strcmp(hdr.szMagic, FBX_MAGIC) != 0) + { + LogError("Invalid FBX magick\n"); + mem_release(m_pCurrentFile); + return(false); + } + + m_isOldVer = false; + + if(hdr.uVersion < FBX_VERSION) + { + //LogError("Unsupported FBX version %u\n", hdr.uVersion); + //mem_release(m_pCurrentFile); + //return(false); + m_isOldVer = true; + } + + FBXNodeRecordHeader recHdr; + + while(!m_pCurrentFile->isEOF()) + { + if(m_isOldVer) + { + FBXNodeRecordHeaderOld recHdrOld; + m_pCurrentFile->readBin(&recHdrOld, sizeof(recHdrOld)); + + recHdr.u8NameLen = recHdrOld.u8NameLen; + recHdr.uEndOffset = recHdrOld.uEndOffset; + recHdr.uNumProperties = recHdrOld.uNumProperties; + recHdr.uPropertyListLen = recHdrOld.uPropertyListLen; + } + else + { + m_pCurrentFile->readBin(&recHdr, sizeof(recHdr)); + } + + if(isNullNode(&recHdr)) + { + break; + } + + FBXNodeRecord &rec = m_rootNode.aNodes[m_rootNode.aNodes.size()]; + rec.hdr = recHdr; + + if(!readNode(&rec)) + { + close(); + return(false); + } + } + + printNode(&m_rootNode, 0); + + XModelInfo x; + + getInfo(&x); + + return(true); +} + +XMODELTYPE XMETHODCALLTYPE CModelLoader::getType() const +{ + return(XMT_STATIC);//return((m_hdr.iFlags & MODEL_FLAG_STATIC) ? XMT_STATIC : XMT_ANIMATED); +} + +bool XMETHODCALLTYPE CModelLoader::loadAsStatic(IXResourceModelStatic *pResource) +{ + //if(!loadGeneric(pResource)) + //{ + // return(false); + //} + + pResource->setPrimitiveTopology(XPT_TRIANGLELIST); + + Array<FBXNodeRecord*> aNodes; + Array<FBXNodeRecord*> aBufferNodes; + + getNodeByName("Model", &aNodes); + + const size_t lodeSize = 10; + int32_t aLodIndex[lodeSize] = {}; + + Array<Array<FBXNodeRecord*>> aLodModels; + + fora(i, aNodes) + { + int index = 0; + if(aNodes[i]->aProps.size() > 1 && aNodes[i]->aProps[1].cType == 'S') + { + FBXPropertyRecord &props = aNodes[i]->aProps[1]; + size_t len = strlen(props.data.pc); + // len > strlen(LOD) word + // после props.data.pc есть еще одна строка (размер strlen(props.data.pc) и props.uArrSize будет разным) + if(len > 5) + { + if( + tolower(props.data.pc[len - 4]) == 'l' && + tolower(props.data.pc[len - 3]) == 'o' && + tolower(props.data.pc[len - 2]) == 'd' && + isdigit(props.data.pc[len - 1])) + { + index = props.data.pc[len - 1] - '0'; + } + } + } + + aLodModels[index].push_back(aNodes[i]); + } + + aBufferNodes.clearFast(); + getNodeByName("Pose", &aBufferNodes, &m_rootNode); + + FBXNodeRecord *pPose = aBufferNodes.size() ? aBufferNodes[0] : NULL; + + + aBufferNodes.clearFast(); + getNodeByName("GlobalSettings", &aBufferNodes, &m_rootNode); + + FBXNodeRecord *pGlobal = aBufferNodes[0]; + + aBufferNodes.clearFast(); + getNodeByName("P", &aBufferNodes, pGlobal); + + fora(i, aBufferNodes) + { + assert(aBufferNodes[i]->aProps[0].cType == 'S'); + + if(strcmp(aBufferNodes[i]->aProps[0].data.pc, "UnitScaleFactor") == 0) + { + assert(aBufferNodes[i]->aProps[4].cType == 'D'); + m_fUnitScaleFactor = (float)aBufferNodes[i]->aProps[4].data.d; + break; + } + } + + Array<Array<XResourceModelStaticVertex>> aVerteces; + Array<Array<uint32_t>> aIndeces; + + Array<UINT> aMaterialMap; + Array<FBXNodeRecord*> aMaterials; + + getNodeByName("Material", &aMaterials, &m_rootNode); + + pResource->setMaterialCount(aMaterials.size(), 1); + + char szDirName[512]; + char szFileName[256]; + + strcpy(szDirName, m_sFileName.c_str()); + dirname(szDirName); + szDirName[strlen(szDirName) - 1] = 0; + + fora(i, aMaterials) + { + FBXNodeRecord *pTextureNode = getChildNode("Texture", aMaterials[i]); + + if(pTextureNode) + { + aBufferNodes.clearFast(); + getNodeByName("FileName", &aBufferNodes, pTextureNode); + FBXPropertyRecord *pProp = &aBufferNodes[0]->aProps[0]; + + assert(pProp->cType == 'S'); + + sprintf(szFileName, "%s_%s", basename(szDirName), basename(pProp->data.pc)); + + UINT uIndex = 0; + int iLastPos = -1; + + while(szFileName[uIndex++]) + { + if(szFileName[uIndex] == '.') + { + iLastPos = uIndex; + } + } + + if(iLastPos > 0) + { + szFileName[iLastPos] = 0; + } + + pResource->setMaterial(i, 0, szFileName); + } + } + + m_sFileName.release(); + + VerticesData vd = {}; + + FBXNodeRecord *pMaterialNode; + UINT uMaterialCounter = 0; + + fora(i, aLodModels) + { + aVerteces.clearFast(); + aIndeces.clearFast(); + + fora(j, aLodModels[i]) + { + FBXNodeRecord *pModel = aLodModels[i][j]; + + uMaterialCounter = 0; + aMaterialMap.clearFast(); + while((pMaterialNode = getChildNode("Material", pModel, uMaterialCounter++))) + { + aMaterialMap.push_back(aMaterials.indexOf(pMaterialNode)); + } + + FBXNodeRecord *pMatrixNode = NULL; + + if(pPose) + { + aBufferNodes.clearFast(); + getNodeByName("PoseNode", &aBufferNodes, pPose); + + fora(k, aBufferNodes) + { + if(pModel->aProps[0].data.i64 == aBufferNodes[k]->aNodes[0].aProps[0].data.i64) + { + pMatrixNode = &(aBufferNodes[k]->aNodes[1]); + } + } + } + + vd.pMatrixNode = pMatrixNode; + vd.qPreRotation = SMQuaternion(); + + aBufferNodes.clearFast(); + getNodeByName("Properties70", &aBufferNodes, pModel); + if(aBufferNodes.size()) + { + FBXNodeRecord *pModelPropsNode = aBufferNodes[0]; + + aBufferNodes.clearFast(); + getNodeByName("P", &aBufferNodes, pModelPropsNode); + + fora(k, aBufferNodes) + { + if(!strcmp(aBufferNodes[k]->aProps[0].data.pc, "PreRotation")) + { + float fX = SMToRadian(aBufferNodes[k]->aProps[4].data.d); + float fY = SMToRadian(aBufferNodes[k]->aProps[5].data.d); + float fZ = SMToRadian(aBufferNodes[k]->aProps[6].data.d); + vd.qPreRotation = SMQuaternion(fX, 'x') * SMQuaternion(fY, 'y') * SMQuaternion(fZ, 'z'); + break; + } + } + } + + FBXNodeRecord *pGeomNode = getChildNode("Geometry", pModel); + + if(!pGeomNode) + { + continue; + } + + aBufferNodes.clearFast(); + getNodeByName("Vertices", &aBufferNodes, pGeomNode); + FBXPropertyRecord *pProp = &aBufferNodes[0]->aProps[0]; + + vd.pVerteces = pProp->data.pd; + + aBufferNodes.clearFast(); + getNodeByName("PolygonVertexIndex", &aBufferNodes, pGeomNode); + pProp = &aBufferNodes[0]->aProps[0]; + + vd.pIndexes = pProp->data.pi; + vd.uSizeIndexes = pProp->uArrSize; + + aBufferNodes.clearFast(); + getNodeByName("LayerElementUV", &aBufferNodes, pGeomNode); + if(!aBufferNodes.size()) + { + LogError("Model " COLOR_LCYAN "%s" COLOR_LRED " has no texture coords\n", pModel->aProps[1].data.pc); + continue; + } + + FBXNodeRecord *pLayerElUV = aBufferNodes[0]; + + { + aBufferNodes.clearFast(); + getNodeByName("MappingInformationType", &aBufferNodes, pLayerElUV); + EnumReflector::Enumerator item = EnumReflector::Get<FBX_MAPPING_INFORMATION_TYPE>().find(aBufferNodes[0]->aProps[0].data.pc); + assert(item.isValid()); + vd.uvMappingInfoType = (FBX_MAPPING_INFORMATION_TYPE)item.getValue(); + } + + { + aBufferNodes.clearFast(); + getNodeByName("ReferenceInformationType", &aBufferNodes, pLayerElUV); + EnumReflector::Enumerator item = EnumReflector::Get<FBX_REFERENCE_INFORMATION_TYPE>().find(aBufferNodes[0]->aProps[0].data.pc); + assert(item.isValid()); + vd.uvRefInfoType = (FBX_REFERENCE_INFORMATION_TYPE)item.getValue(); + } + + aBufferNodes.clearFast(); + getNodeByName("UV", &aBufferNodes, pLayerElUV); + pProp = &aBufferNodes[0]->aProps[0]; + assert(pProp->cType == 'd'); + + vd.pUV = pProp->data.pd; + vd.uSizeUV = pProp->uArrSize; + + aBufferNodes.clearFast(); + getNodeByName("UVIndex", &aBufferNodes, pLayerElUV); + + vd.pUVIndexes = NULL; + vd.uSizeUVIndexes = NULL; + + if(aBufferNodes.size()) + { + pProp = &aBufferNodes[0]->aProps[0]; + assert(pProp->cType == 'i'); + + vd.pUVIndexes = pProp->data.pi; + vd.uSizeUVIndexes = pProp->uArrSize; + } + + aBufferNodes.clearFast(); + getNodeByName("LayerElementNormal", &aBufferNodes, pGeomNode); + FBXNodeRecord *pLayerNormals = aBufferNodes[0]; + + { + aBufferNodes.clearFast(); + getNodeByName("MappingInformationType", &aBufferNodes, pLayerNormals); + EnumReflector::Enumerator item = EnumReflector::Get<FBX_MAPPING_INFORMATION_TYPE>().find(aBufferNodes[0]->aProps[0].data.pc); + assert(item.isValid()); + vd.normalMappingInfoType = (FBX_MAPPING_INFORMATION_TYPE)item.getValue(); + } + + { + aBufferNodes.clearFast(); + getNodeByName("ReferenceInformationType", &aBufferNodes, pLayerNormals); + EnumReflector::Enumerator item = EnumReflector::Get<FBX_REFERENCE_INFORMATION_TYPE>().find(aBufferNodes[0]->aProps[0].data.pc); + assert(item.isValid()); + vd.normalRefInfoType = (FBX_REFERENCE_INFORMATION_TYPE)item.getValue(); + } + + aBufferNodes.clearFast(); + getNodeByName("Normals", &aBufferNodes, pLayerNormals); + pProp = &aBufferNodes[0]->aProps[0]; + assert(pProp->cType == 'd'); + + vd.pNormal = pProp->data.pd; + + aBufferNodes.clearFast(); + getNodeByName("NormalsIndex", &aBufferNodes, pLayerNormals); + + vd.pNormalIndexes = NULL; + + if(aBufferNodes.size()) + { + pProp = &aBufferNodes[0]->aProps[0]; + assert(pProp->cType == 'i'); + + vd.pNormalIndexes = pProp->data.pi; + } + + aBufferNodes.clearFast(); + getNodeByName("LayerElementMaterial", &aBufferNodes, pGeomNode); + FBXNodeRecord *pLayerMaterial = aBufferNodes[0]; + + { + aBufferNodes.clearFast(); + getNodeByName("MappingInformationType", &aBufferNodes, pLayerMaterial); + EnumReflector::Enumerator item = EnumReflector::Get<FBX_MAPPING_INFORMATION_TYPE>().find(aBufferNodes[0]->aProps[0].data.pc); + assert(item.isValid()); + vd.materialMappingInfoType = (FBX_MAPPING_INFORMATION_TYPE)item.getValue(); + } + + { + aBufferNodes.clearFast(); + getNodeByName("ReferenceInformationType", &aBufferNodes, pLayerMaterial); + EnumReflector::Enumerator item = EnumReflector::Get<FBX_REFERENCE_INFORMATION_TYPE>().find(aBufferNodes[0]->aProps[0].data.pc); + assert(item.isValid()); + vd.materialRefInfoType = (FBX_REFERENCE_INFORMATION_TYPE)item.getValue(); + } + + aBufferNodes.clearFast(); + getNodeByName("Materials", &aBufferNodes, pLayerMaterial); + pProp = &aBufferNodes[0]->aProps[0]; + assert(pProp->cType == 'i'); + + vd.pMaterials = pProp->data.pi; + vd.pMaterialMapArray = &aMaterialMap; + + getVertices(vd, &aVerteces, &aIndeces); + } + + if(aVerteces.size()) + { + UINT *s = (UINT*)alloca(aVerteces.size() * sizeof(UINT)); + UINT *s2 = (UINT*)alloca(aVerteces.size() * sizeof(UINT)); + UINT uIndex = 0; + + fora(j, aVerteces) + { + if(aVerteces[j].size() > 0) + { + s[uIndex] = aVerteces[j].size(); + s2[uIndex] = aIndeces[j].size(); + ++uIndex; + } + } + + UINT uLodIndex = pResource->addLod(uIndex, s, s2); + uIndex = 0; + + fora(j, aVerteces) + { + if(aVerteces[j].size() > 0) + { + auto sub = pResource->getSubset(uLodIndex, uIndex); + + sub->iMaterialID = j; + memcpy(sub->pVertices, aVerteces[j], aVerteces[j].size() * sizeof(XResourceModelStaticVertex)); + memcpy(sub->pIndices, aIndeces[j], aIndeces[j].size() * sizeof(UINT)); + + ++uIndex; + } + } + + break; + } + } + + return(true); +} + +bool XMETHODCALLTYPE CModelLoader::loadAsAnimated(IXResourceModelAnimated *pResource) +{ + return(false); +} + +void XMETHODCALLTYPE CModelLoader::close() +{ + m_rootNode.aNodes.clearFast(); + m_rootNode.aProps.clearFast(); + mem_release(m_pCurrentFile); +} + +bool CModelLoader::loadGeneric(IXResourceModel *pResource) +{ + return(false); +} + +bool CModelLoader::readNode(FBXNodeRecord *pNode) +{ + assert(pNode); + + m_pCurrentFile->readBin(pNode->szName, pNode->hdr.u8NameLen); + + pNode->szName[pNode->hdr.u8NameLen] = 0; + + pNode->aProps.reserve(pNode->hdr.uNumProperties); + + for(UINT i = 0; i < pNode->hdr.uNumProperties; ++i) + { + FBXPropertyRecord &prop = pNode->aProps[i]; + m_pCurrentFile->readBin(&prop.cType, sizeof(prop.cType)); + + if(isupper(prop.cType)) + { + switch(prop.cType) + { + case 'Y': + m_pCurrentFile->readBin(&prop.data.i16, sizeof(prop.data.i16)); + break; + case 'C': + m_pCurrentFile->readBin(&prop.data.b, sizeof(prop.data.b)); + break; + case 'I': + m_pCurrentFile->readBin(&prop.data.i, sizeof(prop.data.i)); + break; + case 'F': + m_pCurrentFile->readBin(&prop.data.f, sizeof(prop.data.f)); + break; + case 'D': + m_pCurrentFile->readBin(&prop.data.d, sizeof(prop.data.d)); + break; + case 'L': + m_pCurrentFile->readBin(&prop.data.i64, sizeof(prop.data.i64)); + break; + case 'S': + m_pCurrentFile->readBin(&prop.uArrSize, sizeof(prop.uArrSize)); + prop.data.pc = new char[prop.uArrSize + 1]; + m_pCurrentFile->readBin(prop.data.pc, prop.uArrSize); + prop.data.pc[prop.uArrSize] = 0; + break; + case 'R': + m_pCurrentFile->readBin(&prop.uArrSize, sizeof(prop.uArrSize)); + prop.data.pRaw = new byte[prop.uArrSize]; + m_pCurrentFile->readBin(prop.data.pRaw, prop.uArrSize); + break; + default: + LogError("Unsupported field type value %c\n", prop.cType); + return(false); + } + } + else + { + FBXArrayHeader ahdr = {}; + m_pCurrentFile->readBin(&ahdr, sizeof(ahdr)); + + prop.uArrSize = ahdr.uLength; + + Array<byte> aBytes; + + void *pOut = NULL; + size_t size = 0; + size_t outSize = 0; + + if(ahdr.uEncoding != FBXCT_UNCOMPRESSED && ahdr.uEncoding != FBXCT_DEFLATE) + { + LogError("Unsupported compression type %u\n", ahdr.uEncoding); + return(false); + } + + switch(prop.cType) + { + case 'f': + pOut = prop.data.pf = new float[prop.uArrSize]; + size = sizeof(*prop.data.pf) * prop.uArrSize; + break; + case 'd': + pOut = prop.data.pd = new double[prop.uArrSize]; + size = sizeof(*prop.data.pd) * prop.uArrSize; + break; + case 'l': + pOut = prop.data.pi64 = new int64_t[prop.uArrSize]; + size = sizeof(*prop.data.pi64) * prop.uArrSize; + break; + case 'i': + pOut = prop.data.pi = new int32_t[prop.uArrSize]; + size = sizeof(*prop.data.pi) * prop.uArrSize; + break; + case 'b': + pOut = prop.data.pb = new uint8_t[prop.uArrSize]; + size = sizeof(*prop.data.pb) * prop.uArrSize; + break; + default: + LogError("Unsupported field type value %c\n", prop.cType); + return(false); + } + + if(ahdr.uEncoding == FBXCT_UNCOMPRESSED) + { + m_pCurrentFile->readBin(pOut, size); + } + + if(ahdr.uEncoding == FBXCT_DEFLATE) + { + aBytes.resizeFast(ahdr.uCompressedLength); + m_pCurrentFile->readBin(aBytes, ahdr.uCompressedLength); + + libdeflate_decompressor *pDecompressor = libdeflate_alloc_decompressor(); + // 2 first bytes is a zlib trash header + libdeflate_result res = libdeflate_deflate_decompress(pDecompressor, aBytes + 2, aBytes.size() - 2, pOut, size, &outSize); + libdeflate_free_decompressor(pDecompressor); + + if(res != LIBDEFLATE_SUCCESS) + { + LogError("libdeflate_deflate_decompress(): returned error %d\n", res); + } + } + } + } + + FBXNodeRecordHeader hdr; + + while(m_pCurrentFile->getPos() < pNode->hdr.uEndOffset) + { + if(m_isOldVer) + { + FBXNodeRecordHeaderOld hdrOld; + m_pCurrentFile->readBin(&hdrOld, sizeof(hdrOld)); + + hdr.u8NameLen = hdrOld.u8NameLen; + hdr.uEndOffset = hdrOld.uEndOffset; + hdr.uNumProperties = hdrOld.uNumProperties; + hdr.uPropertyListLen = hdrOld.uPropertyListLen; + } + else + { + m_pCurrentFile->readBin(&hdr, sizeof(hdr)); + } + + if(isNullNode(&hdr)) + { + break; + } + + FBXNodeRecord &rec = pNode->aNodes[pNode->aNodes.size()]; + rec.hdr = hdr; + if(!readNode(&rec)) + { + return(false); + } + } + + return(true); +} + +bool CModelLoader::isNullNode(FBXNodeRecordHeader *pHdr) +{ + return(pHdr->uPropertyListLen == 0 && pHdr->uNumProperties == 0 && pHdr->uEndOffset == 0 && pHdr->u8NameLen == 0); +} + +void CModelLoader::printNode(FBXNodeRecord *pNode, UINT uLevel) +{ + for(UINT i = 0; i < uLevel; ++i) + { + printf("\t"); + } + + printf("%s:", pNode->szName); + + fora(i, pNode->aProps) + { + const FBXPropertyRecord &prop = pNode->aProps[i]; + + switch(prop.cType) + { + case 'f': + printf(" array<float>(%u)", prop.uArrSize); + break; + case 'd': + printf(" array<double>(%u)", prop.uArrSize); + break; + case 'l': + printf(" array<int64>(%u)", prop.uArrSize); + break; + case 'i': + printf(" array<int32>(%u)", prop.uArrSize); + break; + case 'b': + printf(" array<uint8>(%u)", prop.uArrSize); + break; + case 'S': + printf(" \"%s\"", prop.data.pc); + break; + case 'R': + printf(" array<byte>(%u)", prop.uArrSize); + break; + case 'Y': + printf(" Y:%hd", prop.data.i16); + break; + case 'C': + printf(" C:%s", (prop.data.b ? "true" : "false")); + break; + case 'I': + printf(" I:%d", prop.data.i); + break; + case 'F': + printf(" F:%f", prop.data.f); + break; + case 'D': + printf(" D:%f", prop.data.d); + break; + case 'L': + printf(" L:%ld", prop.data.i64); + break; + default: + break; + } + } + + printf("\n"); + + fora(i, pNode->aNodes) + { + printNode(&pNode->aNodes[i], uLevel + 1); + } +} + +void CModelLoader::getNodeByName(const char *szName, Array<FBXNodeRecord*> *pArray, FBXNodeRecord *pNode) +{ + if(!pNode) + { + pNode = &m_rootNode; + } + + fora(i, pNode->aNodes) + { + getNodeByName(szName, pArray, &pNode->aNodes[i]); + } + + if(!strcmp(pNode->szName, szName)) + { + pArray->push_back(pNode); + } +} + +FBXNodeRecord *CModelLoader::getChildNode(const char *szName, const FBXNodeRecord *pNode, UINT uIndex) +{ + assert(pNode->aProps[0].cType == 'L'); + + int64_t id = pNode->aProps[0].data.i64; + + Array<FBXNodeRecord*> array; + + getNodeByName(szName, &array); + + if(!array.size()) + { + return(NULL); + } + + fora(i, m_rootNode.aNodes) + { + if(!strcmp(m_rootNode.aNodes[i].szName, "Connections")) + { + fora(j, m_rootNode.aNodes[i].aNodes) + { + FBXNodeRecord &node = m_rootNode.aNodes[i].aNodes[j]; + + assert(node.aProps[2].cType == 'L'); + + if(node.aProps[2].data.i64 == id) + { + fora(k, array) + { + assert(node.aProps[1].cType == 'L'); + assert(array[k]->aProps[0].cType == 'L'); + + if(array[k]->aProps[0].data.i64 == node.aProps[1].data.i64) + { + if(uIndex-- == 0) + { + return(array[k]); + } + } + } + } + } + } + } + return(NULL); +} + +UINT CModelLoader::getCountIndexes(int32_t *pIndexes, uint32_t uSize) +{ + UINT uResult = 0; + int32_t *pIterator = pIndexes; + + Array<int32_t> array; + + while(pIterator < pIndexes + uSize) + { + array.push_back(*pIterator); + + if(*pIterator < 0) + { + array[array.size() - 1] = -(*pIterator) - 1; + + //assert(array.size() == 3 || array.size() == 4); + + fora(i, array) + { + if(array[i] == array[(i + 1) % il]) + { + array.erase(i); + break; + } + } + + uResult += (array.size() - 2) * 3; + + array.clearFast(); + } + ++pIterator; + } + return(uResult); +} + +void CModelLoader::getVertices(const VerticesData &verticesData, Array<Array<XResourceModelStaticVertex>> *pOutArray, Array<Array<uint32_t>> *pOutIndices) +{ + struct Index + { + int32_t iValue; + UINT uIndex; + }; + + XResourceModelStaticVertex vtx = {}; + const int32_t *pIndexes = verticesData.pIndexes; + const uint32_t uSize = verticesData.uSizeIndexes; + const int32_t *pIterator = pIndexes; + const double *pVerteces = verticesData.pVerteces; + + uint32_t uStartVertex = 0; + + Array<Index> array; + + Array<UINT> uUVIndex; + Array<UINT> uNormalsIndex; + UINT uPolyCounter = 0; + UINT uMaterialIndex = 0; + + SMMATRIX matrix; + + if(verticesData.pMatrixNode) + { + double *m = verticesData.pMatrixNode->aProps[0].data.pd; + + matrix = SMMATRIX(m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[12], m[13], m[14], m[15]); + + float fDet = SMMatrixDeterminant(matrix); + if(fDet < 0.0f) + { + matrix.r[0] = -matrix.r[0]; + } + } + + matrix = matrix * verticesData.qPreRotation.Conjugate().GetMatrix(); + + while(pIterator < pIndexes + uSize) + { + array.push_back({*pIterator, pIterator - pIndexes}); + + if(*pIterator < 0) + { + array[array.size() - 1].iValue = -(*pIterator) - 1; + + fora(i, array) + { + if(array[i].iValue == array[(i + 1) % il].iValue) + { + array.erase(i); + break; + } + } + + fora(i, array) + { + switch(verticesData.uvMappingInfoType) + { + case FBX_ByPolygonVertex: + uUVIndex[i] = array[i].uIndex; + break; + case FBX_ByVertex: + uUVIndex[i] = array[i].iValue; + break; + default: + assert(!"Unsupported maping information type"); + break; + } + + switch(verticesData.uvRefInfoType) + { + case FBX_Direct: + // No need actions + break; + case FBX_IndexToDirect: + uUVIndex[i] = verticesData.pUVIndexes[uUVIndex[i]]; + break; + + default: + assert(!"Unsupported reference information type"); + break; + } + + switch(verticesData.normalMappingInfoType) + { + case FBX_ByPolygon: + uNormalsIndex[i] = uPolyCounter; + break; + case FBX_ByPolygonVertex: + uNormalsIndex[i] = array[i].uIndex; + break; + case FBX_ByVertex: + uNormalsIndex[i] = array[i].iValue; + break; + default: + assert(!"Unsupported maping information type"); + break; + } + + switch(verticesData.normalRefInfoType) + { + case FBX_Direct: + // No need actions + break; + case FBX_IndexToDirect: + uNormalsIndex[i] = verticesData.pNormalIndexes[uNormalsIndex[i]]; + break; + + default: + assert(!"Unsupported reference information type"); + break; + } + } + + switch(verticesData.materialMappingInfoType) + { + case FBX_ByPolygon: + uMaterialIndex = uPolyCounter; + break; + case FBX_AllSame: + uMaterialIndex = 0; + break; + default: + assert(!"Unsupported maping information type"); + break; + } + + switch(verticesData.materialRefInfoType) + { + case FBX_Direct: + // No need actions + break; + case FBX_IndexToDirect: + uMaterialIndex = verticesData.pMaterials[uMaterialIndex]; + break; + + default: + assert(!"Unsupported reference information type"); + break; + } + + uMaterialIndex = (*verticesData.pMaterialMapArray)[uMaterialIndex]; + + uStartVertex = (*pOutArray)[uMaterialIndex].size(); + fora(i, array) + { + vtx.vPos = matrix * float3_t(pVerteces[array[i].iValue * 3], pVerteces[array[i].iValue * 3 + 1], pVerteces[array[i].iValue * 3 + 2]) / 100.0f * m_fUnitScaleFactor; + vtx.vTex = float2_t(verticesData.pUV[uUVIndex[i] * 2], 1.0f - verticesData.pUV[uUVIndex[i] * 2 + 1]); + vtx.vNorm = float3_t(verticesData.pNormal[uNormalsIndex[i] * 3], verticesData.pNormal[uNormalsIndex[i] * 3 + 1], verticesData.pNormal[uNormalsIndex[i] * 3 + 2]); + (*pOutArray)[uMaterialIndex].push_back(vtx); + } + + // (0-5) 012 023 034 045 ... + for(UINT i = 1, l = array.size() - 1; i < l; ++i) + { + (*pOutIndices)[uMaterialIndex].push_back(uStartVertex); + (*pOutIndices)[uMaterialIndex].push_back(uStartVertex + i); + (*pOutIndices)[uMaterialIndex].push_back(uStartVertex + i + 1); + } + + array.clearFast(); + ++uPolyCounter; + } + ++pIterator; + } +} \ No newline at end of file diff --git a/source/fbxplugin/ModelLoader.h b/source/fbxplugin/ModelLoader.h new file mode 100644 index 0000000000000000000000000000000000000000..d8a81986c336b7501608775e923852a87f5ad248 --- /dev/null +++ b/source/fbxplugin/ModelLoader.h @@ -0,0 +1,88 @@ +#ifndef __MODELLOADER_H +#define __MODELLOADER_H + +#include <xcommon/IXModelLoader.h> +#include <xcommon/IFileSystem.h> +#include "fbx.h" + +class CModelLoader final: public IXUnknownImplementation<IXModelLoader> +{ +public: + CModelLoader(IFileSystem *pFileSystem); + XIMPLEMENT_VERSION(IXMODELLOADER_VERSION); + + UINT XMETHODCALLTYPE getExtCount() const override; + const char* XMETHODCALLTYPE getExt(UINT uIndex) const override; + const char* XMETHODCALLTYPE getExtText(UINT uIndex) const override; + const char* XMETHODCALLTYPE getAuthor() const override; + const char* XMETHODCALLTYPE getCopyright() const override; + const char* XMETHODCALLTYPE getDescription() const override; + + bool XMETHODCALLTYPE open(const char *szFileName) override; + XMODELTYPE XMETHODCALLTYPE getType() const override; + bool XMETHODCALLTYPE loadAsStatic(IXResourceModelStatic *pResource) override; + bool XMETHODCALLTYPE loadAsAnimated(IXResourceModelAnimated *pResource) override; + void XMETHODCALLTYPE getInfo(XModelInfo *pModelInfo) override; + void XMETHODCALLTYPE close() override; + + + bool loadGeneric(IXResourceModel *pResource); + +private: + bool readNode(FBXNodeRecord *pNode); + bool isNullNode(FBXNodeRecordHeader *pHdr); + void printNode(FBXNodeRecord *pNode, UINT uLevel); + void getNodeByName(const char *szName, Array<FBXNodeRecord*> *pArray, FBXNodeRecord *pNode = NULL); + + FBXNodeRecord *getChildNode(const char *szName, const FBXNodeRecord *pNode, UINT uIndex = 0); + UINT getCountIndexes(int32_t *pIndexes, uint32_t uSize); + + struct VerticesData + { + int32_t *pIndexes; + uint32_t uSizeIndexes; + + double *pVerteces; + + FBX_MAPPING_INFORMATION_TYPE uvMappingInfoType; + FBX_REFERENCE_INFORMATION_TYPE uvRefInfoType; + + double *pUV; + uint32_t uSizeUV; + + int32_t *pUVIndexes; + uint32_t uSizeUVIndexes; + + double *pNormal; + int32_t *pNormalIndexes; + + FBX_MAPPING_INFORMATION_TYPE normalMappingInfoType; + FBX_REFERENCE_INFORMATION_TYPE normalRefInfoType; + + int32_t *pMaterials; + + FBX_MAPPING_INFORMATION_TYPE materialMappingInfoType; + FBX_REFERENCE_INFORMATION_TYPE materialRefInfoType; + + const Array<UINT> *pMaterialMapArray; + + FBXNodeRecord *pMatrixNode; + + SMQuaternion qPreRotation; + }; + + void getVertices(const VerticesData &verticesData, Array<Array<XResourceModelStaticVertex>> *pOutArray, Array<Array<uint32_t>> *pOutIndices); + + IFileSystem *m_pFileSystem; + IFile *m_pCurrentFile = NULL; + + float m_fUnitScaleFactor = 1.0f; + + String m_sFileName; + + bool m_isOldVer = false; + + FBXNodeRecord m_rootNode; +}; + +#endif diff --git a/source/fbxplugin/dllmain.cpp b/source/fbxplugin/dllmain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..955d268b01c7c34e230e19cda81db4babeee39ba --- /dev/null +++ b/source/fbxplugin/dllmain.cpp @@ -0,0 +1,24 @@ + +/*********************************************************** +Copyright © Ivan Dokunov, Evgeny Danilovich, 2025 +See the license in LICENSE +***********************************************************/ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch(ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/source/fbxplugin/fbx.h b/source/fbxplugin/fbx.h new file mode 100644 index 0000000000000000000000000000000000000000..cadf5911665560dc6408ac38cb6f46d3f4afb2df --- /dev/null +++ b/source/fbxplugin/fbx.h @@ -0,0 +1,126 @@ +#ifndef __FBX_H +#define __FBX_H + +#include <gdefines.h> + +#define FBX_MAGIC "Kaydara FBX Binary " +#define FBX_VERSION 7500 + +enum FBX_COMPRESSION_TYPE +{ + FBXCT_UNCOMPRESSED = 0, + FBXCT_DEFLATE = 1 +}; + +XENUM(FBX_MAPPING_INFORMATION_TYPE, + FBX_ByPolygon, + FBX_ByPolygonVertex, + FBX_ByVertex, + FBX_ByVertice = FBX_ByVertex, + FBX_ByEdge, + FBX_AllSame +); + +XENUM(FBX_REFERENCE_INFORMATION_TYPE, + FBX_Direct, + FBX_IndexToDirect, + FBX_Index = FBX_IndexToDirect +); + +#pragma pack(push, 1) + +struct FBXHeader +{ + char szMagic[21]; + uint16_t _unknown; + uint32_t uVersion; +}; + +struct FBXNodeRecordHeaderOld +{ + uint32_t uEndOffset; + uint32_t uNumProperties; + uint32_t uPropertyListLen; + uint8_t u8NameLen; +}; + +struct FBXNodeRecordHeader +{ + uint64_t uEndOffset; + uint64_t uNumProperties; + uint64_t uPropertyListLen; + uint8_t u8NameLen; +}; + +struct FBXPropertyRecord +{ + char cType; + union + { + int16_t i16; + uint8_t b; + int32_t i; + float f; + double d; + int64_t i64; + + float *pf; + double *pd; + int64_t *pi64; + int32_t *pi; + uint8_t *pb; + + char *pc; + byte *pRaw; + } data; + uint32_t uArrSize; + + ~FBXPropertyRecord() + { + switch(cType) + { + case 'f': + mem_delete_a(data.pf); + break; + case 'd': + mem_delete_a(data.pd); + break; + case 'l': + mem_delete_a(data.pi64); + break; + case 'i': + mem_delete_a(data.pi); + break; + case 'b': + mem_delete_a(data.pb); + break; + case 'S': + mem_delete_a(data.pc); + break; + case 'R': + mem_delete_a(data.pRaw); + break; + default: + break; + } + } +}; + +struct FBXArrayHeader +{ + uint32_t uLength; + uint32_t uEncoding; + uint32_t uCompressedLength; +}; + +struct FBXNodeRecord +{ + FBXNodeRecordHeader hdr; + char szName[256]; + Array<FBXPropertyRecord> aProps; + Array<FBXNodeRecord> aNodes; +}; + +#pragma pack(pop) + +#endif diff --git a/source/fbxplugin/libdeflate.c b/source/fbxplugin/libdeflate.c new file mode 100644 index 0000000000000000000000000000000000000000..e421d7911345c57cee506a375d2950a1d552643c --- /dev/null +++ b/source/fbxplugin/libdeflate.c @@ -0,0 +1,4193 @@ +// ofbx changes : removed unused code, single .h and .c +/* + * Copyright 2016 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + * + * --------------------------------------------------------------------------- + * + * This is a highly optimized DEFLATE decompressor. It is much faster than + * vanilla zlib, typically well over twice as fast, though results vary by CPU. + * + * Why this is faster than vanilla zlib: + * + * - Word accesses rather than byte accesses when reading input + * - Word accesses rather than byte accesses when copying matches + * - Faster Huffman decoding combined with various DEFLATE-specific tricks + * - Larger bitbuffer variable that doesn't need to be refilled as often + * - Other optimizations to remove unnecessary branches + * - Only full-buffer decompression is supported, so the code doesn't need to + * support stopping and resuming decompression. + * - On x86_64, a version of the decompression routine is compiled with BMI2 + * instructions enabled and is used automatically at runtime when supported. + */ + +/* + * lib_common.h - internal header included by all library code + */ + +#ifndef LIB_LIB_COMMON_H +#define LIB_LIB_COMMON_H + +#ifdef LIBDEFLATE_H + /* + * When building the library, LIBDEFLATEAPI needs to be defined properly before + * including libdeflate.h. + */ +# error "lib_common.h must always be included before libdeflate.h" +#endif + +#if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__)) +# define LIBDEFLATE_EXPORT_SYM __declspec(dllexport) +#elif defined(__GNUC__) +# define LIBDEFLATE_EXPORT_SYM __attribute__((visibility("default"))) +#else +# define LIBDEFLATE_EXPORT_SYM +#endif + +/* + * On i386, gcc assumes that the stack is 16-byte aligned at function entry. + * However, some compilers (e.g. MSVC) and programming languages (e.g. Delphi) + * only guarantee 4-byte alignment when calling functions. This is mainly an + * issue on Windows, but it has been seen on Linux too. Work around this ABI + * incompatibility by realigning the stack pointer when entering libdeflate. + * This prevents crashes in SSE/AVX code. + */ +#if defined(__GNUC__) && defined(__i386__) +# define LIBDEFLATE_ALIGN_STACK __attribute__((force_align_arg_pointer)) +#else +# define LIBDEFLATE_ALIGN_STACK +#endif + +#define LIBDEFLATEAPI LIBDEFLATE_EXPORT_SYM LIBDEFLATE_ALIGN_STACK + +/* + * common_defs.h + * + * Copyright 2016 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + */ + +#ifndef COMMON_DEFS_H +#define COMMON_DEFS_H + +#include "libdeflate.h" + +#include <stdbool.h> +#include <stddef.h> /* for size_t */ +#include <stdint.h> +#ifdef _MSC_VER +# include <intrin.h> /* for _BitScan*() and other intrinsics */ +# include <stdlib.h> /* for _byteswap_*() */ + /* Disable MSVC warnings that are expected. */ + /* /W2 */ +# pragma warning(disable : 4146) /* unary minus on unsigned type */ + /* /W3 */ +# pragma warning(disable : 4018) /* signed/unsigned mismatch */ +# pragma warning(disable : 4244) /* possible loss of data */ +# pragma warning(disable : 4267) /* possible loss of precision */ +# pragma warning(disable : 4310) /* cast truncates constant value */ + /* /W4 */ +# pragma warning(disable : 4100) /* unreferenced formal parameter */ +# pragma warning(disable : 4127) /* conditional expression is constant */ +# pragma warning(disable : 4189) /* local variable initialized but not referenced */ +# pragma warning(disable : 4232) /* nonstandard extension used */ +# pragma warning(disable : 4245) /* conversion from 'int' to 'unsigned int' */ +# pragma warning(disable : 4295) /* array too small to include terminating null */ +#endif +#ifndef FREESTANDING +# include <string.h> /* for memcpy() */ +#endif + +/* ========================================================================== */ +/* Target architecture */ +/* ========================================================================== */ + +/* If possible, define a compiler-independent ARCH_* macro. */ +#undef ARCH_X86_64 +#undef ARCH_X86_32 +#undef ARCH_ARM64 +#undef ARCH_ARM32 +#ifdef _MSC_VER +# if defined(_M_X64) +# define ARCH_X86_64 +# elif defined(_M_IX86) +# define ARCH_X86_32 +# elif defined(_M_ARM64) +# define ARCH_ARM64 +# elif defined(_M_ARM) +# define ARCH_ARM32 +# endif +#else +# if defined(__x86_64__) +# define ARCH_X86_64 +# elif defined(__i386__) +# define ARCH_X86_32 +# elif defined(__aarch64__) +# define ARCH_ARM64 +# elif defined(__arm__) +# define ARCH_ARM32 +# endif +#endif + +/* ========================================================================== */ +/* Type definitions */ +/* ========================================================================== */ + +/* Fixed-width integer types */ +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +/* ssize_t, if not available in <sys/types.h> */ +#ifdef _MSC_VER +# ifdef _WIN64 + typedef long long ssize_t; +# else + typedef long ssize_t; +# endif +#endif + +/* + * Word type of the target architecture. Use 'size_t' instead of + * 'unsigned long' to account for platforms such as Windows that use 32-bit + * 'unsigned long' on 64-bit architectures. + */ +typedef size_t machine_word_t; + +/* Number of bytes in a word */ +#define WORDBYTES ((int)sizeof(machine_word_t)) + +/* Number of bits in a word */ +#define WORDBITS (8 * WORDBYTES) + +/* ========================================================================== */ +/* Optional compiler features */ +/* ========================================================================== */ + +/* Compiler version checks. Only use when absolutely necessary. */ +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# define GCC_PREREQ(major, minor) \ + (__GNUC__ > (major) || \ + (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) +#else +# define GCC_PREREQ(major, minor) 0 +#endif +#ifdef __clang__ +# ifdef __apple_build_version__ +# define CLANG_PREREQ(major, minor, apple_version) \ + (__apple_build_version__ >= (apple_version)) +# else +# define CLANG_PREREQ(major, minor, apple_version) \ + (__clang_major__ > (major) || \ + (__clang_major__ == (major) && __clang_minor__ >= (minor))) +# endif +#else +# define CLANG_PREREQ(major, minor, apple_version) 0 +#endif + +/* + * Macros to check for compiler support for attributes and builtins. clang + * implements these macros, but gcc doesn't, so generally any use of one of + * these macros must also be combined with a gcc version check. + */ +#ifndef __has_attribute +# define __has_attribute(attribute) 0 +#endif +#ifndef __has_builtin +# define __has_builtin(builtin) 0 +#endif + +/* inline - suggest that a function be inlined */ +#ifdef _MSC_VER +# define inline __inline +#endif /* else assume 'inline' is usable as-is */ + +/* forceinline - force a function to be inlined, if possible */ +#if defined(__GNUC__) || __has_attribute(always_inline) +# define forceinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +# define forceinline __forceinline +#else +# define forceinline inline +#endif + +/* MAYBE_UNUSED - mark a function or variable as maybe unused */ +#if defined(__GNUC__) || __has_attribute(unused) +# define MAYBE_UNUSED __attribute__((unused)) +#else +# define MAYBE_UNUSED +#endif + +/* + * restrict - hint that writes only occur through the given pointer. + * + * Don't use MSVC's __restrict, since it has nonstandard behavior. + * Standard restrict is okay, if it is supported. + */ +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 201112L) +# if defined(__GNUC__) || defined(__clang__) +# define restrict __restrict__ +# else +# define restrict +# endif +#endif /* else assume 'restrict' is usable as-is */ + +/* likely(expr) - hint that an expression is usually true */ +#if defined(__GNUC__) || __has_builtin(__builtin_expect) +# define likely(expr) __builtin_expect(!!(expr), 1) +#else +# define likely(expr) (expr) +#endif + +/* unlikely(expr) - hint that an expression is usually false */ +#if defined(__GNUC__) || __has_builtin(__builtin_expect) +# define unlikely(expr) __builtin_expect(!!(expr), 0) +#else +# define unlikely(expr) (expr) +#endif + +/* prefetchr(addr) - prefetch into L1 cache for read */ +#undef prefetchr +#if defined(__GNUC__) || __has_builtin(__builtin_prefetch) +# define prefetchr(addr) __builtin_prefetch((addr), 0) +#elif defined(_MSC_VER) +# if defined(ARCH_X86_32) || defined(ARCH_X86_64) +# define prefetchr(addr) _mm_prefetch((addr), _MM_HINT_T0) +# elif defined(ARCH_ARM64) +# define prefetchr(addr) __prefetch2((addr), 0x00 /* prfop=PLDL1KEEP */) +# elif defined(ARCH_ARM32) +# define prefetchr(addr) __prefetch(addr) +# endif +#endif +#ifndef prefetchr +# define prefetchr(addr) +#endif + +/* prefetchw(addr) - prefetch into L1 cache for write */ +#undef prefetchw +#if defined(__GNUC__) || __has_builtin(__builtin_prefetch) +# define prefetchw(addr) __builtin_prefetch((addr), 1) +#elif defined(_MSC_VER) +# if defined(ARCH_X86_32) || defined(ARCH_X86_64) +# define prefetchw(addr) _m_prefetchw(addr) +# elif defined(ARCH_ARM64) +# define prefetchw(addr) __prefetch2((addr), 0x10 /* prfop=PSTL1KEEP */) +# elif defined(ARCH_ARM32) +# define prefetchw(addr) __prefetchw(addr) +# endif +#endif +#ifndef prefetchw +# define prefetchw(addr) +#endif + +/* + * _aligned_attribute(n) - declare that the annotated variable, or variables of + * the annotated type, must be aligned on n-byte boundaries. + */ +#undef _aligned_attribute +#if defined(__GNUC__) || __has_attribute(aligned) +# define _aligned_attribute(n) __attribute__((aligned(n))) +#elif defined(_MSC_VER) +# define _aligned_attribute(n) __declspec(align(n)) +#endif + +/* + * _target_attribute(attrs) - override the compilation target for a function. + * + * This accepts one or more comma-separated suffixes to the -m prefix jointly + * forming the name of a machine-dependent option. On gcc-like compilers, this + * enables codegen for the given targets, including arbitrary compiler-generated + * code as well as the corresponding intrinsics. On other compilers this macro + * expands to nothing, though MSVC allows intrinsics to be used anywhere anyway. + */ +#if GCC_PREREQ(4, 4) || __has_attribute(target) +# define _target_attribute(attrs) __attribute__((target(attrs))) +# define COMPILER_SUPPORTS_TARGET_FUNCTION_ATTRIBUTE 1 +#else +# define _target_attribute(attrs) +# define COMPILER_SUPPORTS_TARGET_FUNCTION_ATTRIBUTE 0 +#endif + +/* ========================================================================== */ +/* Miscellaneous macros */ +/* ========================================================================== */ + +#define ARRAY_LEN(A) (sizeof(A) / sizeof((A)[0])) +#define MIN(a, b) ((a) <= (b) ? (a) : (b)) +#define MAX(a, b) ((a) >= (b) ? (a) : (b)) +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#define STATIC_ASSERT(expr) ((void)sizeof(char[1 - 2 * !(expr)])) +#define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1)) +#define ROUND_UP(n, d) ((d) * DIV_ROUND_UP((n), (d))) + +/* ========================================================================== */ +/* Endianness handling */ +/* ========================================================================== */ + +/* + * CPU_IS_LITTLE_ENDIAN() - 1 if the CPU is little endian, or 0 if it is big + * endian. When possible this is a compile-time macro that can be used in + * preprocessor conditionals. As a fallback, a generic method is used that + * can't be used in preprocessor conditionals but should still be optimized out. + */ +#if defined(__BYTE_ORDER__) /* gcc v4.6+ and clang */ +# define CPU_IS_LITTLE_ENDIAN() (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#elif defined(_MSC_VER) +# define CPU_IS_LITTLE_ENDIAN() true +#else +static forceinline bool CPU_IS_LITTLE_ENDIAN(void) +{ + union { + u32 w; + u8 b; + } u; + + u.w = 1; + return u.b; +} +#endif + +/* bswap16(v) - swap the bytes of a 16-bit integer */ +static forceinline u16 bswap16(u16 v) +{ +#if GCC_PREREQ(4, 8) || __has_builtin(__builtin_bswap16) + return __builtin_bswap16(v); +#elif defined(_MSC_VER) + return _byteswap_ushort(v); +#else + return (v << 8) | (v >> 8); +#endif +} + +/* bswap32(v) - swap the bytes of a 32-bit integer */ +static forceinline u32 bswap32(u32 v) +{ +#if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap32) + return __builtin_bswap32(v); +#elif defined(_MSC_VER) + return _byteswap_ulong(v); +#else + return ((v & 0x000000FF) << 24) | + ((v & 0x0000FF00) << 8) | + ((v & 0x00FF0000) >> 8) | + ((v & 0xFF000000) >> 24); +#endif +} + +/* bswap64(v) - swap the bytes of a 64-bit integer */ +static forceinline u64 bswap64(u64 v) +{ +#if GCC_PREREQ(4, 3) || __has_builtin(__builtin_bswap64) + return __builtin_bswap64(v); +#elif defined(_MSC_VER) + return _byteswap_uint64(v); +#else + return ((v & 0x00000000000000FF) << 56) | + ((v & 0x000000000000FF00) << 40) | + ((v & 0x0000000000FF0000) << 24) | + ((v & 0x00000000FF000000) << 8) | + ((v & 0x000000FF00000000) >> 8) | + ((v & 0x0000FF0000000000) >> 24) | + ((v & 0x00FF000000000000) >> 40) | + ((v & 0xFF00000000000000) >> 56); +#endif +} + +#define le16_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? (v) : bswap16(v)) +#define le32_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? (v) : bswap32(v)) +#define le64_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? (v) : bswap64(v)) +#define be16_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? bswap16(v) : (v)) +#define be32_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? bswap32(v) : (v)) +#define be64_bswap(v) (CPU_IS_LITTLE_ENDIAN() ? bswap64(v) : (v)) + +/* ========================================================================== */ +/* Unaligned memory accesses */ +/* ========================================================================== */ + +/* + * UNALIGNED_ACCESS_IS_FAST() - 1 if unaligned memory accesses can be performed + * efficiently on the target platform, otherwise 0. + */ +#if (defined(__GNUC__) || defined(__clang__)) && \ + (defined(ARCH_X86_64) || defined(ARCH_X86_32) || \ + defined(__ARM_FEATURE_UNALIGNED) || defined(__powerpc64__) || \ + /* + * For all compilation purposes, WebAssembly behaves like any other CPU + * instruction set. Even though WebAssembly engine might be running on + * top of different actual CPU architectures, the WebAssembly spec + * itself permits unaligned access and it will be fast on most of those + * platforms, and simulated at the engine level on others, so it's + * worth treating it as a CPU architecture with fast unaligned access. + */ defined(__wasm__)) +# define UNALIGNED_ACCESS_IS_FAST 1 +#elif defined(_MSC_VER) +# define UNALIGNED_ACCESS_IS_FAST 1 +#else +# define UNALIGNED_ACCESS_IS_FAST 0 +#endif + +/* + * Implementing unaligned memory accesses using memcpy() is portable, and it + * usually gets optimized appropriately by modern compilers. I.e., each + * memcpy() of 1, 2, 4, or WORDBYTES bytes gets compiled to a load or store + * instruction, not to an actual function call. + * + * We no longer use the "packed struct" approach to unaligned accesses, as that + * is nonstandard, has unclear semantics, and doesn't receive enough testing + * (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94994). + * + * arm32 with __ARM_FEATURE_UNALIGNED in gcc 5 and earlier is a known exception + * where memcpy() generates inefficient code + * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67366). However, we no longer + * consider that one case important enough to maintain different code for. + * If you run into it, please just use a newer version of gcc (or use clang). + */ + +#ifdef FREESTANDING +# define MEMCOPY __builtin_memcpy +#else +# define MEMCOPY memcpy +#endif + +/* Unaligned loads and stores without endianness conversion */ + +#define DEFINE_UNALIGNED_TYPE(type) \ +static forceinline type \ +load_##type##_unaligned(const void *p) \ +{ \ + type v; \ + \ + MEMCOPY(&v, p, sizeof(v)); \ + return v; \ +} \ + \ +static forceinline void \ +store_##type##_unaligned(type v, void *p) \ +{ \ + MEMCOPY(p, &v, sizeof(v)); \ +} + +DEFINE_UNALIGNED_TYPE(u16) +DEFINE_UNALIGNED_TYPE(u32) +DEFINE_UNALIGNED_TYPE(u64) +DEFINE_UNALIGNED_TYPE(machine_word_t) + +#undef MEMCOPY + +#define load_word_unaligned load_machine_word_t_unaligned +#define store_word_unaligned store_machine_word_t_unaligned + +/* Unaligned loads with endianness conversion */ + +static forceinline u16 +get_unaligned_le16(const u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) + return le16_bswap(load_u16_unaligned(p)); + else + return ((u16)p[1] << 8) | p[0]; +} + +static forceinline u16 +get_unaligned_be16(const u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) + return be16_bswap(load_u16_unaligned(p)); + else + return ((u16)p[0] << 8) | p[1]; +} + +static forceinline u32 +get_unaligned_le32(const u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) + return le32_bswap(load_u32_unaligned(p)); + else + return ((u32)p[3] << 24) | ((u32)p[2] << 16) | + ((u32)p[1] << 8) | p[0]; +} + +static forceinline u32 +get_unaligned_be32(const u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) + return be32_bswap(load_u32_unaligned(p)); + else + return ((u32)p[0] << 24) | ((u32)p[1] << 16) | + ((u32)p[2] << 8) | p[3]; +} + +static forceinline u64 +get_unaligned_le64(const u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) + return le64_bswap(load_u64_unaligned(p)); + else + return ((u64)p[7] << 56) | ((u64)p[6] << 48) | + ((u64)p[5] << 40) | ((u64)p[4] << 32) | + ((u64)p[3] << 24) | ((u64)p[2] << 16) | + ((u64)p[1] << 8) | p[0]; +} + +static forceinline machine_word_t +get_unaligned_leword(const u8 *p) +{ + STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); + if (WORDBITS == 32) + return get_unaligned_le32(p); + else + return get_unaligned_le64(p); +} + +/* Unaligned stores with endianness conversion */ + +static forceinline void +put_unaligned_le16(u16 v, u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) { + store_u16_unaligned(le16_bswap(v), p); + } else { + p[0] = (u8)(v >> 0); + p[1] = (u8)(v >> 8); + } +} + +static forceinline void +put_unaligned_be16(u16 v, u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) { + store_u16_unaligned(be16_bswap(v), p); + } else { + p[0] = (u8)(v >> 8); + p[1] = (u8)(v >> 0); + } +} + +static forceinline void +put_unaligned_le32(u32 v, u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) { + store_u32_unaligned(le32_bswap(v), p); + } else { + p[0] = (u8)(v >> 0); + p[1] = (u8)(v >> 8); + p[2] = (u8)(v >> 16); + p[3] = (u8)(v >> 24); + } +} + +static forceinline void +put_unaligned_be32(u32 v, u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) { + store_u32_unaligned(be32_bswap(v), p); + } else { + p[0] = (u8)(v >> 24); + p[1] = (u8)(v >> 16); + p[2] = (u8)(v >> 8); + p[3] = (u8)(v >> 0); + } +} + +static forceinline void +put_unaligned_le64(u64 v, u8 *p) +{ + if (UNALIGNED_ACCESS_IS_FAST) { + store_u64_unaligned(le64_bswap(v), p); + } else { + p[0] = (u8)(v >> 0); + p[1] = (u8)(v >> 8); + p[2] = (u8)(v >> 16); + p[3] = (u8)(v >> 24); + p[4] = (u8)(v >> 32); + p[5] = (u8)(v >> 40); + p[6] = (u8)(v >> 48); + p[7] = (u8)(v >> 56); + } +} + +static forceinline void +put_unaligned_leword(machine_word_t v, u8 *p) +{ + STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); + if (WORDBITS == 32) + put_unaligned_le32(v, p); + else + put_unaligned_le64(v, p); +} + +/* ========================================================================== */ +/* Bit manipulation functions */ +/* ========================================================================== */ + +/* + * Bit Scan Reverse (BSR) - find the 0-based index (relative to the least + * significant end) of the *most* significant 1 bit in the input value. The + * input value must be nonzero! + */ + +static forceinline unsigned +bsr32(u32 v) +{ +#if defined(__GNUC__) || __has_builtin(__builtin_clz) + return 31 - __builtin_clz(v); +#elif defined(_MSC_VER) + unsigned long i; + + _BitScanReverse(&i, v); + return i; +#else + unsigned i = 0; + + while ((v >>= 1) != 0) + i++; + return i; +#endif +} + +static forceinline unsigned +bsr64(u64 v) +{ +#if defined(__GNUC__) || __has_builtin(__builtin_clzll) + return 63 - __builtin_clzll(v); +#elif defined(_MSC_VER) && defined(_WIN64) + unsigned long i; + + _BitScanReverse64(&i, v); + return i; +#else + unsigned i = 0; + + while ((v >>= 1) != 0) + i++; + return i; +#endif +} + +static forceinline unsigned +bsrw(machine_word_t v) +{ + STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); + if (WORDBITS == 32) + return bsr32(v); + else + return bsr64(v); +} + +/* + * Bit Scan Forward (BSF) - find the 0-based index (relative to the least + * significant end) of the *least* significant 1 bit in the input value. The + * input value must be nonzero! + */ + +static forceinline unsigned +bsf32(u32 v) +{ +#if defined(__GNUC__) || __has_builtin(__builtin_ctz) + return __builtin_ctz(v); +#elif defined(_MSC_VER) + unsigned long i; + + _BitScanForward(&i, v); + return i; +#else + unsigned i = 0; + + for (; (v & 1) == 0; v >>= 1) + i++; + return i; +#endif +} + +static forceinline unsigned +bsf64(u64 v) +{ +#if defined(__GNUC__) || __has_builtin(__builtin_ctzll) + return __builtin_ctzll(v); +#elif defined(_MSC_VER) && defined(_WIN64) + unsigned long i; + + _BitScanForward64(&i, v); + return i; +#else + unsigned i = 0; + + for (; (v & 1) == 0; v >>= 1) + i++; + return i; +#endif +} + +static forceinline unsigned +bsfw(machine_word_t v) +{ + STATIC_ASSERT(WORDBITS == 32 || WORDBITS == 64); + if (WORDBITS == 32) + return bsf32(v); + else + return bsf64(v); +} + +/* + * rbit32(v): reverse the bits in a 32-bit integer. This doesn't have a + * fallback implementation; use '#ifdef rbit32' to check if this is available. + */ +#undef rbit32 +#if (defined(__GNUC__) || defined(__clang__)) && defined(ARCH_ARM32) && \ + (__ARM_ARCH >= 7 || (__ARM_ARCH == 6 && defined(__ARM_ARCH_6T2__))) +static forceinline u32 +rbit32(u32 v) +{ + __asm__("rbit %0, %1" : "=r" (v) : "r" (v)); + return v; +} +#define rbit32 rbit32 +#elif (defined(__GNUC__) || defined(__clang__)) && defined(ARCH_ARM64) +static forceinline u32 +rbit32(u32 v) +{ + __asm__("rbit %w0, %w1" : "=r" (v) : "r" (v)); + return v; +} +#define rbit32 rbit32 +#endif + +#endif /* COMMON_DEFS_H */ + + +typedef void *(*malloc_func_t)(size_t); +typedef void (*free_func_t)(void *); + +extern malloc_func_t libdeflate_default_malloc_func; +extern free_func_t libdeflate_default_free_func; + +void *libdeflate_aligned_malloc(malloc_func_t malloc_func, + size_t alignment, size_t size); +void libdeflate_aligned_free(free_func_t free_func, void *ptr); + +#ifdef FREESTANDING +/* + * With -ffreestanding, <string.h> may be missing, and we must provide + * implementations of memset(), memcpy(), memmove(), and memcmp(). + * See https://gcc.gnu.org/onlinedocs/gcc/Standards.html + * + * Also, -ffreestanding disables interpreting calls to these functions as + * built-ins. E.g., calling memcpy(&v, p, WORDBYTES) will make a function call, + * not be optimized to a single load instruction. For performance reasons we + * don't want that. So, declare these functions as macros that expand to the + * corresponding built-ins. This approach is recommended in the gcc man page. + * We still need the actual function definitions in case gcc calls them. + */ +void *memset(void *s, int c, size_t n); +#define memset(s, c, n) __builtin_memset((s), (c), (n)) + +void *memcpy(void *dest, const void *src, size_t n); +#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) + +void *memmove(void *dest, const void *src, size_t n); +#define memmove(dest, src, n) __builtin_memmove((dest), (src), (n)) + +int memcmp(const void *s1, const void *s2, size_t n); +#define memcmp(s1, s2, n) __builtin_memcmp((s1), (s2), (n)) + +#undef LIBDEFLATE_ENABLE_ASSERTIONS +#else +#include <string.h> +#endif + +/* + * Runtime assertion support. Don't enable this in production builds; it may + * hurt performance significantly. + */ +#ifdef LIBDEFLATE_ENABLE_ASSERTIONS +void libdeflate_assertion_failed(const char *expr, const char *file, int line); +#define ASSERT(expr) { if (unlikely(!(expr))) \ + libdeflate_assertion_failed(#expr, __FILE__, __LINE__); } +#else +#define ASSERT(expr) (void)(expr) +#endif + +#define CONCAT_IMPL(a, b) a##b +#define CONCAT(a, b) CONCAT_IMPL(a, b) +#define ADD_SUFFIX(name) CONCAT(name, SUFFIX) + +#endif /* LIB_LIB_COMMON_H */ + +/* + * deflate_constants.h - constants for the DEFLATE compression format + */ + +#ifndef LIB_DEFLATE_CONSTANTS_H +#define LIB_DEFLATE_CONSTANTS_H + +/* Valid block types */ +#define DEFLATE_BLOCKTYPE_UNCOMPRESSED 0 +#define DEFLATE_BLOCKTYPE_STATIC_HUFFMAN 1 +#define DEFLATE_BLOCKTYPE_DYNAMIC_HUFFMAN 2 + +/* Minimum and maximum supported match lengths (in bytes) */ +#define DEFLATE_MIN_MATCH_LEN 3 +#define DEFLATE_MAX_MATCH_LEN 258 + +/* Maximum supported match offset (in bytes) */ +#define DEFLATE_MAX_MATCH_OFFSET 32768 + +/* log2 of DEFLATE_MAX_MATCH_OFFSET */ +#define DEFLATE_WINDOW_ORDER 15 + +/* Number of symbols in each Huffman code. Note: for the literal/length + * and offset codes, these are actually the maximum values; a given block + * might use fewer symbols. */ +#define DEFLATE_NUM_PRECODE_SYMS 19 +#define DEFLATE_NUM_LITLEN_SYMS 288 +#define DEFLATE_NUM_OFFSET_SYMS 32 + +/* The maximum number of symbols across all codes */ +#define DEFLATE_MAX_NUM_SYMS 288 + +/* Division of symbols in the literal/length code */ +#define DEFLATE_NUM_LITERALS 256 +#define DEFLATE_END_OF_BLOCK 256 +#define DEFLATE_FIRST_LEN_SYM 257 + +/* Maximum codeword length, in bits, within each Huffman code */ +#define DEFLATE_MAX_PRE_CODEWORD_LEN 7 +#define DEFLATE_MAX_LITLEN_CODEWORD_LEN 15 +#define DEFLATE_MAX_OFFSET_CODEWORD_LEN 15 + +/* The maximum codeword length across all codes */ +#define DEFLATE_MAX_CODEWORD_LEN 15 + +/* Maximum possible overrun when decoding codeword lengths */ +#define DEFLATE_MAX_LENS_OVERRUN 137 + +/* + * Maximum number of extra bits that may be required to represent a match + * length or offset. + */ +#define DEFLATE_MAX_EXTRA_LENGTH_BITS 5 +#define DEFLATE_MAX_EXTRA_OFFSET_BITS 13 + +#endif /* LIB_DEFLATE_CONSTANTS_H */ + +/* + * cpu_features_common.h - code shared by all lib/$arch/cpu_features.c + * + * Copyright 2020 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + */ + +#ifndef LIB_CPU_FEATURES_COMMON_H +#define LIB_CPU_FEATURES_COMMON_H + +#if defined(TEST_SUPPORT__DO_NOT_USE) && !defined(FREESTANDING) + /* for strdup() and strtok_r() */ +# undef _ANSI_SOURCE +# ifndef __APPLE__ +# undef _GNU_SOURCE +# define _GNU_SOURCE +# endif +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +#endif + +struct cpu_feature { + u32 bit; + const char *name; +}; + +#if defined(TEST_SUPPORT__DO_NOT_USE) && !defined(FREESTANDING) +/* Disable any features that are listed in $LIBDEFLATE_DISABLE_CPU_FEATURES. */ +static inline void +disable_cpu_features_for_testing(u32 *features, + const struct cpu_feature *feature_table, + size_t feature_table_length) +{ + char *env_value, *strbuf, *p, *saveptr = NULL; + size_t i; + + env_value = getenv("LIBDEFLATE_DISABLE_CPU_FEATURES"); + if (!env_value) + return; + strbuf = strdup(env_value); + if (!strbuf) + abort(); + p = strtok_r(strbuf, ",", &saveptr); + while (p) { + for (i = 0; i < feature_table_length; i++) { + if (strcmp(p, feature_table[i].name) == 0) { + *features &= ~feature_table[i].bit; + break; + } + } + if (i == feature_table_length) { + fprintf(stderr, + "unrecognized feature in LIBDEFLATE_DISABLE_CPU_FEATURES: \"%s\"\n", + p); + abort(); + } + p = strtok_r(NULL, ",", &saveptr); + } + free(strbuf); +} +#else /* TEST_SUPPORT__DO_NOT_USE */ +static inline void +disable_cpu_features_for_testing(u32 *features, + const struct cpu_feature *feature_table, + size_t feature_table_length) +{ +} +#endif /* !TEST_SUPPORT__DO_NOT_USE */ + +#endif /* LIB_CPU_FEATURES_COMMON_H */ + +/* + * x86/cpu_features.h - feature detection for x86 CPUs + * + * Copyright 2016 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + */ + +#ifndef LIB_X86_CPU_FEATURES_H +#define LIB_X86_CPU_FEATURES_H + +#define HAVE_DYNAMIC_X86_CPU_FEATURES 0 + +#if defined(ARCH_X86_32) || defined(ARCH_X86_64) + +#if COMPILER_SUPPORTS_TARGET_FUNCTION_ATTRIBUTE || defined(_MSC_VER) +# undef HAVE_DYNAMIC_X86_CPU_FEATURES +# define HAVE_DYNAMIC_X86_CPU_FEATURES 1 +#endif + +#define X86_CPU_FEATURE_SSE2 0x00000001 +#define X86_CPU_FEATURE_PCLMUL 0x00000002 +#define X86_CPU_FEATURE_AVX 0x00000004 +#define X86_CPU_FEATURE_AVX2 0x00000008 +#define X86_CPU_FEATURE_BMI2 0x00000010 + +#define HAVE_SSE2(features) (HAVE_SSE2_NATIVE || ((features) & X86_CPU_FEATURE_SSE2)) +#define HAVE_PCLMUL(features) (HAVE_PCLMUL_NATIVE || ((features) & X86_CPU_FEATURE_PCLMUL)) +#define HAVE_AVX(features) (HAVE_AVX_NATIVE || ((features) & X86_CPU_FEATURE_AVX)) +#define HAVE_AVX2(features) (HAVE_AVX2_NATIVE || ((features) & X86_CPU_FEATURE_AVX2)) +#define HAVE_BMI2(features) (HAVE_BMI2_NATIVE || ((features) & X86_CPU_FEATURE_BMI2)) + +#if HAVE_DYNAMIC_X86_CPU_FEATURES +#define X86_CPU_FEATURES_KNOWN 0x80000000 +extern volatile u32 libdeflate_x86_cpu_features; + +void libdeflate_init_x86_cpu_features(void); + +static inline u32 get_x86_cpu_features(void) +{ + if (libdeflate_x86_cpu_features == 0) + libdeflate_init_x86_cpu_features(); + return libdeflate_x86_cpu_features; +} +#else /* HAVE_DYNAMIC_X86_CPU_FEATURES */ +static inline u32 get_x86_cpu_features(void) { return 0; } +#endif /* !HAVE_DYNAMIC_X86_CPU_FEATURES */ + +/* + * Prior to gcc 4.9 (r200349) and clang 3.8 (r239883), x86 intrinsics not + * available in the main target couldn't be used in 'target' attribute + * functions. Unfortunately clang has no feature test macro for this, so we + * have to check its version. + */ +#if HAVE_DYNAMIC_X86_CPU_FEATURES && \ + (GCC_PREREQ(4, 9) || CLANG_PREREQ(3, 8, 7030000) || defined(_MSC_VER)) +# define HAVE_TARGET_INTRINSICS 1 +#else +# define HAVE_TARGET_INTRINSICS 0 +#endif + +/* SSE2 */ +#if defined(__SSE2__) || \ + (defined(_MSC_VER) && \ + (defined(ARCH_X86_64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))) +# define HAVE_SSE2_NATIVE 1 +#else +# define HAVE_SSE2_NATIVE 0 +#endif +#define HAVE_SSE2_INTRIN (HAVE_SSE2_NATIVE || HAVE_TARGET_INTRINSICS) + +/* PCLMUL */ +#if defined(__PCLMUL__) || (defined(_MSC_VER) && defined(__AVX2__)) +# define HAVE_PCLMUL_NATIVE 1 +#else +# define HAVE_PCLMUL_NATIVE 0 +#endif +#if HAVE_PCLMUL_NATIVE || (HAVE_TARGET_INTRINSICS && \ + (GCC_PREREQ(4, 4) || CLANG_PREREQ(3, 2, 0) || \ + defined(_MSC_VER))) +# define HAVE_PCLMUL_INTRIN 1 +#else +# define HAVE_PCLMUL_INTRIN 0 +#endif + +/* AVX */ +#ifdef __AVX__ +# define HAVE_AVX_NATIVE 1 +#else +# define HAVE_AVX_NATIVE 0 +#endif +#if HAVE_AVX_NATIVE || (HAVE_TARGET_INTRINSICS && \ + (GCC_PREREQ(4, 6) || CLANG_PREREQ(3, 0, 0) || \ + defined(_MSC_VER))) +# define HAVE_AVX_INTRIN 1 +#else +# define HAVE_AVX_INTRIN 0 +#endif + +/* AVX2 */ +#ifdef __AVX2__ +# define HAVE_AVX2_NATIVE 1 +#else +# define HAVE_AVX2_NATIVE 0 +#endif +#if HAVE_AVX2_NATIVE || (HAVE_TARGET_INTRINSICS && \ + (GCC_PREREQ(4, 7) || CLANG_PREREQ(3, 1, 0) || \ + defined(_MSC_VER))) +# define HAVE_AVX2_INTRIN 1 +#else +# define HAVE_AVX2_INTRIN 0 +#endif + +/* BMI2 */ +#if defined(__BMI2__) || (defined(_MSC_VER) && defined(__AVX2__)) +# define HAVE_BMI2_NATIVE 1 +#else +# define HAVE_BMI2_NATIVE 0 +#endif +#if HAVE_BMI2_NATIVE || (HAVE_TARGET_INTRINSICS && \ + (GCC_PREREQ(4, 7) || CLANG_PREREQ(3, 1, 0) || \ + defined(_MSC_VER))) +# define HAVE_BMI2_INTRIN 1 +#else +# define HAVE_BMI2_INTRIN 0 +#endif + +#endif /* ARCH_X86_32 || ARCH_X86_64 */ + +#endif /* LIB_X86_CPU_FEATURES_H */ + + +/* + * If the expression passed to SAFETY_CHECK() evaluates to false, then the + * decompression routine immediately returns LIBDEFLATE_BAD_DATA, indicating the + * compressed data is invalid. + * + * Theoretically, these checks could be disabled for specialized applications + * where all input to the decompressor will be trusted. + */ +#if 0 +# pragma message("UNSAFE DECOMPRESSION IS ENABLED. THIS MUST ONLY BE USED IF THE DECOMPRESSOR INPUT WILL ALWAYS BE TRUSTED!") +# define SAFETY_CHECK(expr) (void)(expr) +#else +# define SAFETY_CHECK(expr) if (unlikely(!(expr))) return LIBDEFLATE_BAD_DATA +#endif + +/***************************************************************************** + * Input bitstream * + *****************************************************************************/ + +/* + * The state of the "input bitstream" consists of the following variables: + * + * - in_next: a pointer to the next unread byte in the input buffer + * + * - in_end: a pointer to just past the end of the input buffer + * + * - bitbuf: a word-sized variable containing bits that have been read from + * the input buffer or from the implicit appended zero bytes + * + * - bitsleft: the number of bits in 'bitbuf' available to be consumed. + * After REFILL_BITS_BRANCHLESS(), 'bitbuf' can actually + * contain more bits than this. However, only the bits counted + * by 'bitsleft' can actually be consumed; the rest can only be + * used for preloading. + * + * As a micro-optimization, we allow bits 8 and higher of + * 'bitsleft' to contain garbage. When consuming the bits + * associated with a decode table entry, this allows us to do + * 'bitsleft -= entry' instead of 'bitsleft -= (u8)entry'. + * On some CPUs, this helps reduce instruction dependencies. + * This does have the disadvantage that 'bitsleft' sometimes + * needs to be cast to 'u8', such as when it's used as a shift + * amount in REFILL_BITS_BRANCHLESS(). But that one happens + * for free since most CPUs ignore high bits in shift amounts. + * + * - overread_count: the total number of implicit appended zero bytes that + * have been loaded into the bitbuffer, including any + * counted by 'bitsleft' and any already consumed + */ + +/* + * The type for the bitbuffer variable ('bitbuf' described above). For best + * performance, this should have size equal to a machine word. + * + * 64-bit platforms have a significant advantage: they get a bigger bitbuffer + * which they don't have to refill as often. + */ +typedef machine_word_t bitbuf_t; +#define BITBUF_NBITS (8 * (int)sizeof(bitbuf_t)) + +/* BITMASK(n) returns a bitmask of length 'n'. */ +#define BITMASK(n) (((bitbuf_t)1 << (n)) - 1) + +/* + * MAX_BITSLEFT is the maximum number of consumable bits, i.e. the maximum value + * of '(u8)bitsleft'. This is the size of the bitbuffer variable, minus 1 if + * the branchless refill method is being used (see REFILL_BITS_BRANCHLESS()). + */ +#define MAX_BITSLEFT \ + (UNALIGNED_ACCESS_IS_FAST ? BITBUF_NBITS - 1 : BITBUF_NBITS) + +/* + * CONSUMABLE_NBITS is the minimum number of bits that are guaranteed to be + * consumable (counted in 'bitsleft') immediately after refilling the bitbuffer. + * Since only whole bytes can be added to 'bitsleft', the worst case is + * 'MAX_BITSLEFT - 7': the smallest amount where another byte doesn't fit. + */ +#define CONSUMABLE_NBITS (MAX_BITSLEFT - 7) + +/* + * FASTLOOP_PRELOADABLE_NBITS is the minimum number of bits that are guaranteed + * to be preloadable immediately after REFILL_BITS_IN_FASTLOOP(). (It is *not* + * guaranteed after REFILL_BITS(), since REFILL_BITS() falls back to a + * byte-at-a-time refill method near the end of input.) This may exceed the + * number of consumable bits (counted by 'bitsleft'). Any bits not counted in + * 'bitsleft' can only be used for precomputation and cannot be consumed. + */ +#define FASTLOOP_PRELOADABLE_NBITS \ + (UNALIGNED_ACCESS_IS_FAST ? BITBUF_NBITS : CONSUMABLE_NBITS) + +/* + * PRELOAD_SLACK is the minimum number of bits that are guaranteed to be + * preloadable but not consumable, following REFILL_BITS_IN_FASTLOOP() and any + * subsequent consumptions. This is 1 bit if the branchless refill method is + * being used, and 0 bits otherwise. + */ +#define PRELOAD_SLACK MAX(0, FASTLOOP_PRELOADABLE_NBITS - MAX_BITSLEFT) + +/* + * CAN_CONSUME(n) is true if it's guaranteed that if the bitbuffer has just been + * refilled, then it's always possible to consume 'n' bits from it. 'n' should + * be a compile-time constant, to enable compile-time evaluation. + */ +#define CAN_CONSUME(n) (CONSUMABLE_NBITS >= (n)) + +/* + * CAN_CONSUME_AND_THEN_PRELOAD(consume_nbits, preload_nbits) is true if it's + * guaranteed that after REFILL_BITS_IN_FASTLOOP(), it's always possible to + * consume 'consume_nbits' bits, then preload 'preload_nbits' bits. The + * arguments should be compile-time constants to enable compile-time evaluation. + */ +#define CAN_CONSUME_AND_THEN_PRELOAD(consume_nbits, preload_nbits) \ + (CONSUMABLE_NBITS >= (consume_nbits) && \ + FASTLOOP_PRELOADABLE_NBITS >= (consume_nbits) + (preload_nbits)) + +/* + * REFILL_BITS_BRANCHLESS() branchlessly refills the bitbuffer variable by + * reading the next word from the input buffer and updating 'in_next' and + * 'bitsleft' based on how many bits were refilled -- counting whole bytes only. + * This is much faster than reading a byte at a time, at least if the CPU is + * little endian and supports fast unaligned memory accesses. + * + * The simplest way of branchlessly updating 'bitsleft' would be: + * + * bitsleft += (MAX_BITSLEFT - bitsleft) & ~7; + * + * To make it faster, we define MAX_BITSLEFT to be 'WORDBITS - 1' rather than + * WORDBITS, so that in binary it looks like 111111 or 11111. Then, we update + * 'bitsleft' by just setting the bits above the low 3 bits: + * + * bitsleft |= MAX_BITSLEFT & ~7; + * + * That compiles down to a single instruction like 'or $0x38, %rbp'. Using + * 'MAX_BITSLEFT == WORDBITS - 1' also has the advantage that refills can be + * done when 'bitsleft == MAX_BITSLEFT' without invoking undefined behavior. + * + * The simplest way of branchlessly updating 'in_next' would be: + * + * in_next += (MAX_BITSLEFT - bitsleft) >> 3; + * + * With 'MAX_BITSLEFT == WORDBITS - 1' we could use an XOR instead, though this + * isn't really better: + * + * in_next += (MAX_BITSLEFT ^ bitsleft) >> 3; + * + * An alternative which can be marginally better is the following: + * + * in_next += sizeof(bitbuf_t) - 1; + * in_next -= (bitsleft >> 3) & 0x7; + * + * It seems this would increase the number of CPU instructions from 3 (sub, shr, + * add) to 4 (add, shr, and, sub). However, if the CPU has a bitfield + * extraction instruction (e.g. arm's ubfx), it stays at 3, and is potentially + * more efficient because the length of the longest dependency chain decreases + * from 3 to 2. This alternative also has the advantage that it ignores the + * high bits in 'bitsleft', so it is compatible with the micro-optimization we + * use where we let the high bits of 'bitsleft' contain garbage. + */ +#define REFILL_BITS_BRANCHLESS() \ +do { \ + bitbuf |= get_unaligned_leword(in_next) << (u8)bitsleft; \ + in_next += sizeof(bitbuf_t) - 1; \ + in_next -= (bitsleft >> 3) & 0x7; \ + bitsleft |= MAX_BITSLEFT & ~7; \ +} while (0) + +/* + * REFILL_BITS() loads bits from the input buffer until the bitbuffer variable + * contains at least CONSUMABLE_NBITS consumable bits. + * + * This checks for the end of input, and it doesn't guarantee + * FASTLOOP_PRELOADABLE_NBITS, so it can't be used in the fastloop. + * + * If we would overread the input buffer, we just don't read anything, leaving + * the bits zeroed but marking them filled. This simplifies the decompressor + * because it removes the need to always be able to distinguish between real + * overreads and overreads caused only by the decompressor's own lookahead. + * + * We do still keep track of the number of bytes that have been overread, for + * two reasons. First, it allows us to determine the exact number of bytes that + * were consumed once the stream ends or an uncompressed block is reached. + * Second, it allows us to stop early if the overread amount gets so large (more + * than sizeof bitbuf) that it can only be caused by a real overread. (The + * second part is arguably unneeded, since libdeflate is buffer-based; given + * infinite zeroes, it will eventually either completely fill the output buffer + * or return an error. However, we do it to be slightly more friendly to the + * not-recommended use case of decompressing with an unknown output size.) + */ +#define REFILL_BITS() \ +do { \ + if (UNALIGNED_ACCESS_IS_FAST && \ + likely(in_end - in_next >= sizeof(bitbuf_t))) { \ + REFILL_BITS_BRANCHLESS(); \ + } else { \ + while ((u8)bitsleft < CONSUMABLE_NBITS) { \ + if (likely(in_next != in_end)) { \ + bitbuf |= (bitbuf_t)*in_next++ << \ + (u8)bitsleft; \ + } else { \ + overread_count++; \ + SAFETY_CHECK(overread_count <= \ + sizeof(bitbuf_t)); \ + } \ + bitsleft += 8; \ + } \ + } \ +} while (0) + +/* + * REFILL_BITS_IN_FASTLOOP() is like REFILL_BITS(), but it doesn't check for the + * end of the input. It can only be used in the fastloop. + */ +#define REFILL_BITS_IN_FASTLOOP() \ +do { \ + STATIC_ASSERT(UNALIGNED_ACCESS_IS_FAST || \ + FASTLOOP_PRELOADABLE_NBITS == CONSUMABLE_NBITS); \ + if (UNALIGNED_ACCESS_IS_FAST) { \ + REFILL_BITS_BRANCHLESS(); \ + } else { \ + while ((u8)bitsleft < CONSUMABLE_NBITS) { \ + bitbuf |= (bitbuf_t)*in_next++ << (u8)bitsleft; \ + bitsleft += 8; \ + } \ + } \ +} while (0) + +/* + * This is the worst-case maximum number of output bytes that are written to + * during each iteration of the fastloop. The worst case is 2 literals, then a + * match of length DEFLATE_MAX_MATCH_LEN. Additionally, some slack space must + * be included for the intentional overrun in the match copy implementation. + */ +#define FASTLOOP_MAX_BYTES_WRITTEN \ + (2 + DEFLATE_MAX_MATCH_LEN + (5 * WORDBYTES) - 1) + +/* + * This is the worst-case maximum number of input bytes that are read during + * each iteration of the fastloop. To get this value, we first compute the + * greatest number of bits that can be refilled during a loop iteration. The + * refill at the beginning can add at most MAX_BITSLEFT, and the amount that can + * be refilled later is no more than the maximum amount that can be consumed by + * 2 literals that don't need a subtable, then a match. We convert this value + * to bytes, rounding up; this gives the maximum number of bytes that 'in_next' + * can be advanced. Finally, we add sizeof(bitbuf_t) to account for + * REFILL_BITS_BRANCHLESS() reading a word past 'in_next'. + */ +#define FASTLOOP_MAX_BYTES_READ \ + (DIV_ROUND_UP(MAX_BITSLEFT + (2 * LITLEN_TABLEBITS) + \ + LENGTH_MAXBITS + OFFSET_MAXBITS, 8) + \ + sizeof(bitbuf_t)) + +/***************************************************************************** + * Huffman decoding * + *****************************************************************************/ + +/* + * The fastest way to decode Huffman-encoded data is basically to use a decode + * table that maps the next TABLEBITS bits of data to their symbol. Each entry + * decode_table[i] maps to the symbol whose codeword is a prefix of 'i'. A + * symbol with codeword length 'n' has '2**(TABLEBITS-n)' entries in the table. + * + * Ideally, TABLEBITS and the maximum codeword length would be the same; some + * compression formats are designed with this goal in mind. Unfortunately, in + * DEFLATE, the maximum litlen and offset codeword lengths are 15 bits, which is + * too large for a practical TABLEBITS. It's not *that* much larger, though, so + * the workaround is to use a single level of subtables. In the main table, + * entries for prefixes of codewords longer than TABLEBITS contain a "pointer" + * to the appropriate subtable along with the number of bits it is indexed with. + * + * The most efficient way to allocate subtables is to allocate them dynamically + * after the main table. The worst-case number of table entries needed, + * including subtables, is precomputable; see the ENOUGH constants below. + * + * A useful optimization is to store the codeword lengths in the decode table so + * that they don't have to be looked up by indexing a separate table that maps + * symbols to their codeword lengths. We basically do this; however, for the + * litlen and offset codes we also implement some DEFLATE-specific optimizations + * that build in the consideration of the "extra bits" and the + * literal/length/end-of-block division. For the exact decode table entry + * format we use, see the definitions of the *_decode_results[] arrays below. + */ + + +/* + * These are the TABLEBITS values we use for each of the DEFLATE Huffman codes, + * along with their corresponding ENOUGH values. + * + * For the precode, we use PRECODE_TABLEBITS == 7 since this is the maximum + * precode codeword length. This avoids ever needing subtables. + * + * For the litlen and offset codes, we cannot realistically avoid ever needing + * subtables, since litlen and offset codewords can be up to 15 bits. A higher + * TABLEBITS reduces the number of lookups that need a subtable, which increases + * performance; however, it increases memory usage and makes building the table + * take longer, which decreases performance. We choose values that work well in + * practice, making subtables rarely needed without making the tables too large. + * + * Our choice of OFFSET_TABLEBITS == 8 is a bit low; without any special + * considerations, 9 would fit the trade-off curve better. However, there is a + * performance benefit to using exactly 8 bits when it is a compile-time + * constant, as many CPUs can take the low byte more easily than the low 9 bits. + * + * zlib treats its equivalents of TABLEBITS as maximum values; whenever it + * builds a table, it caps the actual table_bits to the longest codeword. This + * makes sense in theory, as there's no need for the table to be any larger than + * needed to support the longest codeword. However, having the table bits be a + * compile-time constant is beneficial to the performance of the decode loop, so + * there is a trade-off. libdeflate currently uses the dynamic table_bits + * strategy for the litlen table only, due to its larger maximum size. + * PRECODE_TABLEBITS and OFFSET_TABLEBITS are smaller, so going dynamic there + * isn't as useful, and OFFSET_TABLEBITS=8 is useful as mentioned above. + * + * Each TABLEBITS value has a corresponding ENOUGH value that gives the + * worst-case maximum number of decode table entries, including the main table + * and all subtables. The ENOUGH value depends on three parameters: + * + * (1) the maximum number of symbols in the code (DEFLATE_NUM_*_SYMS) + * (2) the maximum number of main table bits (*_TABLEBITS) + * (3) the maximum allowed codeword length (DEFLATE_MAX_*_CODEWORD_LEN) + * + * The ENOUGH values were computed using the utility program 'enough' from zlib. + */ +#define PRECODE_TABLEBITS 7 +#define PRECODE_ENOUGH 128 /* enough 19 7 7 */ +#define LITLEN_TABLEBITS 11 +#define LITLEN_ENOUGH 2342 /* enough 288 11 15 */ +#define OFFSET_TABLEBITS 8 +#define OFFSET_ENOUGH 402 /* enough 32 8 15 */ + +/* + * make_decode_table_entry() creates a decode table entry for the given symbol + * by combining the static part 'decode_results[sym]' with the dynamic part + * 'len', which is the remaining codeword length (the codeword length for main + * table entries, or the codeword length minus TABLEBITS for subtable entries). + * + * In all cases, we add 'len' to each of the two low-order bytes to create the + * appropriately-formatted decode table entry. See the definitions of the + * *_decode_results[] arrays below, where the entry format is described. + */ +static forceinline u32 +make_decode_table_entry(const u32 decode_results[], u32 sym, u32 len) +{ + return decode_results[sym] + (len << 8) + len; +} + +/* + * Here is the format of our precode decode table entries. Bits not explicitly + * described contain zeroes: + * + * Bit 20-16: presym + * Bit 10-8: codeword length [not used] + * Bit 2-0: codeword length + * + * The precode decode table never has subtables, since we use + * PRECODE_TABLEBITS == DEFLATE_MAX_PRE_CODEWORD_LEN. + * + * precode_decode_results[] contains the static part of the entry for each + * symbol. make_decode_table_entry() produces the final entries. + */ +static const u32 precode_decode_results[] = { +#define ENTRY(presym) ((u32)presym << 16) + ENTRY(0) , ENTRY(1) , ENTRY(2) , ENTRY(3) , + ENTRY(4) , ENTRY(5) , ENTRY(6) , ENTRY(7) , + ENTRY(8) , ENTRY(9) , ENTRY(10) , ENTRY(11) , + ENTRY(12) , ENTRY(13) , ENTRY(14) , ENTRY(15) , + ENTRY(16) , ENTRY(17) , ENTRY(18) , +#undef ENTRY +}; + +/* Litlen and offset decode table entry flags */ + +/* Indicates a literal entry in the litlen decode table */ +#define HUFFDEC_LITERAL 0x80000000 + +/* Indicates that HUFFDEC_SUBTABLE_POINTER or HUFFDEC_END_OF_BLOCK is set */ +#define HUFFDEC_EXCEPTIONAL 0x00008000 + +/* Indicates a subtable pointer entry in the litlen or offset decode table */ +#define HUFFDEC_SUBTABLE_POINTER 0x00004000 + +/* Indicates an end-of-block entry in the litlen decode table */ +#define HUFFDEC_END_OF_BLOCK 0x00002000 + +/* Maximum number of bits that can be consumed by decoding a match length */ +#define LENGTH_MAXBITS (DEFLATE_MAX_LITLEN_CODEWORD_LEN + \ + DEFLATE_MAX_EXTRA_LENGTH_BITS) +#define LENGTH_MAXFASTBITS (LITLEN_TABLEBITS /* no subtable needed */ + \ + DEFLATE_MAX_EXTRA_LENGTH_BITS) + +/* + * Here is the format of our litlen decode table entries. Bits not explicitly + * described contain zeroes: + * + * Literals: + * Bit 31: 1 (HUFFDEC_LITERAL) + * Bit 23-16: literal value + * Bit 15: 0 (!HUFFDEC_EXCEPTIONAL) + * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) + * Bit 13: 0 (!HUFFDEC_END_OF_BLOCK) + * Bit 11-8: remaining codeword length [not used] + * Bit 3-0: remaining codeword length + * Lengths: + * Bit 31: 0 (!HUFFDEC_LITERAL) + * Bit 24-16: length base value + * Bit 15: 0 (!HUFFDEC_EXCEPTIONAL) + * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) + * Bit 13: 0 (!HUFFDEC_END_OF_BLOCK) + * Bit 11-8: remaining codeword length + * Bit 4-0: remaining codeword length + number of extra bits + * End of block: + * Bit 31: 0 (!HUFFDEC_LITERAL) + * Bit 15: 1 (HUFFDEC_EXCEPTIONAL) + * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) + * Bit 13: 1 (HUFFDEC_END_OF_BLOCK) + * Bit 11-8: remaining codeword length [not used] + * Bit 3-0: remaining codeword length + * Subtable pointer: + * Bit 31: 0 (!HUFFDEC_LITERAL) + * Bit 30-16: index of start of subtable + * Bit 15: 1 (HUFFDEC_EXCEPTIONAL) + * Bit 14: 1 (HUFFDEC_SUBTABLE_POINTER) + * Bit 13: 0 (!HUFFDEC_END_OF_BLOCK) + * Bit 11-8: number of subtable bits + * Bit 3-0: number of main table bits + * + * This format has several desirable properties: + * + * - The codeword length, length slot base, and number of extra length bits + * are all built in. This eliminates the need to separately look up this + * information by indexing separate arrays by symbol or length slot. + * + * - The HUFFDEC_* flags enable easily distinguishing between the different + * types of entries. The HUFFDEC_LITERAL flag enables a fast path for + * literals; the high bit is used for this, as some CPUs can test the + * high bit more easily than other bits. The HUFFDEC_EXCEPTIONAL flag + * makes it possible to detect the two unlikely cases (subtable pointer + * and end of block) in a single bit flag test. + * + * - The low byte is the number of bits that need to be removed from the + * bitstream; this makes this value easily accessible, and it enables the + * micro-optimization of doing 'bitsleft -= entry' instead of + * 'bitsleft -= (u8)entry'. It also includes the number of extra bits, + * so they don't need to be removed separately. + * + * - The flags in bits 15-13 are arranged to be 0 when the + * "remaining codeword length" in bits 11-8 is needed, making this value + * fairly easily accessible as well via a shift and downcast. + * + * - Similarly, bits 13-12 are 0 when the "subtable bits" in bits 11-8 are + * needed, making it possible to extract this value with '& 0x3F' rather + * than '& 0xF'. This value is only used as a shift amount, so this can + * save an 'and' instruction as the masking by 0x3F happens implicitly. + * + * litlen_decode_results[] contains the static part of the entry for each + * symbol. make_decode_table_entry() produces the final entries. + */ +static const u32 litlen_decode_results[] = { + + /* Literals */ +#define ENTRY(literal) (HUFFDEC_LITERAL | ((u32)literal << 16)) + ENTRY(0) , ENTRY(1) , ENTRY(2) , ENTRY(3) , + ENTRY(4) , ENTRY(5) , ENTRY(6) , ENTRY(7) , + ENTRY(8) , ENTRY(9) , ENTRY(10) , ENTRY(11) , + ENTRY(12) , ENTRY(13) , ENTRY(14) , ENTRY(15) , + ENTRY(16) , ENTRY(17) , ENTRY(18) , ENTRY(19) , + ENTRY(20) , ENTRY(21) , ENTRY(22) , ENTRY(23) , + ENTRY(24) , ENTRY(25) , ENTRY(26) , ENTRY(27) , + ENTRY(28) , ENTRY(29) , ENTRY(30) , ENTRY(31) , + ENTRY(32) , ENTRY(33) , ENTRY(34) , ENTRY(35) , + ENTRY(36) , ENTRY(37) , ENTRY(38) , ENTRY(39) , + ENTRY(40) , ENTRY(41) , ENTRY(42) , ENTRY(43) , + ENTRY(44) , ENTRY(45) , ENTRY(46) , ENTRY(47) , + ENTRY(48) , ENTRY(49) , ENTRY(50) , ENTRY(51) , + ENTRY(52) , ENTRY(53) , ENTRY(54) , ENTRY(55) , + ENTRY(56) , ENTRY(57) , ENTRY(58) , ENTRY(59) , + ENTRY(60) , ENTRY(61) , ENTRY(62) , ENTRY(63) , + ENTRY(64) , ENTRY(65) , ENTRY(66) , ENTRY(67) , + ENTRY(68) , ENTRY(69) , ENTRY(70) , ENTRY(71) , + ENTRY(72) , ENTRY(73) , ENTRY(74) , ENTRY(75) , + ENTRY(76) , ENTRY(77) , ENTRY(78) , ENTRY(79) , + ENTRY(80) , ENTRY(81) , ENTRY(82) , ENTRY(83) , + ENTRY(84) , ENTRY(85) , ENTRY(86) , ENTRY(87) , + ENTRY(88) , ENTRY(89) , ENTRY(90) , ENTRY(91) , + ENTRY(92) , ENTRY(93) , ENTRY(94) , ENTRY(95) , + ENTRY(96) , ENTRY(97) , ENTRY(98) , ENTRY(99) , + ENTRY(100) , ENTRY(101) , ENTRY(102) , ENTRY(103) , + ENTRY(104) , ENTRY(105) , ENTRY(106) , ENTRY(107) , + ENTRY(108) , ENTRY(109) , ENTRY(110) , ENTRY(111) , + ENTRY(112) , ENTRY(113) , ENTRY(114) , ENTRY(115) , + ENTRY(116) , ENTRY(117) , ENTRY(118) , ENTRY(119) , + ENTRY(120) , ENTRY(121) , ENTRY(122) , ENTRY(123) , + ENTRY(124) , ENTRY(125) , ENTRY(126) , ENTRY(127) , + ENTRY(128) , ENTRY(129) , ENTRY(130) , ENTRY(131) , + ENTRY(132) , ENTRY(133) , ENTRY(134) , ENTRY(135) , + ENTRY(136) , ENTRY(137) , ENTRY(138) , ENTRY(139) , + ENTRY(140) , ENTRY(141) , ENTRY(142) , ENTRY(143) , + ENTRY(144) , ENTRY(145) , ENTRY(146) , ENTRY(147) , + ENTRY(148) , ENTRY(149) , ENTRY(150) , ENTRY(151) , + ENTRY(152) , ENTRY(153) , ENTRY(154) , ENTRY(155) , + ENTRY(156) , ENTRY(157) , ENTRY(158) , ENTRY(159) , + ENTRY(160) , ENTRY(161) , ENTRY(162) , ENTRY(163) , + ENTRY(164) , ENTRY(165) , ENTRY(166) , ENTRY(167) , + ENTRY(168) , ENTRY(169) , ENTRY(170) , ENTRY(171) , + ENTRY(172) , ENTRY(173) , ENTRY(174) , ENTRY(175) , + ENTRY(176) , ENTRY(177) , ENTRY(178) , ENTRY(179) , + ENTRY(180) , ENTRY(181) , ENTRY(182) , ENTRY(183) , + ENTRY(184) , ENTRY(185) , ENTRY(186) , ENTRY(187) , + ENTRY(188) , ENTRY(189) , ENTRY(190) , ENTRY(191) , + ENTRY(192) , ENTRY(193) , ENTRY(194) , ENTRY(195) , + ENTRY(196) , ENTRY(197) , ENTRY(198) , ENTRY(199) , + ENTRY(200) , ENTRY(201) , ENTRY(202) , ENTRY(203) , + ENTRY(204) , ENTRY(205) , ENTRY(206) , ENTRY(207) , + ENTRY(208) , ENTRY(209) , ENTRY(210) , ENTRY(211) , + ENTRY(212) , ENTRY(213) , ENTRY(214) , ENTRY(215) , + ENTRY(216) , ENTRY(217) , ENTRY(218) , ENTRY(219) , + ENTRY(220) , ENTRY(221) , ENTRY(222) , ENTRY(223) , + ENTRY(224) , ENTRY(225) , ENTRY(226) , ENTRY(227) , + ENTRY(228) , ENTRY(229) , ENTRY(230) , ENTRY(231) , + ENTRY(232) , ENTRY(233) , ENTRY(234) , ENTRY(235) , + ENTRY(236) , ENTRY(237) , ENTRY(238) , ENTRY(239) , + ENTRY(240) , ENTRY(241) , ENTRY(242) , ENTRY(243) , + ENTRY(244) , ENTRY(245) , ENTRY(246) , ENTRY(247) , + ENTRY(248) , ENTRY(249) , ENTRY(250) , ENTRY(251) , + ENTRY(252) , ENTRY(253) , ENTRY(254) , ENTRY(255) , +#undef ENTRY + + /* End of block */ + HUFFDEC_EXCEPTIONAL | HUFFDEC_END_OF_BLOCK, + + /* Lengths */ +#define ENTRY(length_base, num_extra_bits) \ + (((u32)(length_base) << 16) | (num_extra_bits)) + ENTRY(3 , 0) , ENTRY(4 , 0) , ENTRY(5 , 0) , ENTRY(6 , 0), + ENTRY(7 , 0) , ENTRY(8 , 0) , ENTRY(9 , 0) , ENTRY(10 , 0), + ENTRY(11 , 1) , ENTRY(13 , 1) , ENTRY(15 , 1) , ENTRY(17 , 1), + ENTRY(19 , 2) , ENTRY(23 , 2) , ENTRY(27 , 2) , ENTRY(31 , 2), + ENTRY(35 , 3) , ENTRY(43 , 3) , ENTRY(51 , 3) , ENTRY(59 , 3), + ENTRY(67 , 4) , ENTRY(83 , 4) , ENTRY(99 , 4) , ENTRY(115, 4), + ENTRY(131, 5) , ENTRY(163, 5) , ENTRY(195, 5) , ENTRY(227, 5), + ENTRY(258, 0) , ENTRY(258, 0) , ENTRY(258, 0) , +#undef ENTRY +}; + +/* Maximum number of bits that can be consumed by decoding a match offset */ +#define OFFSET_MAXBITS (DEFLATE_MAX_OFFSET_CODEWORD_LEN + \ + DEFLATE_MAX_EXTRA_OFFSET_BITS) +#define OFFSET_MAXFASTBITS (OFFSET_TABLEBITS /* no subtable needed */ + \ + DEFLATE_MAX_EXTRA_OFFSET_BITS) + +/* + * Here is the format of our offset decode table entries. Bits not explicitly + * described contain zeroes: + * + * Offsets: + * Bit 31-16: offset base value + * Bit 15: 0 (!HUFFDEC_EXCEPTIONAL) + * Bit 14: 0 (!HUFFDEC_SUBTABLE_POINTER) + * Bit 11-8: remaining codeword length + * Bit 4-0: remaining codeword length + number of extra bits + * Subtable pointer: + * Bit 31-16: index of start of subtable + * Bit 15: 1 (HUFFDEC_EXCEPTIONAL) + * Bit 14: 1 (HUFFDEC_SUBTABLE_POINTER) + * Bit 11-8: number of subtable bits + * Bit 3-0: number of main table bits + * + * These work the same way as the length entries and subtable pointer entries in + * the litlen decode table; see litlen_decode_results[] above. + */ +static const u32 offset_decode_results[] = { +#define ENTRY(offset_base, num_extra_bits) \ + (((u32)(offset_base) << 16) | (num_extra_bits)) + ENTRY(1 , 0) , ENTRY(2 , 0) , ENTRY(3 , 0) , ENTRY(4 , 0) , + ENTRY(5 , 1) , ENTRY(7 , 1) , ENTRY(9 , 2) , ENTRY(13 , 2) , + ENTRY(17 , 3) , ENTRY(25 , 3) , ENTRY(33 , 4) , ENTRY(49 , 4) , + ENTRY(65 , 5) , ENTRY(97 , 5) , ENTRY(129 , 6) , ENTRY(193 , 6) , + ENTRY(257 , 7) , ENTRY(385 , 7) , ENTRY(513 , 8) , ENTRY(769 , 8) , + ENTRY(1025 , 9) , ENTRY(1537 , 9) , ENTRY(2049 , 10) , ENTRY(3073 , 10) , + ENTRY(4097 , 11) , ENTRY(6145 , 11) , ENTRY(8193 , 12) , ENTRY(12289 , 12) , + ENTRY(16385 , 13) , ENTRY(24577 , 13) , ENTRY(24577 , 13) , ENTRY(24577 , 13) , +#undef ENTRY +}; + +/* + * The main DEFLATE decompressor structure. Since libdeflate only supports + * full-buffer decompression, this structure doesn't store the entire + * decompression state, most of which is in stack variables. Instead, this + * struct just contains the decode tables and some temporary arrays used for + * building them, as these are too large to comfortably allocate on the stack. + * + * Storing the decode tables in the decompressor struct also allows the decode + * tables for the static codes to be reused whenever two static Huffman blocks + * are decoded without an intervening dynamic block, even across streams. + */ +struct libdeflate_decompressor { + + /* + * The arrays aren't all needed at the same time. 'precode_lens' and + * 'precode_decode_table' are unneeded after 'lens' has been filled. + * Furthermore, 'lens' need not be retained after building the litlen + * and offset decode tables. In fact, 'lens' can be in union with + * 'litlen_decode_table' provided that 'offset_decode_table' is separate + * and is built first. + */ + + union { + u8 precode_lens[DEFLATE_NUM_PRECODE_SYMS]; + + struct { + u8 lens[DEFLATE_NUM_LITLEN_SYMS + + DEFLATE_NUM_OFFSET_SYMS + + DEFLATE_MAX_LENS_OVERRUN]; + + u32 precode_decode_table[PRECODE_ENOUGH]; + } l; + + u32 litlen_decode_table[LITLEN_ENOUGH]; + } u; + + u32 offset_decode_table[OFFSET_ENOUGH]; + + /* used only during build_decode_table() */ + u16 sorted_syms[DEFLATE_MAX_NUM_SYMS]; + + bool static_codes_loaded; + unsigned litlen_tablebits; + + /* The free() function for this struct, chosen at allocation time */ + free_func_t free_func; +}; + +/* + * Build a table for fast decoding of symbols from a Huffman code. As input, + * this function takes the codeword length of each symbol which may be used in + * the code. As output, it produces a decode table for the canonical Huffman + * code described by the codeword lengths. The decode table is built with the + * assumption that it will be indexed with "bit-reversed" codewords, where the + * low-order bit is the first bit of the codeword. This format is used for all + * Huffman codes in DEFLATE. + * + * @decode_table + * The array in which the decode table will be generated. This array must + * have sufficient length; see the definition of the ENOUGH numbers. + * @lens + * An array which provides, for each symbol, the length of the + * corresponding codeword in bits, or 0 if the symbol is unused. This may + * alias @decode_table, since nothing is written to @decode_table until all + * @lens have been consumed. All codeword lengths are assumed to be <= + * @max_codeword_len but are otherwise considered untrusted. If they do + * not form a valid Huffman code, then the decode table is not built and + * %false is returned. + * @num_syms + * The number of symbols in the code, including all unused symbols. + * @decode_results + * An array which gives the incomplete decode result for each symbol. The + * needed values in this array will be combined with codeword lengths to + * make the final decode table entries using make_decode_table_entry(). + * @table_bits + * The log base-2 of the number of main table entries to use. + * If @table_bits_ret != NULL, then @table_bits is treated as a maximum + * value and it will be decreased if a smaller table would be sufficient. + * @max_codeword_len + * The maximum allowed codeword length for this Huffman code. + * Must be <= DEFLATE_MAX_CODEWORD_LEN. + * @sorted_syms + * A temporary array of length @num_syms. + * @table_bits_ret + * If non-NULL, then the dynamic table_bits is enabled, and the actual + * table_bits value will be returned here. + * + * Returns %true if successful; %false if the codeword lengths do not form a + * valid Huffman code. + */ +static bool +build_decode_table(u32 decode_table[], + const u8 lens[], + const unsigned num_syms, + const u32 decode_results[], + unsigned table_bits, + unsigned max_codeword_len, + u16 *sorted_syms, + unsigned *table_bits_ret) +{ + unsigned len_counts[DEFLATE_MAX_CODEWORD_LEN + 1]; + unsigned offsets[DEFLATE_MAX_CODEWORD_LEN + 1]; + unsigned sym; /* current symbol */ + unsigned codeword; /* current codeword, bit-reversed */ + unsigned len; /* current codeword length in bits */ + unsigned count; /* num codewords remaining with this length */ + u32 codespace_used; /* codespace used out of '2^max_codeword_len' */ + unsigned cur_table_end; /* end index of current table */ + unsigned subtable_prefix; /* codeword prefix of current subtable */ + unsigned subtable_start; /* start index of current subtable */ + unsigned subtable_bits; /* log2 of current subtable length */ + + /* Count how many codewords have each length, including 0. */ + for (len = 0; len <= max_codeword_len; len++) + len_counts[len] = 0; + for (sym = 0; sym < num_syms; sym++) + len_counts[lens[sym]]++; + + /* + * Determine the actual maximum codeword length that was used, and + * decrease table_bits to it if allowed. + */ + while (max_codeword_len > 1 && len_counts[max_codeword_len] == 0) + max_codeword_len--; + if (table_bits_ret != NULL) { + table_bits = MIN(table_bits, max_codeword_len); + *table_bits_ret = table_bits; + } + + /* + * Sort the symbols primarily by increasing codeword length and + * secondarily by increasing symbol value; or equivalently by their + * codewords in lexicographic order, since a canonical code is assumed. + * + * For efficiency, also compute 'codespace_used' in the same pass over + * 'len_counts[]' used to build 'offsets[]' for sorting. + */ + + /* Ensure that 'codespace_used' cannot overflow. */ + STATIC_ASSERT(sizeof(codespace_used) == 4); + STATIC_ASSERT(UINT32_MAX / (1U << (DEFLATE_MAX_CODEWORD_LEN - 1)) >= + DEFLATE_MAX_NUM_SYMS); + + offsets[0] = 0; + offsets[1] = len_counts[0]; + codespace_used = 0; + for (len = 1; len < max_codeword_len; len++) { + offsets[len + 1] = offsets[len] + len_counts[len]; + codespace_used = (codespace_used << 1) + len_counts[len]; + } + codespace_used = (codespace_used << 1) + len_counts[len]; + + for (sym = 0; sym < num_syms; sym++) + sorted_syms[offsets[lens[sym]]++] = sym; + + sorted_syms += offsets[0]; /* Skip unused symbols */ + + /* lens[] is done being used, so we can write to decode_table[] now. */ + + /* + * Check whether the lengths form a complete code (exactly fills the + * codespace), an incomplete code (doesn't fill the codespace), or an + * overfull code (overflows the codespace). A codeword of length 'n' + * uses proportion '1/(2^n)' of the codespace. An overfull code is + * nonsensical, so is considered invalid. An incomplete code is + * considered valid only in two specific cases; see below. + */ + + /* overfull code? */ + if (unlikely(codespace_used > (1U << max_codeword_len))) + return false; + + /* incomplete code? */ + if (unlikely(codespace_used < (1U << max_codeword_len))) { + u32 entry; + unsigned i; + + if (codespace_used == 0) { + /* + * An empty code is allowed. This can happen for the + * offset code in DEFLATE, since a dynamic Huffman block + * need not contain any matches. + */ + + /* sym=0, len=1 (arbitrary) */ + entry = make_decode_table_entry(decode_results, 0, 1); + } else { + /* + * Allow codes with a single used symbol, with codeword + * length 1. The DEFLATE RFC is unclear regarding this + * case. What zlib's decompressor does is permit this + * for the litlen and offset codes and assume the + * codeword is '0' rather than '1'. We do the same + * except we allow this for precodes too, since there's + * no convincing reason to treat the codes differently. + * We also assign both codewords '0' and '1' to the + * symbol to avoid having to handle '1' specially. + */ + if (codespace_used != (1U << (max_codeword_len - 1)) || + len_counts[1] != 1) + return false; + entry = make_decode_table_entry(decode_results, + *sorted_syms, 1); + } + /* + * Note: the decode table still must be fully initialized, in + * case the stream is malformed and contains bits from the part + * of the codespace the incomplete code doesn't use. + */ + for (i = 0; i < (1U << table_bits); i++) + decode_table[i] = entry; + return true; + } + + /* + * The lengths form a complete code. Now, enumerate the codewords in + * lexicographic order and fill the decode table entries for each one. + * + * First, process all codewords with len <= table_bits. Each one gets + * '2^(table_bits-len)' direct entries in the table. + * + * Since DEFLATE uses bit-reversed codewords, these entries aren't + * consecutive but rather are spaced '2^len' entries apart. This makes + * filling them naively somewhat awkward and inefficient, since strided + * stores are less cache-friendly and preclude the use of word or + * vector-at-a-time stores to fill multiple entries per instruction. + * + * To optimize this, we incrementally double the table size. When + * processing codewords with length 'len', the table is treated as + * having only '2^len' entries, so each codeword uses just one entry. + * Then, each time 'len' is incremented, the table size is doubled and + * the first half is copied to the second half. This significantly + * improves performance over naively doing strided stores. + * + * Note that some entries copied for each table doubling may not have + * been initialized yet, but it doesn't matter since they're guaranteed + * to be initialized later (because the Huffman code is complete). + */ + codeword = 0; + len = 1; + while ((count = len_counts[len]) == 0) + len++; + cur_table_end = 1U << len; + while (len <= table_bits) { + /* Process all 'count' codewords with length 'len' bits. */ + do { + unsigned bit; + + /* Fill the first entry for the current codeword. */ + decode_table[codeword] = + make_decode_table_entry(decode_results, + *sorted_syms++, len); + + if (codeword == cur_table_end - 1) { + /* Last codeword (all 1's) */ + for (; len < table_bits; len++) { + memcpy(&decode_table[cur_table_end], + decode_table, + cur_table_end * + sizeof(decode_table[0])); + cur_table_end <<= 1; + } + return true; + } + /* + * To advance to the lexicographically next codeword in + * the canonical code, the codeword must be incremented, + * then 0's must be appended to the codeword as needed + * to match the next codeword's length. + * + * Since the codeword is bit-reversed, appending 0's is + * a no-op. However, incrementing it is nontrivial. To + * do so efficiently, use the 'bsr' instruction to find + * the last (highest order) 0 bit in the codeword, set + * it, and clear any later (higher order) 1 bits. But + * 'bsr' actually finds the highest order 1 bit, so to + * use it first flip all bits in the codeword by XOR'ing + * it with (1U << len) - 1 == cur_table_end - 1. + */ + bit = 1U << bsr32(codeword ^ (cur_table_end - 1)); + codeword &= bit - 1; + codeword |= bit; + } while (--count); + + /* Advance to the next codeword length. */ + do { + if (++len <= table_bits) { + memcpy(&decode_table[cur_table_end], + decode_table, + cur_table_end * sizeof(decode_table[0])); + cur_table_end <<= 1; + } + } while ((count = len_counts[len]) == 0); + } + + /* Process codewords with len > table_bits. These require subtables. */ + cur_table_end = 1U << table_bits; + subtable_prefix = -1; + subtable_start = 0; + for (;;) { + u32 entry; + unsigned i; + unsigned stride; + unsigned bit; + + /* + * Start a new subtable if the first 'table_bits' bits of the + * codeword don't match the prefix of the current subtable. + */ + if ((codeword & ((1U << table_bits) - 1)) != subtable_prefix) { + subtable_prefix = (codeword & ((1U << table_bits) - 1)); + subtable_start = cur_table_end; + /* + * Calculate the subtable length. If the codeword has + * length 'table_bits + n', then the subtable needs + * '2^n' entries. But it may need more; if fewer than + * '2^n' codewords of length 'table_bits + n' remain, + * then the length will need to be incremented to bring + * in longer codewords until the subtable can be + * completely filled. Note that because the Huffman + * code is complete, it will always be possible to fill + * the subtable eventually. + */ + subtable_bits = len - table_bits; + codespace_used = count; + while (codespace_used < (1U << subtable_bits)) { + subtable_bits++; + codespace_used = (codespace_used << 1) + + len_counts[table_bits + subtable_bits]; + } + cur_table_end = subtable_start + (1U << subtable_bits); + + /* + * Create the entry that points from the main table to + * the subtable. + */ + decode_table[subtable_prefix] = + ((u32)subtable_start << 16) | + HUFFDEC_EXCEPTIONAL | + HUFFDEC_SUBTABLE_POINTER | + (subtable_bits << 8) | table_bits; + } + + /* Fill the subtable entries for the current codeword. */ + entry = make_decode_table_entry(decode_results, *sorted_syms++, + len - table_bits); + i = subtable_start + (codeword >> table_bits); + stride = 1U << (len - table_bits); + do { + decode_table[i] = entry; + i += stride; + } while (i < cur_table_end); + + /* Advance to the next codeword. */ + if (codeword == (1U << len) - 1) /* last codeword (all 1's)? */ + return true; + bit = 1U << bsr32(codeword ^ ((1U << len) - 1)); + codeword &= bit - 1; + codeword |= bit; + count--; + while (count == 0) + count = len_counts[++len]; + } +} + +/* Build the decode table for the precode. */ +static bool +build_precode_decode_table(struct libdeflate_decompressor *d) +{ + /* When you change TABLEBITS, you must change ENOUGH, and vice versa! */ + STATIC_ASSERT(PRECODE_TABLEBITS == 7 && PRECODE_ENOUGH == 128); + + STATIC_ASSERT(ARRAY_LEN(precode_decode_results) == + DEFLATE_NUM_PRECODE_SYMS); + + return build_decode_table(d->u.l.precode_decode_table, + d->u.precode_lens, + DEFLATE_NUM_PRECODE_SYMS, + precode_decode_results, + PRECODE_TABLEBITS, + DEFLATE_MAX_PRE_CODEWORD_LEN, + d->sorted_syms, + NULL); +} + +/* Build the decode table for the literal/length code. */ +static bool +build_litlen_decode_table(struct libdeflate_decompressor *d, + unsigned num_litlen_syms, unsigned num_offset_syms) +{ + /* When you change TABLEBITS, you must change ENOUGH, and vice versa! */ + STATIC_ASSERT(LITLEN_TABLEBITS == 11 && LITLEN_ENOUGH == 2342); + + STATIC_ASSERT(ARRAY_LEN(litlen_decode_results) == + DEFLATE_NUM_LITLEN_SYMS); + + return build_decode_table(d->u.litlen_decode_table, + d->u.l.lens, + num_litlen_syms, + litlen_decode_results, + LITLEN_TABLEBITS, + DEFLATE_MAX_LITLEN_CODEWORD_LEN, + d->sorted_syms, + &d->litlen_tablebits); +} + +/* Build the decode table for the offset code. */ +static bool +build_offset_decode_table(struct libdeflate_decompressor *d, + unsigned num_litlen_syms, unsigned num_offset_syms) +{ + /* When you change TABLEBITS, you must change ENOUGH, and vice versa! */ + STATIC_ASSERT(OFFSET_TABLEBITS == 8 && OFFSET_ENOUGH == 402); + + STATIC_ASSERT(ARRAY_LEN(offset_decode_results) == + DEFLATE_NUM_OFFSET_SYMS); + + return build_decode_table(d->offset_decode_table, + d->u.l.lens + num_litlen_syms, + num_offset_syms, + offset_decode_results, + OFFSET_TABLEBITS, + DEFLATE_MAX_OFFSET_CODEWORD_LEN, + d->sorted_syms, + NULL); +} + +/***************************************************************************** + * Main decompression routine + *****************************************************************************/ + +typedef enum libdeflate_result (*decompress_func_t) + (struct libdeflate_decompressor * restrict d, + const void * restrict in, size_t in_nbytes, + void * restrict out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret); + +#define FUNCNAME deflate_decompress_default +#undef ATTRIBUTES +#undef EXTRACT_VARBITS +#undef EXTRACT_VARBITS8 +/* + * decompress_template.h + * + * Copyright 2016 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + */ + +/* + * This is the actual DEFLATE decompression routine, lifted out of + * deflate_decompress.c so that it can be compiled multiple times with different + * target instruction sets. + */ + +#ifndef ATTRIBUTES +# define ATTRIBUTES +#endif +#ifndef EXTRACT_VARBITS +# define EXTRACT_VARBITS(word, count) ((word) & BITMASK(count)) +#endif +#ifndef EXTRACT_VARBITS8 +# define EXTRACT_VARBITS8(word, count) ((word) & BITMASK((u8)(count))) +#endif + +static enum libdeflate_result ATTRIBUTES MAYBE_UNUSED +FUNCNAME(struct libdeflate_decompressor * restrict d, + const void * restrict in, size_t in_nbytes, + void * restrict out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret) +{ + u8 *out_next = out; + u8 * const out_end = out_next + out_nbytes_avail; + u8 * const out_fastloop_end = + out_end - MIN(out_nbytes_avail, FASTLOOP_MAX_BYTES_WRITTEN); + + /* Input bitstream state; see deflate_decompress.c for documentation */ + const u8 *in_next = in; + const u8 * const in_end = in_next + in_nbytes; + const u8 * const in_fastloop_end = + in_end - MIN(in_nbytes, FASTLOOP_MAX_BYTES_READ); + bitbuf_t bitbuf = 0; + bitbuf_t saved_bitbuf; + u32 bitsleft = 0; + size_t overread_count = 0; + + bool is_final_block; + unsigned block_type; + unsigned num_litlen_syms; + unsigned num_offset_syms; + bitbuf_t litlen_tablemask; + u32 entry; + +next_block: + /* Starting to read the next block */ + ; + + STATIC_ASSERT(CAN_CONSUME(1 + 2 + 5 + 5 + 4 + 3)); + REFILL_BITS(); + + /* BFINAL: 1 bit */ + is_final_block = bitbuf & BITMASK(1); + + /* BTYPE: 2 bits */ + block_type = (bitbuf >> 1) & BITMASK(2); + + if (block_type == DEFLATE_BLOCKTYPE_DYNAMIC_HUFFMAN) { + + /* Dynamic Huffman block */ + + /* The order in which precode lengths are stored */ + static const u8 deflate_precode_lens_permutation[DEFLATE_NUM_PRECODE_SYMS] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + unsigned num_explicit_precode_lens; + unsigned i; + + /* Read the codeword length counts. */ + + STATIC_ASSERT(DEFLATE_NUM_LITLEN_SYMS == 257 + BITMASK(5)); + num_litlen_syms = 257 + ((bitbuf >> 3) & BITMASK(5)); + + STATIC_ASSERT(DEFLATE_NUM_OFFSET_SYMS == 1 + BITMASK(5)); + num_offset_syms = 1 + ((bitbuf >> 8) & BITMASK(5)); + + STATIC_ASSERT(DEFLATE_NUM_PRECODE_SYMS == 4 + BITMASK(4)); + num_explicit_precode_lens = 4 + ((bitbuf >> 13) & BITMASK(4)); + + d->static_codes_loaded = false; + + /* + * Read the precode codeword lengths. + * + * A 64-bit bitbuffer is just one bit too small to hold the + * maximum number of precode lens, so to minimize branches we + * merge one len with the previous fields. + */ + STATIC_ASSERT(DEFLATE_MAX_PRE_CODEWORD_LEN == (1 << 3) - 1); + if (CAN_CONSUME(3 * (DEFLATE_NUM_PRECODE_SYMS - 1))) { + d->u.precode_lens[deflate_precode_lens_permutation[0]] = + (bitbuf >> 17) & BITMASK(3); + bitbuf >>= 20; + bitsleft -= 20; + REFILL_BITS(); + i = 1; + do { + d->u.precode_lens[deflate_precode_lens_permutation[i]] = + bitbuf & BITMASK(3); + bitbuf >>= 3; + bitsleft -= 3; + } while (++i < num_explicit_precode_lens); + } else { + bitbuf >>= 17; + bitsleft -= 17; + i = 0; + do { + if ((u8)bitsleft < 3) + REFILL_BITS(); + d->u.precode_lens[deflate_precode_lens_permutation[i]] = + bitbuf & BITMASK(3); + bitbuf >>= 3; + bitsleft -= 3; + } while (++i < num_explicit_precode_lens); + } + for (; i < DEFLATE_NUM_PRECODE_SYMS; i++) + d->u.precode_lens[deflate_precode_lens_permutation[i]] = 0; + + /* Build the decode table for the precode. */ + SAFETY_CHECK(build_precode_decode_table(d)); + + /* Decode the litlen and offset codeword lengths. */ + i = 0; + do { + unsigned presym; + u8 rep_val; + unsigned rep_count; + + if ((u8)bitsleft < DEFLATE_MAX_PRE_CODEWORD_LEN + 7) + REFILL_BITS(); + + /* + * The code below assumes that the precode decode table + * doesn't have any subtables. + */ + STATIC_ASSERT(PRECODE_TABLEBITS == DEFLATE_MAX_PRE_CODEWORD_LEN); + + /* Decode the next precode symbol. */ + entry = d->u.l.precode_decode_table[ + bitbuf & BITMASK(DEFLATE_MAX_PRE_CODEWORD_LEN)]; + bitbuf >>= (u8)entry; + bitsleft -= entry; /* optimization: subtract full entry */ + presym = entry >> 16; + + if (presym < 16) { + /* Explicit codeword length */ + d->u.l.lens[i++] = presym; + continue; + } + + /* Run-length encoded codeword lengths */ + + /* + * Note: we don't need to immediately verify that the + * repeat count doesn't overflow the number of elements, + * since we've sized the lens array to have enough extra + * space to allow for the worst-case overrun (138 zeroes + * when only 1 length was remaining). + * + * In the case of the small repeat counts (presyms 16 + * and 17), it is fastest to always write the maximum + * number of entries. That gets rid of branches that + * would otherwise be required. + * + * It is not just because of the numerical order that + * our checks go in the order 'presym < 16', 'presym == + * 16', and 'presym == 17'. For typical data this is + * ordered from most frequent to least frequent case. + */ + STATIC_ASSERT(DEFLATE_MAX_LENS_OVERRUN == 138 - 1); + + if (presym == 16) { + /* Repeat the previous length 3 - 6 times. */ + SAFETY_CHECK(i != 0); + rep_val = d->u.l.lens[i - 1]; + STATIC_ASSERT(3 + BITMASK(2) == 6); + rep_count = 3 + (bitbuf & BITMASK(2)); + bitbuf >>= 2; + bitsleft -= 2; + d->u.l.lens[i + 0] = rep_val; + d->u.l.lens[i + 1] = rep_val; + d->u.l.lens[i + 2] = rep_val; + d->u.l.lens[i + 3] = rep_val; + d->u.l.lens[i + 4] = rep_val; + d->u.l.lens[i + 5] = rep_val; + i += rep_count; + } else if (presym == 17) { + /* Repeat zero 3 - 10 times. */ + STATIC_ASSERT(3 + BITMASK(3) == 10); + rep_count = 3 + (bitbuf & BITMASK(3)); + bitbuf >>= 3; + bitsleft -= 3; + d->u.l.lens[i + 0] = 0; + d->u.l.lens[i + 1] = 0; + d->u.l.lens[i + 2] = 0; + d->u.l.lens[i + 3] = 0; + d->u.l.lens[i + 4] = 0; + d->u.l.lens[i + 5] = 0; + d->u.l.lens[i + 6] = 0; + d->u.l.lens[i + 7] = 0; + d->u.l.lens[i + 8] = 0; + d->u.l.lens[i + 9] = 0; + i += rep_count; + } else { + /* Repeat zero 11 - 138 times. */ + STATIC_ASSERT(11 + BITMASK(7) == 138); + rep_count = 11 + (bitbuf & BITMASK(7)); + bitbuf >>= 7; + bitsleft -= 7; + memset(&d->u.l.lens[i], 0, + rep_count * sizeof(d->u.l.lens[i])); + i += rep_count; + } + } while (i < num_litlen_syms + num_offset_syms); + + /* Unnecessary, but check this for consistency with zlib. */ + SAFETY_CHECK(i == num_litlen_syms + num_offset_syms); + + } else if (block_type == DEFLATE_BLOCKTYPE_UNCOMPRESSED) { + u16 len, nlen; + + /* + * Uncompressed block: copy 'len' bytes literally from the input + * buffer to the output buffer. + */ + + bitsleft -= 3; /* for BTYPE and BFINAL */ + + /* + * Align the bitstream to the next byte boundary. This means + * the next byte boundary as if we were reading a byte at a + * time. Therefore, we have to rewind 'in_next' by any bytes + * that have been refilled but not actually consumed yet (not + * counting overread bytes, which don't increment 'in_next'). + */ + bitsleft = (u8)bitsleft; + SAFETY_CHECK(overread_count <= (bitsleft >> 3)); + in_next -= (bitsleft >> 3) - overread_count; + overread_count = 0; + bitbuf = 0; + bitsleft = 0; + + SAFETY_CHECK(in_end - in_next >= 4); + len = get_unaligned_le16(in_next); + nlen = get_unaligned_le16(in_next + 2); + in_next += 4; + + SAFETY_CHECK(len == (u16)~nlen); + if (unlikely(len > out_end - out_next)) + return LIBDEFLATE_INSUFFICIENT_SPACE; + SAFETY_CHECK(len <= in_end - in_next); + + memcpy(out_next, in_next, len); + in_next += len; + out_next += len; + + goto block_done; + + } else { + unsigned i; + + SAFETY_CHECK(block_type == DEFLATE_BLOCKTYPE_STATIC_HUFFMAN); + + /* + * Static Huffman block: build the decode tables for the static + * codes. Skip doing so if the tables are already set up from + * an earlier static block; this speeds up decompression of + * degenerate input of many empty or very short static blocks. + * + * Afterwards, the remainder is the same as decompressing a + * dynamic Huffman block. + */ + + bitbuf >>= 3; /* for BTYPE and BFINAL */ + bitsleft -= 3; + + if (d->static_codes_loaded) + goto have_decode_tables; + + d->static_codes_loaded = true; + + STATIC_ASSERT(DEFLATE_NUM_LITLEN_SYMS == 288); + STATIC_ASSERT(DEFLATE_NUM_OFFSET_SYMS == 32); + + for (i = 0; i < 144; i++) + d->u.l.lens[i] = 8; + for (; i < 256; i++) + d->u.l.lens[i] = 9; + for (; i < 280; i++) + d->u.l.lens[i] = 7; + for (; i < 288; i++) + d->u.l.lens[i] = 8; + + for (; i < 288 + 32; i++) + d->u.l.lens[i] = 5; + + num_litlen_syms = 288; + num_offset_syms = 32; + } + + /* Decompressing a Huffman block (either dynamic or static) */ + + SAFETY_CHECK(build_offset_decode_table(d, num_litlen_syms, num_offset_syms)); + SAFETY_CHECK(build_litlen_decode_table(d, num_litlen_syms, num_offset_syms)); +have_decode_tables: + litlen_tablemask = BITMASK(d->litlen_tablebits); + + /* + * This is the "fastloop" for decoding literals and matches. It does + * bounds checks on in_next and out_next in the loop conditions so that + * additional bounds checks aren't needed inside the loop body. + * + * To reduce latency, the bitbuffer is refilled and the next litlen + * decode table entry is preloaded before each loop iteration. + */ + if (in_next >= in_fastloop_end || out_next >= out_fastloop_end) + goto generic_loop; + REFILL_BITS_IN_FASTLOOP(); + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + do { + u32 length, offset, lit; + const u8 *src; + u8 *dst; + + /* + * Consume the bits for the litlen decode table entry. Save the + * original bitbuf for later, in case the extra match length + * bits need to be extracted from it. + */ + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; /* optimization: subtract full entry */ + + /* + * Begin by checking for a "fast" literal, i.e. a literal that + * doesn't need a subtable. + */ + if (entry & HUFFDEC_LITERAL) { + /* + * On 64-bit platforms, we decode up to 2 extra fast + * literals in addition to the primary item, as this + * increases performance and still leaves enough bits + * remaining for what follows. We could actually do 3, + * assuming LITLEN_TABLEBITS=11, but that actually + * decreases performance slightly (perhaps by messing + * with the branch prediction of the conditional refill + * that happens later while decoding the match offset). + * + * Note: the definitions of FASTLOOP_MAX_BYTES_WRITTEN + * and FASTLOOP_MAX_BYTES_READ need to be updated if the + * number of extra literals decoded here is changed. + */ + if (/* enough bits for 2 fast literals + length + offset preload? */ + CAN_CONSUME_AND_THEN_PRELOAD(2 * LITLEN_TABLEBITS + + LENGTH_MAXBITS, + OFFSET_TABLEBITS) && + /* enough bits for 2 fast literals + slow literal + litlen preload? */ + CAN_CONSUME_AND_THEN_PRELOAD(2 * LITLEN_TABLEBITS + + DEFLATE_MAX_LITLEN_CODEWORD_LEN, + LITLEN_TABLEBITS)) { + /* 1st extra fast literal */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + *out_next++ = lit; + if (entry & HUFFDEC_LITERAL) { + /* 2nd extra fast literal */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + *out_next++ = lit; + if (entry & HUFFDEC_LITERAL) { + /* + * Another fast literal, but + * this one is in lieu of the + * primary item, so it doesn't + * count as one of the extras. + */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + *out_next++ = lit; + continue; + } + } + } else { + /* + * Decode a literal. While doing so, preload + * the next litlen decode table entry and refill + * the bitbuffer. To reduce latency, we've + * arranged for there to be enough "preloadable" + * bits remaining to do the table preload + * independently of the refill. + */ + STATIC_ASSERT(CAN_CONSUME_AND_THEN_PRELOAD( + LITLEN_TABLEBITS, LITLEN_TABLEBITS)); + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + *out_next++ = lit; + continue; + } + } + + /* + * It's not a literal entry, so it can be a length entry, a + * subtable pointer entry, or an end-of-block entry. Detect the + * two unlikely cases by testing the HUFFDEC_EXCEPTIONAL flag. + */ + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + /* Subtable pointer or end-of-block entry */ + + if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) + goto block_done; + + /* + * A subtable is required. Load and consume the + * subtable entry. The subtable entry can be of any + * type: literal, length, or end-of-block. + */ + entry = d->u.litlen_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + + /* + * 32-bit platforms that use the byte-at-a-time refill + * method have to do a refill here for there to always + * be enough bits to decode a literal that requires a + * subtable, then preload the next litlen decode table + * entry; or to decode a match length that requires a + * subtable, then preload the offset decode table entry. + */ + if (!CAN_CONSUME_AND_THEN_PRELOAD(DEFLATE_MAX_LITLEN_CODEWORD_LEN, + LITLEN_TABLEBITS) || + !CAN_CONSUME_AND_THEN_PRELOAD(LENGTH_MAXBITS, + OFFSET_TABLEBITS)) + REFILL_BITS_IN_FASTLOOP(); + if (entry & HUFFDEC_LITERAL) { + /* Decode a literal that required a subtable. */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + *out_next++ = lit; + continue; + } + if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) + goto block_done; + /* Else, it's a length that required a subtable. */ + } + + /* + * Decode the match length: the length base value associated + * with the litlen symbol (which we extract from the decode + * table entry), plus the extra length bits. We don't need to + * consume the extra length bits here, as they were included in + * the bits consumed by the entry earlier. We also don't need + * to check for too-long matches here, as this is inside the + * fastloop where it's already been verified that the output + * buffer has enough space remaining to copy a max-length match. + */ + length = entry >> 16; + length += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); + + /* + * Decode the match offset. There are enough "preloadable" bits + * remaining to preload the offset decode table entry, but a + * refill might be needed before consuming it. + */ + STATIC_ASSERT(CAN_CONSUME_AND_THEN_PRELOAD(LENGTH_MAXFASTBITS, + OFFSET_TABLEBITS)); + entry = d->offset_decode_table[bitbuf & BITMASK(OFFSET_TABLEBITS)]; + if (CAN_CONSUME_AND_THEN_PRELOAD(OFFSET_MAXBITS, + LITLEN_TABLEBITS)) { + /* + * Decoding a match offset on a 64-bit platform. We may + * need to refill once, but then we can decode the whole + * offset and preload the next litlen table entry. + */ + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + /* Offset codeword requires a subtable */ + if (unlikely((u8)bitsleft < OFFSET_MAXBITS + + LITLEN_TABLEBITS - PRELOAD_SLACK)) + REFILL_BITS_IN_FASTLOOP(); + bitbuf >>= OFFSET_TABLEBITS; + bitsleft -= OFFSET_TABLEBITS; + entry = d->offset_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + } else if (unlikely((u8)bitsleft < OFFSET_MAXFASTBITS + + LITLEN_TABLEBITS - PRELOAD_SLACK)) + REFILL_BITS_IN_FASTLOOP(); + } else { + /* Decoding a match offset on a 32-bit platform */ + REFILL_BITS_IN_FASTLOOP(); + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + /* Offset codeword requires a subtable */ + bitbuf >>= OFFSET_TABLEBITS; + bitsleft -= OFFSET_TABLEBITS; + entry = d->offset_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + REFILL_BITS_IN_FASTLOOP(); + /* No further refill needed before extra bits */ + STATIC_ASSERT(CAN_CONSUME( + OFFSET_MAXBITS - OFFSET_TABLEBITS)); + } else { + /* No refill needed before extra bits */ + STATIC_ASSERT(CAN_CONSUME(OFFSET_MAXFASTBITS)); + } + } + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; /* optimization: subtract full entry */ + offset = entry >> 16; + offset += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); + + /* Validate the match offset; needed even in the fastloop. */ + SAFETY_CHECK(offset <= out_next - (const u8 *)out); + src = out_next - offset; + dst = out_next; + out_next += length; + + /* + * Before starting to issue the instructions to copy the match, + * refill the bitbuffer and preload the litlen decode table + * entry for the next loop iteration. This can increase + * performance by allowing the latency of the match copy to + * overlap with these other operations. To further reduce + * latency, we've arranged for there to be enough bits remaining + * to do the table preload independently of the refill, except + * on 32-bit platforms using the byte-at-a-time refill method. + */ + if (!CAN_CONSUME_AND_THEN_PRELOAD( + MAX(OFFSET_MAXBITS - OFFSET_TABLEBITS, + OFFSET_MAXFASTBITS), + LITLEN_TABLEBITS) && + unlikely((u8)bitsleft < LITLEN_TABLEBITS - PRELOAD_SLACK)) + REFILL_BITS_IN_FASTLOOP(); + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + + /* + * Copy the match. On most CPUs the fastest method is a + * word-at-a-time copy, unconditionally copying about 5 words + * since this is enough for most matches without being too much. + * + * The normal word-at-a-time copy works for offset >= WORDBYTES, + * which is most cases. The case of offset == 1 is also common + * and is worth optimizing for, since it is just RLE encoding of + * the previous byte, which is the result of compressing long + * runs of the same byte. + * + * Writing past the match 'length' is allowed here, since it's + * been ensured there is enough output space left for a slight + * overrun. FASTLOOP_MAX_BYTES_WRITTEN needs to be updated if + * the maximum possible overrun here is changed. + */ + if (UNALIGNED_ACCESS_IS_FAST && offset >= WORDBYTES) { + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + while (dst < out_next) { + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + } + } else if (UNALIGNED_ACCESS_IS_FAST && offset == 1) { + machine_word_t v; + + /* + * This part tends to get auto-vectorized, so keep it + * copying a multiple of 16 bytes at a time. + */ + v = (machine_word_t)0x0101010101010101 * src[0]; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + while (dst < out_next) { + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + } + } else if (UNALIGNED_ACCESS_IS_FAST) { + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + do { + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + } while (dst < out_next); + } else { + *dst++ = *src++; + *dst++ = *src++; + do { + *dst++ = *src++; + } while (dst < out_next); + } + } while (in_next < in_fastloop_end && out_next < out_fastloop_end); + + /* + * This is the generic loop for decoding literals and matches. This + * handles cases where in_next and out_next are close to the end of + * their respective buffers. Usually this loop isn't performance- + * critical, as most time is spent in the fastloop above instead. We + * therefore omit some optimizations here in favor of smaller code. + */ +generic_loop: + for (;;) { + u32 length, offset; + const u8 *src; + u8 *dst; + + REFILL_BITS(); + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + if (unlikely(entry & HUFFDEC_SUBTABLE_POINTER)) { + entry = d->u.litlen_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + } + length = entry >> 16; + if (entry & HUFFDEC_LITERAL) { + if (unlikely(out_next == out_end)) + return LIBDEFLATE_INSUFFICIENT_SPACE; + *out_next++ = length; + continue; + } + if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) + goto block_done; + length += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); + if (unlikely(length > out_end - out_next)) + return LIBDEFLATE_INSUFFICIENT_SPACE; + + if (!CAN_CONSUME(LENGTH_MAXBITS + OFFSET_MAXBITS)) + REFILL_BITS(); + entry = d->offset_decode_table[bitbuf & BITMASK(OFFSET_TABLEBITS)]; + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + bitbuf >>= OFFSET_TABLEBITS; + bitsleft -= OFFSET_TABLEBITS; + entry = d->offset_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + if (!CAN_CONSUME(OFFSET_MAXBITS)) + REFILL_BITS(); + } + offset = entry >> 16; + offset += EXTRACT_VARBITS8(bitbuf, entry) >> (u8)(entry >> 8); + bitbuf >>= (u8)entry; + bitsleft -= entry; + + SAFETY_CHECK(offset <= out_next - (const u8 *)out); + src = out_next - offset; + dst = out_next; + out_next += length; + + STATIC_ASSERT(DEFLATE_MIN_MATCH_LEN == 3); + *dst++ = *src++; + *dst++ = *src++; + do { + *dst++ = *src++; + } while (dst < out_next); + } + +block_done: + /* Finished decoding a block */ + + if (!is_final_block) + goto next_block; + + /* That was the last block. */ + + bitsleft = (u8)bitsleft; + + /* + * If any of the implicit appended zero bytes were consumed (not just + * refilled) before hitting end of stream, then the data is bad. + */ + SAFETY_CHECK(overread_count <= (bitsleft >> 3)); + + /* Optionally return the actual number of bytes consumed. */ + if (actual_in_nbytes_ret) { + /* Don't count bytes that were refilled but not consumed. */ + in_next -= (bitsleft >> 3) - overread_count; + + *actual_in_nbytes_ret = in_next - (u8 *)in; + } + + /* Optionally return the actual number of bytes written. */ + if (actual_out_nbytes_ret) { + *actual_out_nbytes_ret = out_next - (u8 *)out; + } else { + if (out_next != out_end) + return LIBDEFLATE_SHORT_OUTPUT; + } + return LIBDEFLATE_SUCCESS; +} + +#undef FUNCNAME +#undef ATTRIBUTES +#undef EXTRACT_VARBITS +#undef EXTRACT_VARBITS8 + + +/* Include architecture-specific implementation(s) if available. */ +#undef DEFAULT_IMPL +#undef arch_select_decompress_func +#if defined(ARCH_X86_32) || defined(ARCH_X86_64) +#ifndef LIB_X86_DECOMPRESS_IMPL_H +#define LIB_X86_DECOMPRESS_IMPL_H + +/* + * BMI2 optimized version + * + * FIXME: with MSVC, this isn't actually compiled with BMI2 code generation + * enabled yet. That would require that this be moved to its own .c file. + */ +#if HAVE_BMI2_INTRIN +# define deflate_decompress_bmi2 deflate_decompress_bmi2 +# define FUNCNAME deflate_decompress_bmi2 +# if !HAVE_BMI2_NATIVE +# define ATTRIBUTES _target_attribute("bmi2") +# endif + /* + * Even with __attribute__((target("bmi2"))), gcc doesn't reliably use the + * bzhi instruction for 'word & BITMASK(count)'. So use the bzhi intrinsic + * explicitly. EXTRACT_VARBITS() is equivalent to 'word & BITMASK(count)'; + * EXTRACT_VARBITS8() is equivalent to 'word & BITMASK((u8)count)'. + * Nevertheless, their implementation using the bzhi intrinsic is identical, + * as the bzhi instruction truncates the count to 8 bits implicitly. + */ +# ifndef __clang__ +# include <immintrin.h> +# ifdef ARCH_X86_64 +# define EXTRACT_VARBITS(word, count) _bzhi_u64((word), (count)) +# define EXTRACT_VARBITS8(word, count) _bzhi_u64((word), (count)) +# else +# define EXTRACT_VARBITS(word, count) _bzhi_u32((word), (count)) +# define EXTRACT_VARBITS8(word, count) _bzhi_u32((word), (count)) +# endif +# endif +/* + * decompress_template.h + * + * Copyright 2016 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + */ + +/* + * This is the actual DEFLATE decompression routine, lifted out of + * deflate_decompress.c so that it can be compiled multiple times with different + * target instruction sets. + */ + +#ifndef ATTRIBUTES +# define ATTRIBUTES +#endif +#ifndef EXTRACT_VARBITS +# define EXTRACT_VARBITS(word, count) ((word) & BITMASK(count)) +#endif +#ifndef EXTRACT_VARBITS8 +# define EXTRACT_VARBITS8(word, count) ((word) & BITMASK((u8)(count))) +#endif + +static enum libdeflate_result ATTRIBUTES MAYBE_UNUSED +FUNCNAME(struct libdeflate_decompressor * restrict d, + const void * restrict in, size_t in_nbytes, + void * restrict out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret) +{ + u8 *out_next = out; + u8 * const out_end = out_next + out_nbytes_avail; + u8 * const out_fastloop_end = + out_end - MIN(out_nbytes_avail, FASTLOOP_MAX_BYTES_WRITTEN); + + /* Input bitstream state; see deflate_decompress.c for documentation */ + const u8 *in_next = in; + const u8 * const in_end = in_next + in_nbytes; + const u8 * const in_fastloop_end = + in_end - MIN(in_nbytes, FASTLOOP_MAX_BYTES_READ); + bitbuf_t bitbuf = 0; + bitbuf_t saved_bitbuf; + u32 bitsleft = 0; + size_t overread_count = 0; + + bool is_final_block; + unsigned block_type; + unsigned num_litlen_syms; + unsigned num_offset_syms; + bitbuf_t litlen_tablemask; + u32 entry; + +next_block: + /* Starting to read the next block */ + ; + + STATIC_ASSERT(CAN_CONSUME(1 + 2 + 5 + 5 + 4 + 3)); + REFILL_BITS(); + + /* BFINAL: 1 bit */ + is_final_block = bitbuf & BITMASK(1); + + /* BTYPE: 2 bits */ + block_type = (bitbuf >> 1) & BITMASK(2); + + if (block_type == DEFLATE_BLOCKTYPE_DYNAMIC_HUFFMAN) { + + /* Dynamic Huffman block */ + + /* The order in which precode lengths are stored */ + static const u8 deflate_precode_lens_permutation[DEFLATE_NUM_PRECODE_SYMS] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + unsigned num_explicit_precode_lens; + unsigned i; + + /* Read the codeword length counts. */ + + STATIC_ASSERT(DEFLATE_NUM_LITLEN_SYMS == 257 + BITMASK(5)); + num_litlen_syms = 257 + ((bitbuf >> 3) & BITMASK(5)); + + STATIC_ASSERT(DEFLATE_NUM_OFFSET_SYMS == 1 + BITMASK(5)); + num_offset_syms = 1 + ((bitbuf >> 8) & BITMASK(5)); + + STATIC_ASSERT(DEFLATE_NUM_PRECODE_SYMS == 4 + BITMASK(4)); + num_explicit_precode_lens = 4 + ((bitbuf >> 13) & BITMASK(4)); + + d->static_codes_loaded = false; + + /* + * Read the precode codeword lengths. + * + * A 64-bit bitbuffer is just one bit too small to hold the + * maximum number of precode lens, so to minimize branches we + * merge one len with the previous fields. + */ + STATIC_ASSERT(DEFLATE_MAX_PRE_CODEWORD_LEN == (1 << 3) - 1); + if (CAN_CONSUME(3 * (DEFLATE_NUM_PRECODE_SYMS - 1))) { + d->u.precode_lens[deflate_precode_lens_permutation[0]] = + (bitbuf >> 17) & BITMASK(3); + bitbuf >>= 20; + bitsleft -= 20; + REFILL_BITS(); + i = 1; + do { + d->u.precode_lens[deflate_precode_lens_permutation[i]] = + bitbuf & BITMASK(3); + bitbuf >>= 3; + bitsleft -= 3; + } while (++i < num_explicit_precode_lens); + } else { + bitbuf >>= 17; + bitsleft -= 17; + i = 0; + do { + if ((u8)bitsleft < 3) + REFILL_BITS(); + d->u.precode_lens[deflate_precode_lens_permutation[i]] = + bitbuf & BITMASK(3); + bitbuf >>= 3; + bitsleft -= 3; + } while (++i < num_explicit_precode_lens); + } + for (; i < DEFLATE_NUM_PRECODE_SYMS; i++) + d->u.precode_lens[deflate_precode_lens_permutation[i]] = 0; + + /* Build the decode table for the precode. */ + SAFETY_CHECK(build_precode_decode_table(d)); + + /* Decode the litlen and offset codeword lengths. */ + i = 0; + do { + unsigned presym; + u8 rep_val; + unsigned rep_count; + + if ((u8)bitsleft < DEFLATE_MAX_PRE_CODEWORD_LEN + 7) + REFILL_BITS(); + + /* + * The code below assumes that the precode decode table + * doesn't have any subtables. + */ + STATIC_ASSERT(PRECODE_TABLEBITS == DEFLATE_MAX_PRE_CODEWORD_LEN); + + /* Decode the next precode symbol. */ + entry = d->u.l.precode_decode_table[ + bitbuf & BITMASK(DEFLATE_MAX_PRE_CODEWORD_LEN)]; + bitbuf >>= (u8)entry; + bitsleft -= entry; /* optimization: subtract full entry */ + presym = entry >> 16; + + if (presym < 16) { + /* Explicit codeword length */ + d->u.l.lens[i++] = presym; + continue; + } + + /* Run-length encoded codeword lengths */ + + /* + * Note: we don't need to immediately verify that the + * repeat count doesn't overflow the number of elements, + * since we've sized the lens array to have enough extra + * space to allow for the worst-case overrun (138 zeroes + * when only 1 length was remaining). + * + * In the case of the small repeat counts (presyms 16 + * and 17), it is fastest to always write the maximum + * number of entries. That gets rid of branches that + * would otherwise be required. + * + * It is not just because of the numerical order that + * our checks go in the order 'presym < 16', 'presym == + * 16', and 'presym == 17'. For typical data this is + * ordered from most frequent to least frequent case. + */ + STATIC_ASSERT(DEFLATE_MAX_LENS_OVERRUN == 138 - 1); + + if (presym == 16) { + /* Repeat the previous length 3 - 6 times. */ + SAFETY_CHECK(i != 0); + rep_val = d->u.l.lens[i - 1]; + STATIC_ASSERT(3 + BITMASK(2) == 6); + rep_count = 3 + (bitbuf & BITMASK(2)); + bitbuf >>= 2; + bitsleft -= 2; + d->u.l.lens[i + 0] = rep_val; + d->u.l.lens[i + 1] = rep_val; + d->u.l.lens[i + 2] = rep_val; + d->u.l.lens[i + 3] = rep_val; + d->u.l.lens[i + 4] = rep_val; + d->u.l.lens[i + 5] = rep_val; + i += rep_count; + } else if (presym == 17) { + /* Repeat zero 3 - 10 times. */ + STATIC_ASSERT(3 + BITMASK(3) == 10); + rep_count = 3 + (bitbuf & BITMASK(3)); + bitbuf >>= 3; + bitsleft -= 3; + d->u.l.lens[i + 0] = 0; + d->u.l.lens[i + 1] = 0; + d->u.l.lens[i + 2] = 0; + d->u.l.lens[i + 3] = 0; + d->u.l.lens[i + 4] = 0; + d->u.l.lens[i + 5] = 0; + d->u.l.lens[i + 6] = 0; + d->u.l.lens[i + 7] = 0; + d->u.l.lens[i + 8] = 0; + d->u.l.lens[i + 9] = 0; + i += rep_count; + } else { + /* Repeat zero 11 - 138 times. */ + STATIC_ASSERT(11 + BITMASK(7) == 138); + rep_count = 11 + (bitbuf & BITMASK(7)); + bitbuf >>= 7; + bitsleft -= 7; + memset(&d->u.l.lens[i], 0, + rep_count * sizeof(d->u.l.lens[i])); + i += rep_count; + } + } while (i < num_litlen_syms + num_offset_syms); + + /* Unnecessary, but check this for consistency with zlib. */ + SAFETY_CHECK(i == num_litlen_syms + num_offset_syms); + + } else if (block_type == DEFLATE_BLOCKTYPE_UNCOMPRESSED) { + u16 len, nlen; + + /* + * Uncompressed block: copy 'len' bytes literally from the input + * buffer to the output buffer. + */ + + bitsleft -= 3; /* for BTYPE and BFINAL */ + + /* + * Align the bitstream to the next byte boundary. This means + * the next byte boundary as if we were reading a byte at a + * time. Therefore, we have to rewind 'in_next' by any bytes + * that have been refilled but not actually consumed yet (not + * counting overread bytes, which don't increment 'in_next'). + */ + bitsleft = (u8)bitsleft; + SAFETY_CHECK(overread_count <= (bitsleft >> 3)); + in_next -= (bitsleft >> 3) - overread_count; + overread_count = 0; + bitbuf = 0; + bitsleft = 0; + + SAFETY_CHECK(in_end - in_next >= 4); + len = get_unaligned_le16(in_next); + nlen = get_unaligned_le16(in_next + 2); + in_next += 4; + + SAFETY_CHECK(len == (u16)~nlen); + if (unlikely(len > out_end - out_next)) + return LIBDEFLATE_INSUFFICIENT_SPACE; + SAFETY_CHECK(len <= in_end - in_next); + + memcpy(out_next, in_next, len); + in_next += len; + out_next += len; + + goto block_done; + + } else { + unsigned i; + + SAFETY_CHECK(block_type == DEFLATE_BLOCKTYPE_STATIC_HUFFMAN); + + /* + * Static Huffman block: build the decode tables for the static + * codes. Skip doing so if the tables are already set up from + * an earlier static block; this speeds up decompression of + * degenerate input of many empty or very short static blocks. + * + * Afterwards, the remainder is the same as decompressing a + * dynamic Huffman block. + */ + + bitbuf >>= 3; /* for BTYPE and BFINAL */ + bitsleft -= 3; + + if (d->static_codes_loaded) + goto have_decode_tables; + + d->static_codes_loaded = true; + + STATIC_ASSERT(DEFLATE_NUM_LITLEN_SYMS == 288); + STATIC_ASSERT(DEFLATE_NUM_OFFSET_SYMS == 32); + + for (i = 0; i < 144; i++) + d->u.l.lens[i] = 8; + for (; i < 256; i++) + d->u.l.lens[i] = 9; + for (; i < 280; i++) + d->u.l.lens[i] = 7; + for (; i < 288; i++) + d->u.l.lens[i] = 8; + + for (; i < 288 + 32; i++) + d->u.l.lens[i] = 5; + + num_litlen_syms = 288; + num_offset_syms = 32; + } + + /* Decompressing a Huffman block (either dynamic or static) */ + + SAFETY_CHECK(build_offset_decode_table(d, num_litlen_syms, num_offset_syms)); + SAFETY_CHECK(build_litlen_decode_table(d, num_litlen_syms, num_offset_syms)); +have_decode_tables: + litlen_tablemask = BITMASK(d->litlen_tablebits); + + /* + * This is the "fastloop" for decoding literals and matches. It does + * bounds checks on in_next and out_next in the loop conditions so that + * additional bounds checks aren't needed inside the loop body. + * + * To reduce latency, the bitbuffer is refilled and the next litlen + * decode table entry is preloaded before each loop iteration. + */ + if (in_next >= in_fastloop_end || out_next >= out_fastloop_end) + goto generic_loop; + REFILL_BITS_IN_FASTLOOP(); + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + do { + u32 length, offset, lit; + const u8 *src; + u8 *dst; + + /* + * Consume the bits for the litlen decode table entry. Save the + * original bitbuf for later, in case the extra match length + * bits need to be extracted from it. + */ + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; /* optimization: subtract full entry */ + + /* + * Begin by checking for a "fast" literal, i.e. a literal that + * doesn't need a subtable. + */ + if (entry & HUFFDEC_LITERAL) { + /* + * On 64-bit platforms, we decode up to 2 extra fast + * literals in addition to the primary item, as this + * increases performance and still leaves enough bits + * remaining for what follows. We could actually do 3, + * assuming LITLEN_TABLEBITS=11, but that actually + * decreases performance slightly (perhaps by messing + * with the branch prediction of the conditional refill + * that happens later while decoding the match offset). + * + * Note: the definitions of FASTLOOP_MAX_BYTES_WRITTEN + * and FASTLOOP_MAX_BYTES_READ need to be updated if the + * number of extra literals decoded here is changed. + */ + if (/* enough bits for 2 fast literals + length + offset preload? */ + CAN_CONSUME_AND_THEN_PRELOAD(2 * LITLEN_TABLEBITS + + LENGTH_MAXBITS, + OFFSET_TABLEBITS) && + /* enough bits for 2 fast literals + slow literal + litlen preload? */ + CAN_CONSUME_AND_THEN_PRELOAD(2 * LITLEN_TABLEBITS + + DEFLATE_MAX_LITLEN_CODEWORD_LEN, + LITLEN_TABLEBITS)) { + /* 1st extra fast literal */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + *out_next++ = lit; + if (entry & HUFFDEC_LITERAL) { + /* 2nd extra fast literal */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + *out_next++ = lit; + if (entry & HUFFDEC_LITERAL) { + /* + * Another fast literal, but + * this one is in lieu of the + * primary item, so it doesn't + * count as one of the extras. + */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + *out_next++ = lit; + continue; + } + } + } else { + /* + * Decode a literal. While doing so, preload + * the next litlen decode table entry and refill + * the bitbuffer. To reduce latency, we've + * arranged for there to be enough "preloadable" + * bits remaining to do the table preload + * independently of the refill. + */ + STATIC_ASSERT(CAN_CONSUME_AND_THEN_PRELOAD( + LITLEN_TABLEBITS, LITLEN_TABLEBITS)); + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + *out_next++ = lit; + continue; + } + } + + /* + * It's not a literal entry, so it can be a length entry, a + * subtable pointer entry, or an end-of-block entry. Detect the + * two unlikely cases by testing the HUFFDEC_EXCEPTIONAL flag. + */ + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + /* Subtable pointer or end-of-block entry */ + + if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) + goto block_done; + + /* + * A subtable is required. Load and consume the + * subtable entry. The subtable entry can be of any + * type: literal, length, or end-of-block. + */ + entry = d->u.litlen_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + + /* + * 32-bit platforms that use the byte-at-a-time refill + * method have to do a refill here for there to always + * be enough bits to decode a literal that requires a + * subtable, then preload the next litlen decode table + * entry; or to decode a match length that requires a + * subtable, then preload the offset decode table entry. + */ + if (!CAN_CONSUME_AND_THEN_PRELOAD(DEFLATE_MAX_LITLEN_CODEWORD_LEN, + LITLEN_TABLEBITS) || + !CAN_CONSUME_AND_THEN_PRELOAD(LENGTH_MAXBITS, + OFFSET_TABLEBITS)) + REFILL_BITS_IN_FASTLOOP(); + if (entry & HUFFDEC_LITERAL) { + /* Decode a literal that required a subtable. */ + lit = entry >> 16; + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + *out_next++ = lit; + continue; + } + if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) + goto block_done; + /* Else, it's a length that required a subtable. */ + } + + /* + * Decode the match length: the length base value associated + * with the litlen symbol (which we extract from the decode + * table entry), plus the extra length bits. We don't need to + * consume the extra length bits here, as they were included in + * the bits consumed by the entry earlier. We also don't need + * to check for too-long matches here, as this is inside the + * fastloop where it's already been verified that the output + * buffer has enough space remaining to copy a max-length match. + */ + length = entry >> 16; + length += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); + + /* + * Decode the match offset. There are enough "preloadable" bits + * remaining to preload the offset decode table entry, but a + * refill might be needed before consuming it. + */ + STATIC_ASSERT(CAN_CONSUME_AND_THEN_PRELOAD(LENGTH_MAXFASTBITS, + OFFSET_TABLEBITS)); + entry = d->offset_decode_table[bitbuf & BITMASK(OFFSET_TABLEBITS)]; + if (CAN_CONSUME_AND_THEN_PRELOAD(OFFSET_MAXBITS, + LITLEN_TABLEBITS)) { + /* + * Decoding a match offset on a 64-bit platform. We may + * need to refill once, but then we can decode the whole + * offset and preload the next litlen table entry. + */ + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + /* Offset codeword requires a subtable */ + if (unlikely((u8)bitsleft < OFFSET_MAXBITS + + LITLEN_TABLEBITS - PRELOAD_SLACK)) + REFILL_BITS_IN_FASTLOOP(); + bitbuf >>= OFFSET_TABLEBITS; + bitsleft -= OFFSET_TABLEBITS; + entry = d->offset_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + } else if (unlikely((u8)bitsleft < OFFSET_MAXFASTBITS + + LITLEN_TABLEBITS - PRELOAD_SLACK)) + REFILL_BITS_IN_FASTLOOP(); + } else { + /* Decoding a match offset on a 32-bit platform */ + REFILL_BITS_IN_FASTLOOP(); + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + /* Offset codeword requires a subtable */ + bitbuf >>= OFFSET_TABLEBITS; + bitsleft -= OFFSET_TABLEBITS; + entry = d->offset_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + REFILL_BITS_IN_FASTLOOP(); + /* No further refill needed before extra bits */ + STATIC_ASSERT(CAN_CONSUME( + OFFSET_MAXBITS - OFFSET_TABLEBITS)); + } else { + /* No refill needed before extra bits */ + STATIC_ASSERT(CAN_CONSUME(OFFSET_MAXFASTBITS)); + } + } + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; /* optimization: subtract full entry */ + offset = entry >> 16; + offset += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); + + /* Validate the match offset; needed even in the fastloop. */ + SAFETY_CHECK(offset <= out_next - (const u8 *)out); + src = out_next - offset; + dst = out_next; + out_next += length; + + /* + * Before starting to issue the instructions to copy the match, + * refill the bitbuffer and preload the litlen decode table + * entry for the next loop iteration. This can increase + * performance by allowing the latency of the match copy to + * overlap with these other operations. To further reduce + * latency, we've arranged for there to be enough bits remaining + * to do the table preload independently of the refill, except + * on 32-bit platforms using the byte-at-a-time refill method. + */ + if (!CAN_CONSUME_AND_THEN_PRELOAD( + MAX(OFFSET_MAXBITS - OFFSET_TABLEBITS, + OFFSET_MAXFASTBITS), + LITLEN_TABLEBITS) && + unlikely((u8)bitsleft < LITLEN_TABLEBITS - PRELOAD_SLACK)) + REFILL_BITS_IN_FASTLOOP(); + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + REFILL_BITS_IN_FASTLOOP(); + + /* + * Copy the match. On most CPUs the fastest method is a + * word-at-a-time copy, unconditionally copying about 5 words + * since this is enough for most matches without being too much. + * + * The normal word-at-a-time copy works for offset >= WORDBYTES, + * which is most cases. The case of offset == 1 is also common + * and is worth optimizing for, since it is just RLE encoding of + * the previous byte, which is the result of compressing long + * runs of the same byte. + * + * Writing past the match 'length' is allowed here, since it's + * been ensured there is enough output space left for a slight + * overrun. FASTLOOP_MAX_BYTES_WRITTEN needs to be updated if + * the maximum possible overrun here is changed. + */ + if (UNALIGNED_ACCESS_IS_FAST && offset >= WORDBYTES) { + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + while (dst < out_next) { + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + store_word_unaligned(load_word_unaligned(src), dst); + src += WORDBYTES; + dst += WORDBYTES; + } + } else if (UNALIGNED_ACCESS_IS_FAST && offset == 1) { + machine_word_t v; + + /* + * This part tends to get auto-vectorized, so keep it + * copying a multiple of 16 bytes at a time. + */ + v = (machine_word_t)0x0101010101010101 * src[0]; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + while (dst < out_next) { + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + store_word_unaligned(v, dst); + dst += WORDBYTES; + } + } else if (UNALIGNED_ACCESS_IS_FAST) { + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + do { + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + store_word_unaligned(load_word_unaligned(src), dst); + src += offset; + dst += offset; + } while (dst < out_next); + } else { + *dst++ = *src++; + *dst++ = *src++; + do { + *dst++ = *src++; + } while (dst < out_next); + } + } while (in_next < in_fastloop_end && out_next < out_fastloop_end); + + /* + * This is the generic loop for decoding literals and matches. This + * handles cases where in_next and out_next are close to the end of + * their respective buffers. Usually this loop isn't performance- + * critical, as most time is spent in the fastloop above instead. We + * therefore omit some optimizations here in favor of smaller code. + */ +generic_loop: + for (;;) { + u32 length, offset; + const u8 *src; + u8 *dst; + + REFILL_BITS(); + entry = d->u.litlen_decode_table[bitbuf & litlen_tablemask]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + if (unlikely(entry & HUFFDEC_SUBTABLE_POINTER)) { + entry = d->u.litlen_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + saved_bitbuf = bitbuf; + bitbuf >>= (u8)entry; + bitsleft -= entry; + } + length = entry >> 16; + if (entry & HUFFDEC_LITERAL) { + if (unlikely(out_next == out_end)) + return LIBDEFLATE_INSUFFICIENT_SPACE; + *out_next++ = length; + continue; + } + if (unlikely(entry & HUFFDEC_END_OF_BLOCK)) + goto block_done; + length += EXTRACT_VARBITS8(saved_bitbuf, entry) >> (u8)(entry >> 8); + if (unlikely(length > out_end - out_next)) + return LIBDEFLATE_INSUFFICIENT_SPACE; + + if (!CAN_CONSUME(LENGTH_MAXBITS + OFFSET_MAXBITS)) + REFILL_BITS(); + entry = d->offset_decode_table[bitbuf & BITMASK(OFFSET_TABLEBITS)]; + if (unlikely(entry & HUFFDEC_EXCEPTIONAL)) { + bitbuf >>= OFFSET_TABLEBITS; + bitsleft -= OFFSET_TABLEBITS; + entry = d->offset_decode_table[(entry >> 16) + + EXTRACT_VARBITS(bitbuf, (entry >> 8) & 0x3F)]; + if (!CAN_CONSUME(OFFSET_MAXBITS)) + REFILL_BITS(); + } + offset = entry >> 16; + offset += EXTRACT_VARBITS8(bitbuf, entry) >> (u8)(entry >> 8); + bitbuf >>= (u8)entry; + bitsleft -= entry; + + SAFETY_CHECK(offset <= out_next - (const u8 *)out); + src = out_next - offset; + dst = out_next; + out_next += length; + + STATIC_ASSERT(DEFLATE_MIN_MATCH_LEN == 3); + *dst++ = *src++; + *dst++ = *src++; + do { + *dst++ = *src++; + } while (dst < out_next); + } + +block_done: + /* Finished decoding a block */ + + if (!is_final_block) + goto next_block; + + /* That was the last block. */ + + bitsleft = (u8)bitsleft; + + /* + * If any of the implicit appended zero bytes were consumed (not just + * refilled) before hitting end of stream, then the data is bad. + */ + SAFETY_CHECK(overread_count <= (bitsleft >> 3)); + + /* Optionally return the actual number of bytes consumed. */ + if (actual_in_nbytes_ret) { + /* Don't count bytes that were refilled but not consumed. */ + in_next -= (bitsleft >> 3) - overread_count; + + *actual_in_nbytes_ret = in_next - (u8 *)in; + } + + /* Optionally return the actual number of bytes written. */ + if (actual_out_nbytes_ret) { + *actual_out_nbytes_ret = out_next - (u8 *)out; + } else { + if (out_next != out_end) + return LIBDEFLATE_SHORT_OUTPUT; + } + return LIBDEFLATE_SUCCESS; +} + +#undef FUNCNAME +#undef ATTRIBUTES +#undef EXTRACT_VARBITS +#undef EXTRACT_VARBITS8 + +#endif /* HAVE_BMI2_INTRIN */ + +#if defined(deflate_decompress_bmi2) && HAVE_BMI2_NATIVE +#define DEFAULT_IMPL deflate_decompress_bmi2 +#else +static inline decompress_func_t +arch_select_decompress_func(void) +{ +#ifdef deflate_decompress_bmi2 + if (HAVE_BMI2(get_x86_cpu_features())) + return deflate_decompress_bmi2; +#endif + return NULL; +} +#define arch_select_decompress_func arch_select_decompress_func +#endif + +#endif /* LIB_X86_DECOMPRESS_IMPL_H */ + +#endif + +#ifndef DEFAULT_IMPL +# define DEFAULT_IMPL deflate_decompress_default +#endif + +#ifdef arch_select_decompress_func +static enum libdeflate_result +dispatch_decomp(struct libdeflate_decompressor *d, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret); + +static volatile decompress_func_t decompress_impl = dispatch_decomp; + +/* Choose the best implementation at runtime. */ +static enum libdeflate_result +dispatch_decomp(struct libdeflate_decompressor *d, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, size_t *actual_out_nbytes_ret) +{ + decompress_func_t f = arch_select_decompress_func(); + + if (f == NULL) + f = DEFAULT_IMPL; + + decompress_impl = f; + return f(d, in, in_nbytes, out, out_nbytes_avail, + actual_in_nbytes_ret, actual_out_nbytes_ret); +} +#else +/* The best implementation is statically known, so call it directly. */ +# define decompress_impl DEFAULT_IMPL +#endif + +/* + * This is the main DEFLATE decompression routine. See libdeflate.h for the + * documentation. + * + * Note that the real code is in decompress_template.h. The part here just + * handles calling the appropriate implementation depending on the CPU features + * at runtime. + */ +LIBDEFLATEAPI enum libdeflate_result +libdeflate_deflate_decompress_ex(struct libdeflate_decompressor *d, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, + size_t *actual_out_nbytes_ret) +{ + return decompress_impl(d, in, in_nbytes, out, out_nbytes_avail, + actual_in_nbytes_ret, actual_out_nbytes_ret); +} + +LIBDEFLATEAPI enum libdeflate_result +libdeflate_deflate_decompress(struct libdeflate_decompressor *d, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_out_nbytes_ret) +{ + return libdeflate_deflate_decompress_ex(d, in, in_nbytes, + out, out_nbytes_avail, + NULL, actual_out_nbytes_ret); +} + +LIBDEFLATEAPI struct libdeflate_decompressor * +libdeflate_alloc_decompressor_ex(const struct libdeflate_options *options) +{ + struct libdeflate_decompressor *d; + + /* + * Note: if more fields are added to libdeflate_options, this code will + * need to be updated to support both the old and new structs. + */ + if (options->sizeof_options != sizeof(*options)) + return NULL; + + d = (options->malloc_func ? options->malloc_func : + libdeflate_default_malloc_func)(sizeof(*d)); + if (d == NULL) + return NULL; + /* + * Note that only certain parts of the decompressor actually must be + * initialized here: + * + * - 'static_codes_loaded' must be initialized to false. + * + * - The first half of the main portion of each decode table must be + * initialized to any value, to avoid reading from uninitialized + * memory during table expansion in build_decode_table(). (Although, + * this is really just to avoid warnings with dynamic tools like + * valgrind, since build_decode_table() is guaranteed to initialize + * all entries eventually anyway.) + * + * - 'free_func' must be set. + * + * But for simplicity, we currently just zero the whole decompressor. + */ + memset(d, 0, sizeof(*d)); + d->free_func = options->free_func ? + options->free_func : libdeflate_default_free_func; + return d; +} + +LIBDEFLATEAPI struct libdeflate_decompressor * +libdeflate_alloc_decompressor(void) +{ + static const struct libdeflate_options defaults = { + .sizeof_options = sizeof(defaults), + }; + return libdeflate_alloc_decompressor_ex(&defaults); +} + +LIBDEFLATEAPI void +libdeflate_free_decompressor(struct libdeflate_decompressor *d) +{ + if (d) + d->free_func(d); +} + + +/* + * utils.c - utility functions for libdeflate + * + * Copyright 2016 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + */ + +#ifdef FREESTANDING +# define malloc NULL +# define free NULL +#else +# include <stdlib.h> +#endif + +malloc_func_t libdeflate_default_malloc_func = malloc; +free_func_t libdeflate_default_free_func = free; + +void * +libdeflate_aligned_malloc(malloc_func_t malloc_func, + size_t alignment, size_t size) +{ + void *ptr = (*malloc_func)(sizeof(void *) + alignment - 1 + size); + + if (ptr) { + void *orig_ptr = ptr; + + ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment); + ((void **)ptr)[-1] = orig_ptr; + } + return ptr; +} + +void +libdeflate_aligned_free(free_func_t free_func, void *ptr) +{ + (*free_func)(((void **)ptr)[-1]); +} + +LIBDEFLATEAPI void +libdeflate_set_memory_allocator(malloc_func_t malloc_func, + free_func_t free_func) +{ + libdeflate_default_malloc_func = malloc_func; + libdeflate_default_free_func = free_func; +} + +/* + * Implementations of libc functions for freestanding library builds. + * Normal library builds don't use these. Not optimized yet; usually the + * compiler expands these functions and doesn't actually call them anyway. + */ +#ifdef FREESTANDING +#undef memset +void * __attribute__((weak)) +memset(void *s, int c, size_t n) +{ + u8 *p = s; + size_t i; + + for (i = 0; i < n; i++) + p[i] = c; + return s; +} + +#undef memcpy +void * __attribute__((weak)) +memcpy(void *dest, const void *src, size_t n) +{ + u8 *d = dest; + const u8 *s = src; + size_t i; + + for (i = 0; i < n; i++) + d[i] = s[i]; + return dest; +} + +#undef memmove +void * __attribute__((weak)) +memmove(void *dest, const void *src, size_t n) +{ + u8 *d = dest; + const u8 *s = src; + size_t i; + + if (d <= s) + return memcpy(d, s, n); + + for (i = n; i > 0; i--) + d[i - 1] = s[i - 1]; + return dest; +} + +#undef memcmp +int __attribute__((weak)) +memcmp(const void *s1, const void *s2, size_t n) +{ + const u8 *p1 = s1; + const u8 *p2 = s2; + size_t i; + + for (i = 0; i < n; i++) { + if (p1[i] != p2[i]) + return (int)p1[i] - (int)p2[i]; + } + return 0; +} +#endif /* FREESTANDING */ + +#ifdef LIBDEFLATE_ENABLE_ASSERTIONS +#include <stdio.h> +#include <stdlib.h> +void +libdeflate_assertion_failed(const char *expr, const char *file, int line) +{ + fprintf(stderr, "Assertion failed: %s at %s:%d\n", expr, file, line); + abort(); +} +#endif /* LIBDEFLATE_ENABLE_ASSERTIONS */ + +/* + * x86/cpu_features.c - feature detection for x86 CPUs + * + * Copyright 2016 Eric Biggers + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 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 OR COPYRIGHT + * HOLDERS 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. + */ + +#if HAVE_DYNAMIC_X86_CPU_FEATURES + +/* + * With old GCC versions we have to manually save and restore the x86_32 PIC + * register (ebx). See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602 + */ +#if defined(ARCH_X86_32) && defined(__PIC__) +# define EBX_CONSTRAINT "=&r" +#else +# define EBX_CONSTRAINT "=b" +#endif + +/* Execute the CPUID instruction. */ +static inline void +cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d) +{ +#ifdef _MSC_VER + int result[4]; + + __cpuidex(result, leaf, subleaf); + *a = result[0]; + *b = result[1]; + *c = result[2]; + *d = result[3]; +#else + __asm__ volatile(".ifnc %%ebx, %1; mov %%ebx, %1; .endif\n" + "cpuid \n" + ".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n" + : "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d) + : "a" (leaf), "c" (subleaf)); +#endif +} + +/* Read an extended control register. */ +static inline u64 +read_xcr(u32 index) +{ +#ifdef _MSC_VER + return _xgetbv(index); +#else + u32 d, a; + + /* + * Execute the "xgetbv" instruction. Old versions of binutils do not + * recognize this instruction, so list the raw bytes instead. + * + * This must be 'volatile' to prevent this code from being moved out + * from under the check for OSXSAVE. + */ + __asm__ volatile(".byte 0x0f, 0x01, 0xd0" : + "=d" (d), "=a" (a) : "c" (index)); + + return ((u64)d << 32) | a; +#endif +} + +static const struct cpu_feature x86_cpu_feature_table[] = { + {X86_CPU_FEATURE_SSE2, "sse2"}, + {X86_CPU_FEATURE_PCLMUL, "pclmul"}, + {X86_CPU_FEATURE_AVX, "avx"}, + {X86_CPU_FEATURE_AVX2, "avx2"}, + {X86_CPU_FEATURE_BMI2, "bmi2"}, +}; + +volatile u32 libdeflate_x86_cpu_features = 0; + +/* Initialize libdeflate_x86_cpu_features. */ +void libdeflate_init_x86_cpu_features(void) +{ + u32 max_leaf, a, b, c, d; + u64 xcr0 = 0; + u32 features = 0; + + /* EAX=0: Highest Function Parameter and Manufacturer ID */ + cpuid(0, 0, &max_leaf, &b, &c, &d); + if (max_leaf < 1) + goto out; + + /* EAX=1: Processor Info and Feature Bits */ + cpuid(1, 0, &a, &b, &c, &d); + if (d & (1 << 26)) + features |= X86_CPU_FEATURE_SSE2; + if (c & (1 << 1)) + features |= X86_CPU_FEATURE_PCLMUL; + if (c & (1 << 27)) + xcr0 = read_xcr(0); + if ((c & (1 << 28)) && ((xcr0 & 0x6) == 0x6)) + features |= X86_CPU_FEATURE_AVX; + + if (max_leaf < 7) + goto out; + + /* EAX=7, ECX=0: Extended Features */ + cpuid(7, 0, &a, &b, &c, &d); + if ((b & (1 << 5)) && ((xcr0 & 0x6) == 0x6)) + features |= X86_CPU_FEATURE_AVX2; + if (b & (1 << 8)) + features |= X86_CPU_FEATURE_BMI2; + +out: + disable_cpu_features_for_testing(&features, x86_cpu_feature_table, + ARRAY_LEN(x86_cpu_feature_table)); + + libdeflate_x86_cpu_features = features | X86_CPU_FEATURES_KNOWN; +} + +#endif /* HAVE_DYNAMIC_X86_CPU_FEATURES */ diff --git a/source/fbxplugin/libdeflate.h b/source/fbxplugin/libdeflate.h new file mode 100644 index 0000000000000000000000000000000000000000..382d895de89c237960a6b4d8a5ba32013e67ba53 --- /dev/null +++ b/source/fbxplugin/libdeflate.h @@ -0,0 +1,411 @@ +/* + * libdeflate.h - public header for libdeflate + */ + +#ifndef LIBDEFLATE_H +#define LIBDEFLATE_H + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBDEFLATE_VERSION_MAJOR 1 +#define LIBDEFLATE_VERSION_MINOR 18 +#define LIBDEFLATE_VERSION_STRING "1.18" + +/* + * Users of libdeflate.dll on Windows can define LIBDEFLATE_DLL to cause + * __declspec(dllimport) to be used. This should be done when it's easy to do. + * Otherwise it's fine to skip it, since it is a very minor performance + * optimization that is irrelevant for most use cases of libdeflate. + */ +#ifndef LIBDEFLATEAPI +# if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__)) +# define LIBDEFLATEAPI __declspec(dllimport) +# else +# define LIBDEFLATEAPI +# endif +#endif + +/* ========================================================================== */ +/* Compression */ +/* ========================================================================== */ + +struct libdeflate_compressor; +struct libdeflate_options; + +/* + * libdeflate_alloc_compressor() allocates a new compressor that supports + * DEFLATE, zlib, and gzip compression. 'compression_level' is the compression + * level on a zlib-like scale but with a higher maximum value (1 = fastest, 6 = + * medium/default, 9 = slow, 12 = slowest). Level 0 is also supported and means + * "no compression", specifically "create a valid stream, but only emit + * uncompressed blocks" (this will expand the data slightly). + * + * The return value is a pointer to the new compressor, or NULL if out of memory + * or if the compression level is invalid (i.e. outside the range [0, 12]). + * + * Note: for compression, the sliding window size is defined at compilation time + * to 32768, the largest size permissible in the DEFLATE format. It cannot be + * changed at runtime. + * + * A single compressor is not safe to use by multiple threads concurrently. + * However, different threads may use different compressors concurrently. + */ +LIBDEFLATEAPI struct libdeflate_compressor * +libdeflate_alloc_compressor(int compression_level); + +/* + * Like libdeflate_alloc_compressor(), but adds the 'options' argument. + */ +LIBDEFLATEAPI struct libdeflate_compressor * +libdeflate_alloc_compressor_ex(int compression_level, + const struct libdeflate_options *options); + +/* + * libdeflate_deflate_compress() performs raw DEFLATE compression on a buffer of + * data. It attempts to compress 'in_nbytes' bytes of data located at 'in' and + * write the result to 'out', which has space for 'out_nbytes_avail' bytes. The + * return value is the compressed size in bytes, or 0 if the data could not be + * compressed to 'out_nbytes_avail' bytes or fewer (but see note below). + * + * If compression is successful, then the output data is guaranteed to be a + * valid DEFLATE stream that decompresses to the input data. No other + * guarantees are made about the output data. Notably, different versions of + * libdeflate can produce different compressed data for the same uncompressed + * data, even at the same compression level. Do ***NOT*** do things like + * writing tests that compare compressed data to a golden output, as this can + * break when libdeflate is updated. (This property isn't specific to + * libdeflate; the same is true for zlib and other compression libraries too.) + */ +LIBDEFLATEAPI size_t +libdeflate_deflate_compress(struct libdeflate_compressor *compressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail); + +/* + * libdeflate_deflate_compress_bound() returns a worst-case upper bound on the + * number of bytes of compressed data that may be produced by compressing any + * buffer of length less than or equal to 'in_nbytes' using + * libdeflate_deflate_compress() with the specified compressor. This bound will + * necessarily be a number greater than or equal to 'in_nbytes'. It may be an + * overestimate of the true upper bound. The return value is guaranteed to be + * the same for all invocations with the same compressor and same 'in_nbytes'. + * + * As a special case, 'compressor' may be NULL. This causes the bound to be + * taken across *any* libdeflate_compressor that could ever be allocated with + * this build of the library, with any options. + * + * Note that this function is not necessary in many applications. With + * block-based compression, it is usually preferable to separately store the + * uncompressed size of each block and to store any blocks that did not compress + * to less than their original size uncompressed. In that scenario, there is no + * need to know the worst-case compressed size, since the maximum number of + * bytes of compressed data that may be used would always be one less than the + * input length. You can just pass a buffer of that size to + * libdeflate_deflate_compress() and store the data uncompressed if + * libdeflate_deflate_compress() returns 0, indicating that the compressed data + * did not fit into the provided output buffer. + */ +LIBDEFLATEAPI size_t +libdeflate_deflate_compress_bound(struct libdeflate_compressor *compressor, + size_t in_nbytes); + +/* + * Like libdeflate_deflate_compress(), but uses the zlib wrapper format instead + * of raw DEFLATE. + */ +LIBDEFLATEAPI size_t +libdeflate_zlib_compress(struct libdeflate_compressor *compressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail); + +/* + * Like libdeflate_deflate_compress_bound(), but assumes the data will be + * compressed with libdeflate_zlib_compress() rather than with + * libdeflate_deflate_compress(). + */ +LIBDEFLATEAPI size_t +libdeflate_zlib_compress_bound(struct libdeflate_compressor *compressor, + size_t in_nbytes); + +/* + * Like libdeflate_deflate_compress(), but uses the gzip wrapper format instead + * of raw DEFLATE. + */ +LIBDEFLATEAPI size_t +libdeflate_gzip_compress(struct libdeflate_compressor *compressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail); + +/* + * Like libdeflate_deflate_compress_bound(), but assumes the data will be + * compressed with libdeflate_gzip_compress() rather than with + * libdeflate_deflate_compress(). + */ +LIBDEFLATEAPI size_t +libdeflate_gzip_compress_bound(struct libdeflate_compressor *compressor, + size_t in_nbytes); + +/* + * libdeflate_free_compressor() frees a compressor that was allocated with + * libdeflate_alloc_compressor(). If a NULL pointer is passed in, no action is + * taken. + */ +LIBDEFLATEAPI void +libdeflate_free_compressor(struct libdeflate_compressor *compressor); + +/* ========================================================================== */ +/* Decompression */ +/* ========================================================================== */ + +struct libdeflate_decompressor; +struct libdeflate_options; + +/* + * libdeflate_alloc_decompressor() allocates a new decompressor that can be used + * for DEFLATE, zlib, and gzip decompression. The return value is a pointer to + * the new decompressor, or NULL if out of memory. + * + * This function takes no parameters, and the returned decompressor is valid for + * decompressing data that was compressed at any compression level and with any + * sliding window size. + * + * A single decompressor is not safe to use by multiple threads concurrently. + * However, different threads may use different decompressors concurrently. + */ +LIBDEFLATEAPI struct libdeflate_decompressor * +libdeflate_alloc_decompressor(void); + +/* + * Like libdeflate_alloc_decompressor(), but adds the 'options' argument. + */ +LIBDEFLATEAPI struct libdeflate_decompressor * +libdeflate_alloc_decompressor_ex(const struct libdeflate_options *options); + +/* + * Result of a call to libdeflate_deflate_decompress(), + * libdeflate_zlib_decompress(), or libdeflate_gzip_decompress(). + */ +enum libdeflate_result { + /* Decompression was successful. */ + LIBDEFLATE_SUCCESS = 0, + + /* Decompression failed because the compressed data was invalid, + * corrupt, or otherwise unsupported. */ + LIBDEFLATE_BAD_DATA = 1, + + /* A NULL 'actual_out_nbytes_ret' was provided, but the data would have + * decompressed to fewer than 'out_nbytes_avail' bytes. */ + LIBDEFLATE_SHORT_OUTPUT = 2, + + /* The data would have decompressed to more than 'out_nbytes_avail' + * bytes. */ + LIBDEFLATE_INSUFFICIENT_SPACE = 3, +}; + +/* + * libdeflate_deflate_decompress() decompresses a DEFLATE stream from the buffer + * 'in' with compressed size up to 'in_nbytes' bytes. The uncompressed data is + * written to 'out', a buffer with size 'out_nbytes_avail' bytes. If + * decompression succeeds, then 0 (LIBDEFLATE_SUCCESS) is returned. Otherwise, + * a nonzero result code such as LIBDEFLATE_BAD_DATA is returned, and the + * contents of the output buffer are undefined. + * + * Decompression stops at the end of the DEFLATE stream (as indicated by the + * BFINAL flag), even if it is actually shorter than 'in_nbytes' bytes. + * + * libdeflate_deflate_decompress() can be used in cases where the actual + * uncompressed size is known (recommended) or unknown (not recommended): + * + * - If the actual uncompressed size is known, then pass the actual + * uncompressed size as 'out_nbytes_avail' and pass NULL for + * 'actual_out_nbytes_ret'. This makes libdeflate_deflate_decompress() fail + * with LIBDEFLATE_SHORT_OUTPUT if the data decompressed to fewer than the + * specified number of bytes. + * + * - If the actual uncompressed size is unknown, then provide a non-NULL + * 'actual_out_nbytes_ret' and provide a buffer with some size + * 'out_nbytes_avail' that you think is large enough to hold all the + * uncompressed data. In this case, if the data decompresses to less than + * or equal to 'out_nbytes_avail' bytes, then + * libdeflate_deflate_decompress() will write the actual uncompressed size + * to *actual_out_nbytes_ret and return 0 (LIBDEFLATE_SUCCESS). Otherwise, + * it will return LIBDEFLATE_INSUFFICIENT_SPACE if the provided buffer was + * not large enough but no other problems were encountered, or another + * nonzero result code if decompression failed for another reason. + */ +LIBDEFLATEAPI enum libdeflate_result +libdeflate_deflate_decompress(struct libdeflate_decompressor *decompressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_out_nbytes_ret); + +/* + * Like libdeflate_deflate_decompress(), but adds the 'actual_in_nbytes_ret' + * argument. If decompression succeeds and 'actual_in_nbytes_ret' is not NULL, + * then the actual compressed size of the DEFLATE stream (aligned to the next + * byte boundary) is written to *actual_in_nbytes_ret. + */ +LIBDEFLATEAPI enum libdeflate_result +libdeflate_deflate_decompress_ex(struct libdeflate_decompressor *decompressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, + size_t *actual_out_nbytes_ret); + +/* + * Like libdeflate_deflate_decompress(), but assumes the zlib wrapper format + * instead of raw DEFLATE. + * + * Decompression will stop at the end of the zlib stream, even if it is shorter + * than 'in_nbytes'. If you need to know exactly where the zlib stream ended, + * use libdeflate_zlib_decompress_ex(). + */ +LIBDEFLATEAPI enum libdeflate_result +libdeflate_zlib_decompress(struct libdeflate_decompressor *decompressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_out_nbytes_ret); + +/* + * Like libdeflate_zlib_decompress(), but adds the 'actual_in_nbytes_ret' + * argument. If 'actual_in_nbytes_ret' is not NULL and the decompression + * succeeds (indicating that the first zlib-compressed stream in the input + * buffer was decompressed), then the actual number of input bytes consumed is + * written to *actual_in_nbytes_ret. + */ +LIBDEFLATEAPI enum libdeflate_result +libdeflate_zlib_decompress_ex(struct libdeflate_decompressor *decompressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, + size_t *actual_out_nbytes_ret); + +/* + * Like libdeflate_deflate_decompress(), but assumes the gzip wrapper format + * instead of raw DEFLATE. + * + * If multiple gzip-compressed members are concatenated, then only the first + * will be decompressed. Use libdeflate_gzip_decompress_ex() if you need + * multi-member support. + */ +LIBDEFLATEAPI enum libdeflate_result +libdeflate_gzip_decompress(struct libdeflate_decompressor *decompressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_out_nbytes_ret); + +/* + * Like libdeflate_gzip_decompress(), but adds the 'actual_in_nbytes_ret' + * argument. If 'actual_in_nbytes_ret' is not NULL and the decompression + * succeeds (indicating that the first gzip-compressed member in the input + * buffer was decompressed), then the actual number of input bytes consumed is + * written to *actual_in_nbytes_ret. + */ +LIBDEFLATEAPI enum libdeflate_result +libdeflate_gzip_decompress_ex(struct libdeflate_decompressor *decompressor, + const void *in, size_t in_nbytes, + void *out, size_t out_nbytes_avail, + size_t *actual_in_nbytes_ret, + size_t *actual_out_nbytes_ret); + +/* + * libdeflate_free_decompressor() frees a decompressor that was allocated with + * libdeflate_alloc_decompressor(). If a NULL pointer is passed in, no action + * is taken. + */ +LIBDEFLATEAPI void +libdeflate_free_decompressor(struct libdeflate_decompressor *decompressor); + +/* ========================================================================== */ +/* Checksums */ +/* ========================================================================== */ + +/* + * libdeflate_adler32() updates a running Adler-32 checksum with 'len' bytes of + * data and returns the updated checksum. When starting a new checksum, the + * required initial value for 'adler' is 1. This value is also returned when + * 'buffer' is specified as NULL. + */ +LIBDEFLATEAPI uint32_t +libdeflate_adler32(uint32_t adler, const void *buffer, size_t len); + + +/* + * libdeflate_crc32() updates a running CRC-32 checksum with 'len' bytes of data + * and returns the updated checksum. When starting a new checksum, the required + * initial value for 'crc' is 0. This value is also returned when 'buffer' is + * specified as NULL. + */ +LIBDEFLATEAPI uint32_t +libdeflate_crc32(uint32_t crc, const void *buffer, size_t len); + +/* ========================================================================== */ +/* Custom memory allocator */ +/* ========================================================================== */ + +/* + * Install a custom memory allocator which libdeflate will use for all memory + * allocations by default. 'malloc_func' is a function that must behave like + * malloc(), and 'free_func' is a function that must behave like free(). + * + * The per-(de)compressor custom memory allocator that can be specified in + * 'struct libdeflate_options' takes priority over this. + * + * This doesn't affect the free() function that will be used to free + * (de)compressors that were already in existence when this is called. + */ +LIBDEFLATEAPI void +libdeflate_set_memory_allocator(void *(*malloc_func)(size_t), + void (*free_func)(void *)); + +/* + * Advanced options. This is the options structure that + * libdeflate_alloc_compressor_ex() and libdeflate_alloc_decompressor_ex() + * require. Most users won't need this and should just use the non-"_ex" + * functions instead. If you do need this, it should be initialized like this: + * + * struct libdeflate_options options; + * + * memset(&options, 0, sizeof(options)); + * options.sizeof_options = sizeof(options); + * // Then set the fields that you need to override the defaults for. + */ +struct libdeflate_options { + + /* + * This field must be set to the struct size. This field exists for + * extensibility, so that fields can be appended to this struct in + * future versions of libdeflate while still supporting old binaries. + */ + size_t sizeof_options; + + /* + * An optional custom memory allocator to use for this (de)compressor. + * 'malloc_func' must be a function that behaves like malloc(), and + * 'free_func' must be a function that behaves like free(). + * + * This is useful in cases where a process might have multiple users of + * libdeflate who want to use different memory allocators. For example, + * a library might want to use libdeflate with a custom memory allocator + * without interfering with user code that might use libdeflate too. + * + * This takes priority over the "global" memory allocator (which by + * default is malloc() and free(), but can be changed by + * libdeflate_set_memory_allocator()). Moreover, libdeflate will never + * call the "global" memory allocator if a per-(de)compressor custom + * allocator is always given. + */ + void *(*malloc_func)(size_t); + void (*free_func)(void *); +}; + +#ifdef __cplusplus +} +#endif + +#endif /* LIBDEFLATE_H */ diff --git a/source/fbxplugin/plugin_main.cpp b/source/fbxplugin/plugin_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08d1cacab6f669d18793e3e7a41e01bbcf0f4283 --- /dev/null +++ b/source/fbxplugin/plugin_main.cpp @@ -0,0 +1,51 @@ +#include <xcommon/IXPlugin.h> +#include "ModelLoader.h" + +class CFBXPlugin: public IXUnknownImplementation<IXPlugin> +{ +public: + void XMETHODCALLTYPE startup(IXCore *pCore) override + { + m_pCore = pCore; + } + + void XMETHODCALLTYPE shutdown() override + { + } + + UINT XMETHODCALLTYPE getInterfaceCount() override + { + return(1); + } + const XGUID* XMETHODCALLTYPE getInterfaceGUID(UINT id) override + { + static XGUID s_guid; + switch(id) + { + case 0: + s_guid = IXMODELLOADER_GUID; + break; + + default: + return(NULL); + } + return(&s_guid); + } + void XMETHODCALLTYPE getInterface(UINT id, void **ppOut) override + { + switch(id) + { + case 0: + *ppOut = new CModelLoader(m_pCore->getFileSystem()); + break; + + default: + *ppOut = NULL; + } + } + +private: + IXCore *m_pCore = NULL; +}; + +DECLARE_XPLUGIN(CFBXPlugin); diff --git a/source/game/BaseAmmo.cpp b/source/game/BaseAmmo.cpp index 8714102d3348320990b7f1ca63891f49ba742bac..35abac1eea8a590650ec7e3abe5d2d9420f35cb7 100644 --- a/source/game/BaseAmmo.cpp +++ b/source/game/BaseAmmo.cpp @@ -9,11 +9,10 @@ See the license in LICENSE #include "BaseTool.h" #include <particles/sxparticles.h> #include <mtrl/IXMaterial.h> -#include <decals/sxdecals.h> //#include <BulletCollision/NarrowPhaseCollision/btRaycastCallback.h> //! TODO Reimplement me! -#define SMtrl_MtlGetPhysicMaterial(id) MTLTYPE_PHYSIC_METAL +#define SMtrl_MtlGetPhysicMaterial(id) MTLTYPE_PHYSIC_CONCRETE #define SMtrl_MtlGetDurability(id) 1.0f #define SMtrl_MtlGetDensity(id) 1.0f #define SMtrl_MtlGetHitChance(id) 1.0f @@ -107,8 +106,7 @@ void CBaseAmmo::fire(const float3 &_vStart, const float3 &_vDir, CBaseCharacter }); for(int i = 0, l = aHitPoints.size(); i < l; ++i) { - - ID idMtl = -1; // SPhysics_GetMtlID(aHitPoints[i].pCollisionObject, &aHitPoints[i].shapeInfo); + ID idMtl = 0; // SPhysics_GetMtlID(aHitPoints[i].pCollisionObject, &aHitPoints[i].shapeInfo); if(ID_VALID(idMtl) && !aHitPoints[i].isExit) { float fHitChance = SMtrl_MtlGetHitChance(idMtl); @@ -184,6 +182,15 @@ void CBaseAmmo::fire(const float3 &_vStart, const float3 &_vDir, CBaseCharacter // restart fire with new dir and speed float3 vStart2 = aHitPoints[i].vPosition + SMVector3Normalize(vDir) * 0.001f; + for(int j = i + 1; j < l; ++j) + { + if(aHitPoints[j].isExit) + { + // restart at exit point + vStart2 = aHitPoints[i].vPosition + SMVector3Normalize(vDir) * 0.001f; + } + } + vDir = vNewDir; fSpeed = fNewSpeed; @@ -240,26 +247,26 @@ void CBaseAmmo::shootDecal(const float3 &vPos, const float3 &vNormal, ID idMtl) if(ID_VALID(idMtl)) { MTLTYPE_PHYSIC type = SMtrl_MtlGetPhysicMaterial(idMtl); - DECAL_TYPE decalType = DECAL_TYPE_CONCRETE; + XDECAL_TYPE decalType = XDT_CONCRETE; switch(type) { case MTLTYPE_PHYSIC_METAL: - decalType = DECAL_TYPE_METAL; + decalType = XDT_METAL; break; case MTLTYPE_PHYSIC_FLESH: - decalType = DECAL_TYPE_FLESH; + decalType = XDT_FLESH; break; case MTLTYPE_PHYSIC_GROUD_SAND: - decalType = DECAL_TYPE_EARTH; + decalType = XDT_EARTH; break; case MTLTYPE_PHYSIC_PLASTIC: - decalType = DECAL_TYPE_PLASTIC; + decalType = XDT_PLASTIC; break; case MTLTYPE_PHYSIC_TREE: - decalType = DECAL_TYPE_WOOD; + decalType = XDT_WOOD; break; } - SXDecals_ShootDecal(decalType, vPos, vNormal); + SAFE_CALL(GetDecalProvider(), shootDecal, decalType, vPos, vNormal); //SPE_EffectPlayByName("create_decal_test", &aHitPoints[i].vPosition, &aHitPoints[i].vNormal); } @@ -267,7 +274,7 @@ void CBaseAmmo::shootDecal(const float3 &vPos, const float3 &vNormal, ID idMtl) void CBaseAmmo::shootBlood(const float3 &vPos, const float3 &vNormal) { - SXDecals_ShootDecal(DECAL_TYPE_BLOOD_BIG, vPos, vNormal); + SAFE_CALL(GetDecalProvider(), shootDecal, XDT_BLOOD_BIG, vPos, vNormal); } bool CBaseAmmo::shouldRecochet(const float3 &vPos, const float3 &vNormal, const float3 &vDir, ID idMtl, float fSpeed, float3 *pvNewDir, float *pfNewSpeed) diff --git a/source/game/BaseAnimating.cpp b/source/game/BaseAnimating.cpp index 5b37882ff0d5cecd2e175a261753e0af96906e28..c5eb19afb8c2b850724be4d86fd7ac1fe7295491 100644 --- a/source/game/BaseAnimating.cpp +++ b/source/game/BaseAnimating.cpp @@ -358,7 +358,10 @@ void CBaseAnimating::initPhysics() } } } - pShape->setLocalScaling(m_fBaseScale); + if(!SMIsZero(m_fBaseScale - 1.0f)) + { + pShape->setLocalScaling(m_fBaseScale); + } pShape->recalculateLocalAabb(); m_pCollideShape = pShape; createPhysBody(); diff --git a/source/game/BaseEntity.h b/source/game/BaseEntity.h index 53ecd6de3f3eb3fbfd47e08a0069ab9b61f2c086..a6bee19631543f2b0abd55b67c9582dcbba2c7cb 100644 --- a/source/game/BaseEntity.h +++ b/source/game/BaseEntity.h @@ -36,11 +36,13 @@ See the license in LICENSE #include <light/IXLightSystem.h> +#include <xcommon/resource/IXDecalProvider.h> + #pragma pointers_to_members(full_generality, virtual_inheritance) IXRender* GetRender(); IXParticleSystem* GetParticleSystem(); - +IXDecalProvider* GetDecalProvider(); @@ -170,6 +172,10 @@ public: return(false); } + virtual void setSize(const float3_t &vSize) + { + } + private: void setClassName(const char *name); void setDefaults(); diff --git a/source/game/BaseMover.cpp b/source/game/BaseMover.cpp index d1eed1100fb19396ccf75ec46eaaea9469b83150..0008c78b676ec18bab8b1c443c1cfb37d3aaec24 100644 --- a/source/game/BaseMover.cpp +++ b/source/game/BaseMover.cpp @@ -31,7 +31,7 @@ BEGIN_PROPTABLE(CBaseMover) DEFINE_FLAG(MOVER_NO_AUTOMOUNT, "Disable automount") END_PROPTABLE() -REGISTER_ENTITY(CBaseMover, base_mover); +REGISTER_ENTITY_NOLISTING(CBaseMover, base_mover); IEventChannel<XEventPhysicsStep> *CBaseMover::m_pTickEventChannel = NULL; diff --git a/source/game/BaseTool.cpp b/source/game/BaseTool.cpp index 5d84a3a0fa5b3c5dfe5026fd5c4fe95d425a8bfb..ccc15248bd43e6211a5ace7862af189df0c4ef6a 100644 --- a/source/game/BaseTool.cpp +++ b/source/game/BaseTool.cpp @@ -7,7 +7,6 @@ See the license in LICENSE #include "BaseTool.h" #include <particles/sxparticles.h> -#include <decals/sxdecals.h> #include "Player.h" /*! \skydocent base_tool @@ -138,7 +137,8 @@ void CBaseTool::primaryAction(BOOL st) if(cb.hasHit()) { //shoot decal - SXDecals_ShootDecal(DECAL_TYPE_CONCRETE, BTVEC_F3(cb.m_hitPointWorld), BTVEC_F3(cb.m_hitNormalWorld)); + SAFE_CALL(GetDecalProvider(), shootDecal, XDT_CONCRETE, cb.m_result.vHitPoint, cb.m_result.vHitNormal); + //SXDecals_ShootDecal(DECAL_TYPE_CONCRETE, BTVEC_F3(cb.m_hitPointWorld), BTVEC_F3(cb.m_hitNormalWorld)); SPE_EffectPlayByName("fire", &BTVEC_F3(cb.m_hitPointWorld), &BTVEC_F3(cb.m_hitNormalWorld)); IXRigidBody *pRB = cb.m_result.pCollisionObject->asRigidBody(); diff --git a/source/game/Editable.cpp b/source/game/Editable.cpp index d9484c6714bbd3ddf3e66f29daa70a44428a01ba..dc3239c24b6876ba31a7ec1cd237fc0ba6c81dd1 100644 --- a/source/game/Editable.cpp +++ b/source/game/Editable.cpp @@ -110,14 +110,15 @@ void CEditable::onSelectionChanged(CEditorObject *pObject) bool XMETHODCALLTYPE CEditable::canUseModel(const char *szClass) { - IEntityFactory *pFactory = CEntityFactoryMap::GetInstance()->getFactory(szClass); + return(getClassKV(szClass, "model_field") != NULL); +} + +const char* XMETHODCALLTYPE CEditable::getClassKV(const char *szClassName, const char *szKey) +{ + IEntityFactory *pFactory = CEntityFactoryMap::GetInstance()->getFactory(szClassName); if(pFactory) { - const char *szModelField = pFactory->getKV("model_field"); - if(szModelField) - { - return(true); - } + return(pFactory->getKV(szKey)); } - return(false); + return(NULL); } diff --git a/source/game/Editable.h b/source/game/Editable.h index 3db86f3e571426fb60a88cd3b09ed1836d3342f3..b2f66e35549c4af59d1f7c199a496c3d6519261d 100644 --- a/source/game/Editable.h +++ b/source/game/Editable.h @@ -80,6 +80,8 @@ public: return(m_pDevice); } + const char* XMETHODCALLTYPE getClassKV(const char *szClassName, const char *szKey) override; + protected: IGXDevice *m_pDevice = NULL; IXCore *m_pCore = NULL; diff --git a/source/game/EditorObject.cpp b/source/game/EditorObject.cpp index 944656b71a186737cba9e37b9ab212f9c68ef0af..83da6cb47cfffe04f57985332e1ca7ddc2dd1e01 100644 --- a/source/game/EditorObject.cpp +++ b/source/game/EditorObject.cpp @@ -71,6 +71,8 @@ void CEditorObject::_iniFieldList() && pField->editor.type != PDE_NONE ) { + xField.pEditorData = NULL; + switch(pField->editor.type) { case PDE_COMBOBOX: @@ -79,27 +81,30 @@ void CEditorObject::_iniFieldList() break; case PDE_FILEFIELD: xField.editorType = XPET_FILE; - xField.pEditorData = NULL; { - editor_kv *pKV = (editor_kv*)pField->editor.pData; - if(pKV && pKV[0].value) + kv_t *pKV = (kv_t*)pField->editor.pData; + if(pKV && pKV[0].szValue) { - if(!fstrcmp(pKV[0].value, "dse")) + if(!fstrcmp(pKV[0].szValue, "dse")) { xField.pEditorData = "model"; } - else if(!fstrcmp(pKV[0].value, "ogg")) + else if(!fstrcmp(pKV[0].szValue, "ogg")) { xField.pEditorData = "sound"; } - else if(!fstrcmp(pKV[0].value, "dds")) + else if(!fstrcmp(pKV[0].szValue, "dds")) { xField.pEditorData = "texture"; } - else if(!fstrcmp(pKV[0].value, "eff")) + else if(!fstrcmp(pKV[0].szValue, "eff")) { xField.pEditorData = "effect"; } + else if(!fstrcmp(pKV[0].szValue, "mtl")) + { + xField.pEditorData = "material"; + } } //xField.pEditorData } @@ -119,6 +124,7 @@ void CEditorObject::_iniFieldList() break; case PDE_POINTCOORD: xField.editorType = XPET_POINTCOORD; + xField.pEditorData = pField->editor.pData; break; case PDE_RADIUS: xField.editorType = XPET_RADIUS; @@ -129,6 +135,9 @@ void CEditorObject::_iniFieldList() case PDE_HDRCOLOR: xField.editorType = XPET_HDRCOLOR; break; + case PDE_SIZE: + xField.editorType = XPET_SIZE; + break; } xField.szHelp = ""; xField.szKey = pField->szKey; @@ -218,8 +227,8 @@ void CEditorObject::setPos(const float3_t &pos, bool isSeparate) void XMETHODCALLTYPE CEditorObject::setSize(const float3_t &vSize) { - // TODO Implement me //m_vScale = vScale; + m_pEntity->setSize(vSize); } /*void CEditorObject::setScale(const float3_t &vScale, bool isSeparate) diff --git a/source/game/EntityFactory.h b/source/game/EntityFactory.h index 46ff5c91c3305bdbaa86b8b6eeeedd4507ae70c3..1ec3331aa9c0e3a792d98f708298c4b0b350f746 100644 --- a/source/game/EntityFactory.h +++ b/source/game/EntityFactory.h @@ -203,6 +203,9 @@ private: #define REC_ICON(s) REC_KV("icon", s) #define REC_MODEL(s) REC_KV("model", s) #define REC_MODEL_FIELD(s) REC_KV("model_field", s) +#define REC_NO_HEIGHT_ADJUST() REC_KV("no_height_adj", "1") +#define REC_ALIGN_WITH_NORMAL(axis) REC_KV("align_with_norm", axis) +#define REC_MATERIAL_FIELD(s) REC_KV("material_field", s) #define REGISTER_ENTITY(cls, name, ...) \ CEntityFactory<cls> ent_ ## name ## _factory(#name, {__VA_ARGS__}) diff --git a/source/game/EntityManager.cpp b/source/game/EntityManager.cpp index f7bdfed35d1ef3e3676bd0e6d9c6ab1ce8179b1f..f7a14bf636cc0f33f3d042888fdb676cea6dac76 100644 --- a/source/game/EntityManager.cpp +++ b/source/game/EntityManager.cpp @@ -400,14 +400,14 @@ bool CEntityManager::exportList(const char * file) int ic = 0; //if(m_isOldImported) - { + /*{ FILE *fp = fopen(file, "w"); if(fp) { fclose(fp); } m_isOldImported = false; - } + }*/ // conf->set("meta", "count", "0"); @@ -463,13 +463,7 @@ bool CEntityManager::import(const char * file, bool shouldSendProgress) CBaseEntity *pEnt = NULL; Array<CBaseEntity*> tmpList; - char szFullPath[1024]; - if(!Core_GetIXCore()->getFileSystem()->resolvePath(file, szFullPath, sizeof(szFullPath))) - { - goto err; - } - - if(conf->open(szFullPath)) + if(conf->open(file)) { goto err; } diff --git a/source/game/GUIInventoryController.cpp b/source/game/GUIInventoryController.cpp index 354374438694492971ae4bed4216f733e69b8b9d..c06a4fd7bcf5fbaf1b4629a36626f77119140da6 100644 --- a/source/game/GUIInventoryController.cpp +++ b/source/game/GUIInventoryController.cpp @@ -38,7 +38,7 @@ CGUIInventoryController::CGUIInventoryController(CCharacterInventory *pInventory { m_aEquipAreas[i].type = (EQUIP_ITEM_TYPE)enumerator.getValue(); m_aEquipAreas[i].uIndex = pTypeCounts[enumerator.getValue()]++; - m_aEquipAreas[i].pNode->setAttribute(L"equip_index", (unsigned long)m_aEquipAreas[i].uIndex); + m_aEquipAreas[i].pNode->setAttribute(L"equip_index", m_aEquipAreas[i].uIndex); if(m_aEquipAreas[i].pNode->getAttribute(L"split_container").toBool()) { @@ -52,13 +52,13 @@ CGUIInventoryController::CGUIInventoryController(CCharacterInventory *pInventory if(j == 0 && k == 0) { m_aEquipAreas[i].pCellNode = aCells[k]; - m_aEquipAreas[i].pCellNode->setAttribute(L"equip_index", (unsigned long)m_aEquipAreas[i].uIndex); + m_aEquipAreas[i].pCellNode->setAttribute(L"equip_index", m_aEquipAreas[i].uIndex); } else { newArea.uIndex = pTypeCounts[enumerator.getValue()]++; newArea.pCellNode = aCells[k]; - newArea.pCellNode->setAttribute(L"equip_index", (unsigned long)newArea.uIndex); + newArea.pCellNode->setAttribute(L"equip_index", newArea.uIndex); m_aEquipAreas.push_back(newArea); } diff --git a/source/game/GameData.cpp b/source/game/GameData.cpp index a759bab3943e33f7e5b6181e9951d5975667c0d7..1f6d4ed04c74d4e943320d065d60fc55a9e820e5 100644 --- a/source/game/GameData.cpp +++ b/source/game/GameData.cpp @@ -81,6 +81,7 @@ static IXPhysics *g_pPhysics = NULL; static IXPhysicsWorld *g_pPhysWorld = NULL; static IXRender *g_pRender = NULL; static IXParticleSystem *g_pParticleSystem = NULL; +static IXDecalProvider *g_pDecalProvider = NULL; //########################################################################## @@ -114,6 +115,10 @@ IXParticleSystem* GetParticleSystem() { return(g_pParticleSystem); } +IXDecalProvider* GetDecalProvider() +{ + return(g_pDecalProvider); +} //########################################################################## @@ -342,9 +347,12 @@ void XMETHODCALLTYPE CLevelLoadTask::exec() evLevel.szLevelName = m_szLevelName; g_levelProgressListener.onLoadBegin(); - + evLevel.type = XEventLevel::TYPE_LOAD; g_pLevelChannel->broadcastEvent(&evLevel); + + evLevel.type = XEventLevel::TYPE_LOAD_WAIT_ASYNC_TASKS; + g_pLevelChannel->broadcastEvent(&evLevel); } void XMETHODCALLTYPE CLevelLoadTask::onFinished() @@ -489,6 +497,7 @@ GameData::GameData(HWND hWnd, bool isGame): g_pPhysics = (IXPhysics*)Core_GetIXCore()->getPluginManager()->getInterface(IXPHYSICS_GUID); g_pPhysWorld = g_pPhysics->getWorld(); g_pParticleSystem = (IXParticleSystem*)Core_GetIXCore()->getPluginManager()->getInterface(IXPARTICLESYSTEM_GUID); + g_pDecalProvider = (IXDecalProvider*)Core_GetIXCore()->getPluginManager()->getInterface(IXDECALPROVIDER_GUID); if(m_pLightSystem && false) { diff --git a/source/game/InfoOverlay.cpp b/source/game/InfoOverlay.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d534185547e5ba48ac82dacbb5350993b783db45 --- /dev/null +++ b/source/game/InfoOverlay.cpp @@ -0,0 +1,372 @@ +#include "InfoOverlay.h" + +BEGIN_PROPTABLE(CInfoOverlay) + //! Значение по умолчанию + DEFINE_FIELD_STRINGFN(m_szMaterial, PDFF_NONE, "material", "Overlay material", setMaterial, EDITOR_MATERIAL) + + //! Высота объема декали + DEFINE_FIELD_FLOATFN(m_fHeight, PDFF_NONE, "height", "Height", setHeight, EDITOR_TEXTFIELD) + + //! Угол 1 + DEFINE_FIELD_VECTORFN(m_vCorner0, PDFF_USE_GIZMO, "point0", "Point 1", setCorner0, EDITOR_POINTCOORDEX) + EDITOR_KV("lock", "plane") + EDITOR_KV("axis", "z") + EDITOR_KV("local", "1") + EDITOR_END() + //! Угол 2 + DEFINE_FIELD_VECTORFN(m_vCorner1, PDFF_USE_GIZMO, "point1", "Point 2", setCorner1, EDITOR_POINTCOORDEX) + EDITOR_KV("lock", "plane") + EDITOR_KV("axis", "z") + EDITOR_KV("local", "1") + EDITOR_END() + //! Угол 3 + DEFINE_FIELD_VECTORFN(m_vCorner2, PDFF_USE_GIZMO, "point2", "Point 3", setCorner2, EDITOR_POINTCOORDEX) + EDITOR_KV("lock", "plane") + EDITOR_KV("axis", "z") + EDITOR_KV("local", "1") + EDITOR_END() + //! Угол 4 + DEFINE_FIELD_VECTORFN(m_vCorner3, PDFF_USE_GIZMO, "point3", "Point 4", setCorner3, EDITOR_POINTCOORDEX) + EDITOR_KV("lock", "plane") + EDITOR_KV("axis", "z") + EDITOR_KV("local", "1") + EDITOR_END() + + //! Разрешить перетекание на перпендикулярные поверхности + DEFINE_FIELD_BOOLFN(m_isLeakAllowed, PDFF_NONE, "allow_leak", "Allow leak to perpendicular surface", setLeakAllowed, EDITOR_YESNO) + + //! Угол 4 + //DEFINE_FIELD_VECTORFN(m_vSize, PDFF_USE_GIZMO, "size", "Size", setSize, EDITOR_SIZE) + + //! Включает + //DEFINE_INPUT(turnOn, "turnOn", "Turn on", PDF_NONE) + //! Выключает + //DEFINE_INPUT(turnOff, "turnOff", "Turn off", PDF_NONE) + //! Переключает состояние + //DEFINE_INPUT(toggle, "toggle", "Toggle", PDF_NONE) + + + + //! Изначально выключена + //DEFINE_FLAG(MOVER_INITIALLY_DISABLED, "Start disabled") + //! Отключить автомонтирование (требуется нажать кнопку взаимодействия) + //DEFINE_FLAG(MOVER_NO_AUTOMOUNT, "Disable automount") +END_PROPTABLE() + +REGISTER_ENTITY(CInfoOverlay, info_overlay, REC_NO_HEIGHT_ADJUST(), REC_ALIGN_WITH_NORMAL("z"), REC_MATERIAL_FIELD("material")); + +CInfoOverlay::CInfoOverlay() +{ + float2_t vSize(1.0f, 1.0f); + + float2_t sBound(-vSize.x * 0.5f, vSize.x * 0.5f); + float2_t tBound(-vSize.y * 0.5f, vSize.y * 0.5f); + + m_vCorner0 = float3_t(sBound.x, tBound.x, 0.0f); + m_vCorner1 = float3_t(sBound.x, tBound.y, 0.0f); + m_vCorner2 = float3_t(sBound.y, tBound.y, 0.0f); + m_vCorner3 = float3_t(sBound.y, tBound.x, 0.0f); + + m_fHeight = min(vSize.x, vSize.y) * 0.25f; + + GetDecalProvider()->newDecal(&m_pDecal); + + m_pDecal->setHeight(m_fHeight); + + m_pDecal->setCorner(0, float2(m_vCorner0)); + m_pDecal->setCorner(1, float2(m_vCorner1)); + m_pDecal->setCorner(2, float2(m_vCorner2)); + m_pDecal->setCorner(3, float2(m_vCorner3)); + + updateSize(); +} + +CInfoOverlay::~CInfoOverlay() +{ + mem_release(m_pDecal); +} + +void CInfoOverlay::setCorner0(const float3 &v) +{ + m_vCorner0 = v; + m_pDecal->setCorner(0, float2(v)); + updateSize(); +} +void CInfoOverlay::setCorner1(const float3 &v) +{ + m_vCorner1 = v; + m_pDecal->setCorner(1, float2(v)); + updateSize(); +} +void CInfoOverlay::setCorner2(const float3 &v) +{ + m_vCorner2 = v; + m_pDecal->setCorner(2, float2(v)); + updateSize(); +} +void CInfoOverlay::setCorner3(const float3 &v) +{ + m_vCorner3 = v; + m_pDecal->setCorner(3, float2(v)); + updateSize(); +} + +void CInfoOverlay::setPos(const float3 &vPos) +{ + BaseClass::setPos(vPos); + + m_pDecal->setPosition(vPos); +} + +void CInfoOverlay::setOrient(const SMQuaternion &qRot) +{ + BaseClass::setOrient(qRot); + + m_pDecal->setOrientation(qRot); +} + +void CInfoOverlay::setMaterial(const char *szValue) +{ + _setStrVal(&m_szMaterial, szValue); + + m_pDecal->setMaterial(szValue); +} + +void CInfoOverlay::setHeight(float fHeight) +{ + m_fHeight = fHeight; + m_pDecal->setHeight(fHeight); +} + +void CInfoOverlay::renderEditor(bool is3D, bool bRenderSelection, IXGizmoRenderer *pRenderer) +{ + if(pRenderer) + { + pRenderer->setLineWidth(is3D ? 0.01f : 1.0f); + + pRenderer->setColor(float4(1.0f, 0.0f, 0.0f, 1.0f)); + pRenderer->jumpTo(getPos()); + pRenderer->lineTo(getPos() + getOrient() * float3(0.1f, 0.0f, 0.0f)); + + pRenderer->setColor(float4(0.0f, 1.0f, 0.0f, 1.0f)); + pRenderer->jumpTo(getPos()); + pRenderer->lineTo(getPos() + getOrient() * float3(0.0f, 0.1f, 0.0f)); + + pRenderer->setColor(float4(0.0f, 0.0f, 1.0f, 1.0f)); + pRenderer->jumpTo(getPos()); + pRenderer->lineTo(getPos() + getOrient() * float3(0.0f, 0.0f, 0.2f)); + + if(bRenderSelection || !is3D) + { + pRenderer->setLineWidth(is3D ? 0.005f : 1.0f); + const float4 c_vLineColor = bRenderSelection ? float4(1.0f, 0.0f, 0.0f, 1.0f) : float4(1.0f, 0.0f, 1.0f, 1.0f); + pRenderer->setColor(c_vLineColor); + + float3 vS = getOrient() * float3(1.0f, 0.0f, 0.0f); + float3 vT = getOrient() * float3(0.0f, 1.0f, 0.0f); + float3 vN = getOrient() * float3(0.0f, 0.0f, 1.0f); + + float3_t vPt0 = getPos() + m_vCorner0.x * vS + m_vCorner0.y * vT; + float3_t vPt1 = getPos() + m_vCorner1.x * vS + m_vCorner1.y * vT; + float3_t vPt2 = getPos() + m_vCorner2.x * vS + m_vCorner2.y * vT; + float3_t vPt3 = getPos() + m_vCorner3.x * vS + m_vCorner3.y * vT; + + float3 vTopOffset = vN * m_fHeight; + + pRenderer->jumpTo(vPt0 - vTopOffset); + pRenderer->lineTo(vPt1 - vTopOffset); + pRenderer->lineTo(vPt2 - vTopOffset); + pRenderer->lineTo(vPt3 - vTopOffset); + pRenderer->lineTo(vPt0 - vTopOffset); + pRenderer->lineTo(vPt0 + vTopOffset); + pRenderer->lineTo(vPt1 + vTopOffset); + pRenderer->lineTo(vPt2 + vTopOffset); + pRenderer->lineTo(vPt3 + vTopOffset); + pRenderer->lineTo(vPt0 + vTopOffset); + + pRenderer->jumpTo(vPt1 - vTopOffset); + pRenderer->lineTo(vPt1 + vTopOffset); + + pRenderer->jumpTo(vPt2 - vTopOffset); + pRenderer->lineTo(vPt2 + vTopOffset); + + pRenderer->jumpTo(vPt3 - vTopOffset); + pRenderer->lineTo(vPt3 + vTopOffset); + } + } +} + +void CInfoOverlay::getMinMax(float3 *min, float3 *max) +{ + float3 vS = getOrient() * float3(1.0f, 0.0f, 0.0f); + float3 vT = getOrient() * float3(0.0f, 1.0f, 0.0f); + float3 vN = getOrient() * float3(0.0f, 0.0f, 1.0f); + + float3 vTopOffset = vN * m_fHeight; + + float3_t vPt0 = m_vCorner0.x * vS + m_vCorner0.y * vT; + float3_t vPt1 = m_vCorner1.x * vS + m_vCorner1.y * vT; + float3_t vPt2 = m_vCorner2.x * vS + m_vCorner2.y * vT; + float3_t vPt3 = m_vCorner3.x * vS + m_vCorner3.y * vT; + + float3_t vPt10 = vPt0 + vTopOffset; + float3_t vPt11 = vPt1 + vTopOffset; + float3_t vPt12 = vPt2 + vTopOffset; + float3_t vPt13 = vPt3 + vTopOffset; + + vPt0 = vPt0 - vTopOffset; + vPt1 = vPt1 - vTopOffset; + vPt2 = vPt2 - vTopOffset; + vPt3 = vPt3 - vTopOffset; + + if(min) + { + *min = SMVectorMin( + SMVectorMin(SMVectorMin(vPt0, vPt1), SMVectorMin(vPt2, vPt3)), + SMVectorMin(SMVectorMin(vPt10, vPt11), SMVectorMin(vPt12, vPt13)) + ); + } + + if(max) + { + *max = SMVectorMax( + SMVectorMax(SMVectorMax(vPt0, vPt1), SMVectorMax(vPt2, vPt3)), + SMVectorMax(SMVectorMax(vPt10, vPt11), SMVectorMax(vPt12, vPt13)) + ); + } +} + +void CInfoOverlay::setSize(const float3_t &vSize) +{ + float3 vMin, vMax; + getMinMax(&vMin, &vMax); + BaseClass::setSize(vSize); + + float3 vRelativeSize = vSize / (vMax - vMin); + + float3 vS = getOrient() * float3(1.0f, 0.0f, 0.0f); + float3 vT = getOrient() * float3(0.0f, 1.0f, 0.0f); + float3 vN = getOrient() * float3(0.0f, 0.0f, 1.0f); + + float3_t vPt0 = (m_vCorner0.x * vS + m_vCorner0.y * vT) * vRelativeSize; + float3_t vPt1 = (m_vCorner1.x * vS + m_vCorner1.y * vT) * vRelativeSize; + float3_t vPt2 = (m_vCorner2.x * vS + m_vCorner2.y * vT) * vRelativeSize; + float3_t vPt3 = (m_vCorner3.x * vS + m_vCorner3.y * vT) * vRelativeSize; + + m_vCorner0 = float3_t(SMVector3Dot(vPt0, vS), SMVector3Dot(vPt0, vT), 0.0f); + m_vCorner1 = float3_t(SMVector3Dot(vPt1, vS), SMVector3Dot(vPt1, vT), 0.0f); + m_vCorner2 = float3_t(SMVector3Dot(vPt2, vS), SMVector3Dot(vPt2, vT), 0.0f); + m_vCorner3 = float3_t(SMVector3Dot(vPt3, vS), SMVector3Dot(vPt3, vT), 0.0f); + + m_pDecal->setCorner(0, float2(m_vCorner0)); + m_pDecal->setCorner(1, float2(m_vCorner1)); + m_pDecal->setCorner(2, float2(m_vCorner2)); + m_pDecal->setCorner(3, float2(m_vCorner3)); + + m_fHeight *= SMVector3Dot(vN, vRelativeSize); + m_pDecal->setHeight(m_fHeight); + + updateSize(); +} + +bool CInfoOverlay::rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut, float3 *pvNormal, bool isRayInWorldSpace, bool bReturnNearestPoint) +{ + if(!isRayInWorldSpace) + { + float3 vPos = getPos(); + SMQuaternion qRot = getOrient().Conjugate(); + + float3 vRayStart = qRot * vStart - vPos; + float3 vRayEnd = qRot * vEnd - vPos; + return(m_pDecal->rayTest(vRayStart, vRayEnd, pvOut, pvNormal)); + } + + return(m_pDecal->rayTest(vStart, vEnd, pvOut, pvNormal)); +} + +void CInfoOverlay::updateSize() +{ + float3 vMin = SMVectorMin(SMVectorMin(m_vCorner0, m_vCorner1), SMVectorMin(m_vCorner2, m_vCorner3)); + float3 vMax = SMVectorMax(SMVectorMax(m_vCorner0, m_vCorner1), SMVectorMax(m_vCorner2, m_vCorner3)); + + m_vSize = vMax - vMin; + m_vSize.z = m_fHeight * 2.0f; +} + +void CInfoOverlay::setSize(const float3 &v) +{ +} + +void CInfoOverlay::setLeakAllowed(bool yesNo) +{ + m_isLeakAllowed = yesNo; + m_pDecal->setLeakAllowed(yesNo); +} + +#if 0 + +void CInfoOverlay::updateFlags() +{ + BaseClass::updateFlags(); + + if(getFlags() & MOVER_INITIALLY_DISABLED) + { + disable(); + } + else + { + enable(); + } +} + +void CInfoOverlay::turnOn(inputdata_t *pInputdata) +{ + enable(); +} + +void CInfoOverlay::turnOff(inputdata_t *pInputdata) +{ + disable(); +} + +void CInfoOverlay::toggle(inputdata_t *pInputdata) +{ + if(m_isEnabled) + { + turnOff(pInputdata); + } + else + { + turnOn(pInputdata); + } +} + + + +void CInfoOverlay::enable() +{ + if(!m_isEnabled) + { + if(m_pGhostObject) + { + GetPhysWorld()->addCollisionObject(m_pGhostObject, CG_LADDER, CG_CHARACTER); + } + m_pTickEventChannel->addListener(&m_physicsTicker); + m_isEnabled = true; + } +} + +void CInfoOverlay::disable() +{ + if(m_isEnabled) + { + if(m_pGhostObject) + { + GetPhysWorld()->removeCollisionObject(m_pGhostObject); + } + m_pTickEventChannel->removeListener(&m_physicsTicker); + m_isEnabled = false; + } +} + +#endif diff --git a/source/game/InfoOverlay.h b/source/game/InfoOverlay.h new file mode 100644 index 0000000000000000000000000000000000000000..ca46e39c52d3ffeb2fe6891bd5113822b7fa8764 --- /dev/null +++ b/source/game/InfoOverlay.h @@ -0,0 +1,71 @@ +#ifndef __INFO_OVERLAY_H +#define __INFO_OVERLAY_H + +#include "PointEntity.h" + +//#define MOVER_INITIALLY_DISABLED ENT_FLAG_0 +//#define MOVER_NO_AUTOMOUNT ENT_FLAG_1 + +class CInfoOverlay: public CPointEntity +{ + DECLARE_CLASS(CInfoOverlay, CPointEntity); + DECLARE_PROPTABLE(); +public: + DECLARE_CONSTRUCTOR(); + ~CInfoOverlay(); + + void setPos(const float3 &vPos) override; + void setOrient(const SMQuaternion &qRot) override; + + void renderEditor(bool is3D, bool bRenderSelection, IXGizmoRenderer *pRenderer) override; + + void getMinMax(float3 *min, float3 *max) override; + + + + bool rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut = NULL, float3 *pvNormal = NULL, bool isRayInWorldSpace = true, bool bReturnNearestPoint = false) override; + + void setHeight(float fHeight); + void setMaterial(const char *szValue); + + void setSize(const float3_t &vSize) override; + + void setLeakAllowed(bool yesNo); + +private: + void setCorner0(const float3 &v); + void setCorner1(const float3 &v); + void setCorner2(const float3 &v); + void setCorner3(const float3 &v); + + void setSize(const float3 &v); + //void updateFlags() override; + + //void turnOn(inputdata_t *pInputdata); + //void turnOff(inputdata_t *pInputdata); + //void toggle(inputdata_t *pInputdata); + //void enable(); + //void disable(); + + //SMAABB getBound(); + void updateSize(); + +private: + IXDecal *m_pDecal = NULL; + const char *m_szMaterial = NULL; + + float3_t m_vCorner0; + float3_t m_vCorner1; + float3_t m_vCorner2; + float3_t m_vCorner3; + + float m_fHeight = 0.1f; + + float3_t m_vSize; + + bool m_isLeakAllowed = true; + + //bool m_isEnabled = false; +}; + +#endif diff --git a/source/game/ZombieHands.cpp b/source/game/ZombieHands.cpp index 19b7df3658635fc40ebad62f6021db8467b8a3ec..4e8e4f1887143866e16efce5465d843585c807ef 100644 --- a/source/game/ZombieHands.cpp +++ b/source/game/ZombieHands.cpp @@ -1,7 +1,6 @@ #include "ZombieHands.h" #include "BaseCharacter.h" -#include <decals/sxdecals.h> #include "Tracer.h" /*! \skydocent wpn_zombie_hands @@ -52,7 +51,7 @@ void CZombieHands::actualShoot(float dt) if(cb.hasHit()) { - SXDecals_ShootDecal(DECAL_TYPE_BLOOD_BIG, BTVEC_F3(cb.m_hitPointWorld), BTVEC_F3(cb.m_hitNormalWorld)); + SAFE_CALL(GetDecalProvider(), shootDecal, XDT_BLOOD_BIG, cb.m_result.vHitPoint, cb.m_result.vHitNormal); if(cb.m_result.pCollisionObject->getUserPointer() && cb.m_result.pCollisionObject->getUserTypeId() == 1) { diff --git a/source/game/proptable.cpp b/source/game/proptable.cpp index 619cba20ad883d8889af3b7296df202deaa73c98..57723b117e69977e564bfb0a58cf06182a0c2dec 100644 --- a/source/game/proptable.cpp +++ b/source/game/proptable.cpp @@ -11,14 +11,14 @@ See the license in LICENSE #include "BaseEntity.h" -prop_editor_t _GetEditorCombobox(int start, ...) +prop_editor_t _GetEditor(PDE_TYPE type, ...) { prop_editor_t out; - out.type = PDE_COMBOBOX; - editor_kv kvs[ED_COMBO_MAXELS]; + out.type = type; + kv_t kvs[ED_COMBO_MAXELS]; va_list va; - va_start(va, start); + va_start(va, type); const char * el; int i = 0; @@ -28,56 +28,21 @@ prop_editor_t _GetEditorCombobox(int start, ...) { break; } - kvs[i].key = el; + kvs[i].szKey = el; if(!(el = va_arg(va, const char *))) { break; } - kvs[i].value = el; + kvs[i].szValue = el; ++i; } - kvs[i].value = kvs[i].key = 0; + kvs[i].szValue = kvs[i].szKey = 0; ++i; va_end(va); - out.pData = new editor_kv[i]; - memcpy(out.pData, kvs, sizeof(editor_kv)* i); - return(out); -} - -prop_editor_t _GetEditorFilefield(int start, ...) -{ - prop_editor_t out; - out.type = PDE_FILEFIELD; - editor_kv kvs[ED_COMBO_MAXELS]; - - va_list va; - va_start(va, start); - - const char * el; - int i = 0; - while(i < ED_COMBO_MAXELS) - { - if(!(el = va_arg(va, const char *))) - { - break; - } - kvs[i].key = el; - if(!(el = va_arg(va, const char *))) - { - break; - } - kvs[i].value = el; - ++i; - } - kvs[i].value = kvs[i].key = 0; - ++i; - - va_end(va); - - out.pData = new editor_kv[i]; - memcpy(out.pData, kvs, sizeof(editor_kv)* i); + out.pData = new kv_t[i]; + memcpy(out.pData, kvs, sizeof(kv_t)* i); return(out); } diff --git a/source/game/proptable.h b/source/game/proptable.h index b73c430f918522042ca208b713ced369059cd7f1..3f97ecba76de87cf3078cd626635b3999668dc39 100644 --- a/source/game/proptable.h +++ b/source/game/proptable.h @@ -74,7 +74,8 @@ enum PDE_TYPE PDE_POINTCOORD, PDE_RADIUS, PDE_COLOR, - PDE_HDRCOLOR + PDE_HDRCOLOR, + PDE_SIZE }; enum PDF_FLAG @@ -170,12 +171,6 @@ union PFNFIELDSET int __; }; -struct editor_kv -{ - const char * key; - const char * value; -}; - struct prop_editor_t { PDE_TYPE type; @@ -367,8 +362,7 @@ struct proptable_t #define ED_COMBO_MAXELS 256 -prop_editor_t _GetEditorCombobox(int start, ...); -prop_editor_t _GetEditorFilefield(int start, ...); +prop_editor_t _GetEditor(PDE_TYPE type, ...); #define DECLARE_PROPTABLE() \ \ @@ -454,6 +448,10 @@ void cls::InitPropData() \ const char * GetEmptyString(); +#define EDITOR_BEGIN(type) _GetEditor(type +#define EDITOR_KV(name, value) , name, value +#define EDITOR_END() , NULL)} + #define EDITOR_NONE {PDE_NONE, NULL}} #define EDITOR_TEXTFIELD {PDE_TEXTFIELD, NULL}} #define EDITOR_TIMEFIELD {PDE_TIME, NULL}} @@ -463,19 +461,24 @@ const char * GetEmptyString(); #define EDITOR_FLAGS {PDE_FLAGS, NULL}} #define EDITOR_COLOR {PDE_COLOR, NULL}} #define EDITOR_HDRCOLOR {PDE_HDRCOLOR, NULL}} +#define EDITOR_SIZE {PDE_SIZE, NULL}} + +#define EDITOR_POINTCOORDEX EDITOR_BEGIN(PDE_POINTCOORD) + -#define EDITOR_COMBOBOX _GetEditorCombobox(0 -#define COMBO_OPTION(name, value) , name, value -#define EDITOR_COMBO_END() , NULL)} +#define EDITOR_COMBOBOX EDITOR_BEGIN(PDE_COMBOBOX) +#define COMBO_OPTION(name, value) EDITOR_KV(name, value) +#define EDITOR_COMBO_END() EDITOR_END() -#define EDITOR_FILEFIELD _GetEditorFilefield(0 -#define FILE_OPTION(name, value) , name, value -#define EDITOR_FILE_END() , NULL)} +#define EDITOR_FILEFIELD EDITOR_BEGIN(PDE_FILEFIELD) +#define FILE_OPTION(name, value) EDITOR_KV(name, value) +#define EDITOR_FILE_END() EDITOR_END() #define EDITOR_YESNO EDITOR_COMBOBOX COMBO_OPTION("Yes", "1") COMBO_OPTION("No", "0") EDITOR_COMBO_END() #define EDITOR_MODEL EDITOR_FILEFIELD FILE_OPTION("Select model", "dse") EDITOR_FILE_END() #define EDITOR_SOUND EDITOR_FILEFIELD FILE_OPTION("Select sound", "ogg") EDITOR_FILE_END() #define EDITOR_TEXTURE EDITOR_FILEFIELD FILE_OPTION("Select texture", "dds") EDITOR_FILE_END() +#define EDITOR_MATERIAL EDITOR_FILEFIELD FILE_OPTION("Select material", "mtl") EDITOR_FILE_END() #define EDITOR_EFFECT EDITOR_FILEFIELD FILE_OPTION("Select effect", "eff") EDITOR_FILE_END() #define DEFINE_FIELD_STRING(field, flags, keyname, edname, editor) , {propdata_t::ToFieldType<const char* DataClass::*>(&DataClass::field), PDF_STRING, flags, keyname, edname, editor diff --git a/source/gdefines.h b/source/gdefines.h index 30f4bac0d425986a774eff01ce5cf0072086fefc..bc8b37e1f5df43181052d80037318ab599bb27cd 100644 --- a/source/gdefines.h +++ b/source/gdefines.h @@ -221,6 +221,12 @@ typedef void(*report_func) (int iLevel, const char *szLibName, const char *szMes #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) #endif +struct kv_t +{ + const char *szKey; + const char *szValue; +}; + /** \name Уровни критичности сообщений для функции репортов */ //! @{ #define REPORT_MSG_LEVEL_NOTICE 0 /*!< заметка */ diff --git a/source/gui/DOMdocument.cpp b/source/gui/DOMdocument.cpp index 1f99d6373ffea1fbaa3e417429bbd7e3cf0ce67c..ed90d41302f870db794f988d3700c1793e0e4789 100644 --- a/source/gui/DOMdocument.cpp +++ b/source/gui/DOMdocument.cpp @@ -133,11 +133,11 @@ namespace gui if(cls.length()) { - UINT pos = 0; + size_t pos = 0; while(true) { pos = cls.find(L" "); - if(pos != (UINT)(-1)) + if(pos != StringW::EOS) { StringW c = cls.substr(0, pos); UINT icls = doc->getIndexForClassString(cls); @@ -1813,14 +1813,14 @@ namespace gui else if(name == L"class") { StringW cls = value+L" "; - UINT pos = 0; + size_t pos = 0; Array<UINT> vNewCls; Array<UINT> vAddCls; Array<UINT> vRemoveCls; while(true) { pos = cls.find(L" "); - if(pos != ~0) + if(pos != StringW::EOS) { if(pos != 0) { @@ -2045,8 +2045,8 @@ namespace gui BOOL CDOMnode::classExists(const StringW &cls) { const StringW &wsClass = getAttribute(L"class"); - int pos = wsClass.find(cls); - if(pos >= 0) + size_t pos = wsClass.find(cls); + if(pos != StringW::EOS) { wchar_t wc = wsClass[pos + cls.length()]; if(wc == 0 || iswspace(wc)) diff --git a/source/gui/Font.cpp b/source/gui/Font.cpp index 46a55fc536762948d8c38acf96f6beffffdbcfdd..76a0400a464cfc64f35509abf83c3f61a31917e2 100644 --- a/source/gui/Font.cpp +++ b/source/gui/Font.cpp @@ -679,7 +679,7 @@ namespace gui void CFont::addChar(WCHAR c, bool full) { - if(m_szFontChars.find(c) == (UINT)-1) + if(m_szFontChars.find(c) == StringW::EOS) { m_szFontChars += c; if(full) diff --git a/source/gui/ICSS.cpp b/source/gui/ICSS.cpp index e064c5411db60ea25a1b13638c08cee778fdd709..4d7c1017bd9c872d9be01c5bdff22560a2d529e0 100644 --- a/source/gui/ICSS.cpp +++ b/source/gui/ICSS.cpp @@ -235,8 +235,8 @@ namespace gui } if(pseudoclass & (PSEUDOCLASS_NTH_CHILD | PSEUDOCLASS_NTH_LAST_CHILD | PSEUDOCLASS_NTH_OF_TYPE | PSEUDOCLASS_NTH_LAST_OF_TYPE)) { - UINT pos = str.find(L"("); - if(pos != (UINT)(-1)) + size_t pos = str.find(L"("); + if(pos != StringW::EOS) { UINT d = 0, o = 0; WCHAR cn; diff --git a/source/gui/IHTMLparser.cpp b/source/gui/IHTMLparser.cpp index 4ca54eaf9441f48f33a3a2ca0cc179334d81d0a9..164cf3b5bcd7a5bf5dfd89d1e83db6524007c817 100644 --- a/source/gui/IHTMLparser.cpp +++ b/source/gui/IHTMLparser.cpp @@ -473,7 +473,7 @@ namespace gui } for(AssotiativeArray<StringW, StringW>::Iterator j = attrs.begin(); j; ++j) { - pCur->setAttribute(j.first, j.second); + pCur->setAttribute(*j.first, *j.second); } attrs.clear(); // wprintf(L"open tag %s\n", tagname); diff --git a/source/light/LightSystem.cpp b/source/light/LightSystem.cpp index 94b5e1e66fad7f1338b866bea041f7937f581cce..a69580b4e92b2f350e46460b3394b5dc19a840b6 100644 --- a/source/light/LightSystem.cpp +++ b/source/light/LightSystem.cpp @@ -852,7 +852,7 @@ void CLightSystem::renderGI(CGIGraphNodeData *pNodeData, IXRenderTarget *pFinalT pCtx->addTimestamp("lpv_inject -"); - const bool *dev_lpv_points = m_pCore->getConsole()->getPCVarBool("dev_lpv_points"); + static const bool *dev_lpv_points = m_pCore->getConsole()->getPCVarBool("dev_lpv_points"); if(*dev_lpv_points) { auto pTarget = pNodeData->m_pGBufferColor->asRenderTarget(); diff --git a/source/physics/PhyWorld.cpp b/source/physics/PhyWorld.cpp index 0dd249c57282fc0b1fd30fa45d528f3a1bc7590a..c4eb6d186be5e804983271a66b263e81adc9e567 100644 --- a/source/physics/PhyWorld.cpp +++ b/source/physics/PhyWorld.cpp @@ -56,7 +56,7 @@ struct XRayResultCallback: public btCollisionWorld::RayResultCallback m_hitPointWorld.setInterpolate3(m_rayFromWorld, m_rayToWorld, rayResult.m_hitFraction); m_result.vHitPoint = BTVEC_F3(m_hitPointWorld); - m_result.vHitNormal = BTVEC_F3(m_hitPointWorld); + m_result.vHitNormal = BTVEC_F3(m_hitNormalWorld); m_result.pCollisionObject = m_collisionObject->getUserIndex() == 2 ? (IXCollisionObject*)m_collisionObject->getUserPointer() : NULL; m_result.fHitFraction = rayResult.m_hitFraction; diff --git a/source/render/Scene.cpp b/source/render/Scene.cpp index 42e5ad8a8b557b67fe6cb19cc90f7b3a5803fc4e..46d10cb9cc88b4586996f9a5299367e1b7c2cb09 100644 --- a/source/render/Scene.cpp +++ b/source/render/Scene.cpp @@ -1287,6 +1287,11 @@ public: return(m_pEditorExt->getRenderer()); } + const char* XMETHODCALLTYPE getClassKV(const char *szClassName, const char *szKey) override + { + return(NULL); + } + private: CEditorExt *m_pEditorExt = NULL; }; @@ -1696,3 +1701,8 @@ void CScene::print() m_pRootNode->print(0, &uNodesCount, &uObjectsCount, &uMaxDepth); printf("Total objects: %u, Total nodes: %u, Max depth: %u, avg objects per node: %f\n", uObjectsCount, uNodesCount, uMaxDepth, (float)uObjectsCount / (float)uNodesCount); } + +bool CScene::hasPendingOps() +{ + return(!m_qUpdate.empty()); +} diff --git a/source/render/Scene.h b/source/render/Scene.h index 4d810b09aaf95bef82711d954347bdc953ae975b..7b42aeff747a16a0e22954db3f3ce7b83bcc62d2 100644 --- a/source/render/Scene.h +++ b/source/render/Scene.h @@ -275,6 +275,8 @@ public: bool validate(); void print(); + bool hasPendingOps(); + protected: void addObject(CSceneObject *pObject); void removeObject(CSceneObject *pObject); diff --git a/source/render/ShaderPreprocessor.cpp b/source/render/ShaderPreprocessor.cpp index 567fae0a834d8dec1e8bff7f678c49070272ff8f..cdfcc149012688ae42f109f01291b015bce9f2e3 100644 --- a/source/render/ShaderPreprocessor.cpp +++ b/source/render/ShaderPreprocessor.cpp @@ -545,7 +545,7 @@ String CShaderPreprocessor::process(const char *src, const char *file) String sOut; if(out.size()) { - sOut.reserve(out.size()); + sOut.resize(out.size()); memcpy((void*)(sOut.c_str()), &(out[0]), out.size() * sizeof(char)); ((char*)(sOut.c_str()))[out.size()] = 0; } @@ -1097,7 +1097,7 @@ String CShaderPreprocessor::getInclude(const String &name, const char *szLocalPa { int iSize = (int)pFile->getSize(); String s; - s.reserve(iSize + 2); + s.resize(iSize + 1); pFile->readBin((void*)(s.c_str()), iSize); s[iSize] = '\n'; s[iSize + 1] = 0; @@ -1119,7 +1119,7 @@ String CShaderPreprocessor::getInclude(const String &name, const char *szLocalPa { int iSize = (int)pFile->getSize(); String s; - s.reserve(iSize + 2); + s.resize(iSize + 1); pFile->readBin((void*)(s.c_str()), iSize); s[iSize] = '\n'; s[iSize + 1] = 0; diff --git a/source/render/plugin_main.cpp b/source/render/plugin_main.cpp index 0e510908d5fba6132a52ba7642596c3f96e1a97a..01d6d674333e70fb40f671902f51f54515d336df 100644 --- a/source/render/plugin_main.cpp +++ b/source/render/plugin_main.cpp @@ -15,6 +15,33 @@ #pragma comment(lib, "sxgame.lib") #endif +class CLoadLevelEventListener final: public IEventListener<XEventLevel> +{ +public: + CLoadLevelEventListener(CScene *pScene): + m_pScene(pScene) + { + } + void onEvent(const XEventLevel *pData) + { + switch(pData->type) + { + case XEventLevel::TYPE_LOAD_WAIT_ASYNC_TASKS: + while(m_pScene->hasPendingOps()) + { + Sleep(100); + } + break; + + default: + break; + } + } + +protected: + CScene *m_pScene; +}; + class CRenderPlugin: public IXUnknownImplementation<IXPlugin> { public: @@ -25,6 +52,10 @@ public: void XMETHODCALLTYPE shutdown() override { + if(m_pLevelLoadEventListener) + { + m_pCore->getEventChannel<XEventLevel>(EVENT_LEVEL_GUID)->removeListener(m_pLevelLoadEventListener); + } } UINT XMETHODCALLTYPE getInterfaceCount() override @@ -68,6 +99,9 @@ public: if(!m_pScene) { m_pScene = new CScene(m_pCore); + m_pLevelLoadEventListener = new CLoadLevelEventListener(m_pScene); + + m_pCore->getEventChannel<XEventLevel>(EVENT_LEVEL_GUID)->addListener(m_pLevelLoadEventListener); } *ppOut = m_pScene; break; @@ -144,6 +178,7 @@ protected: CScene *m_pScene = NULL; CUpdatable *m_pUpdatable = NULL; CRender *m_pRender = NULL; + CLoadLevelEventListener *m_pLevelLoadEventListener = NULL; }; DECLARE_XPLUGIN(CRenderPlugin); diff --git a/source/terrax/CommandBuildModel.cpp b/source/terrax/CommandBuildModel.cpp index f3ff59d42f8b94b22cca96ca04f36814cf8e700e..30484344f531eb76436cf3aa900bb4d877df50d1 100644 --- a/source/terrax/CommandBuildModel.cpp +++ b/source/terrax/CommandBuildModel.cpp @@ -5,7 +5,7 @@ extern AssotiativeArray<AAString, IXEditable*> g_mEditableSystems; CCommandBuildModel::CCommandBuildModel(const char *szTypeName, const char *szClassName) { - m_pCommandCreate = new CCommandCreate(float3_t(), szTypeName, szClassName); + m_pCommandCreate = new CCommandCreate(float3_t(), float3_t(), szTypeName, szClassName); } CCommandBuildModel::~CCommandBuildModel() diff --git a/source/terrax/CommandCreate.cpp b/source/terrax/CommandCreate.cpp index 7dac8882c0c4ba1f15c956157d58bd30376f6040..df0279c186a20a1bd50bb06be8390cf9023337ec 100644 --- a/source/terrax/CommandCreate.cpp +++ b/source/terrax/CommandCreate.cpp @@ -3,15 +3,15 @@ extern AssotiativeArray<AAString, IXEditable*> g_mEditableSystems; -CCommandCreate::CCommandCreate(const float3_t &vPos, const char *szTypeName, const char *szClassName, bool useRandomScaleYaw) +CCommandCreate::CCommandCreate(const float3_t &vPos, const float3_t &vNorm, const char *szTypeName, const char *szClassName, bool useRandomScaleYaw): + m_vPos(vPos), + m_vNorm(vNorm), + m_sClassName(szClassName) { - m_vPos = vPos; - m_sClassName = szClassName; - if(useRandomScaleYaw) { m_fScale = randf(0.7, 1.3); - m_qOrient = SMQuaternion(randf(0, SM_2PI), 'y'); + m_fRotAngle = randf(0, SM_2PI); } const AssotiativeArray<AAString, IXEditable*>::Node *pNode; @@ -42,10 +42,82 @@ bool XMETHODCALLTYPE CCommandCreate::exec() m_pObject->setPos(m_vPos); //! @TODO Implement random scale? //m_pObject->setScale(float3(m_fScale)); - m_pObject->setOrient(m_qOrient); + const char *szNormalAlign = m_pEditable->getClassKV(m_sClassName.c_str(), "align_with_norm"); + if(!SMIsZero(SMVector3Length2(m_vNorm)) && szNormalAlign) + { + char cAxis = szNormalAlign[0]; + float fVal = 1.0f; + if(cAxis == '-') + { + cAxis = szNormalAlign[1]; + float fVal = -1.0f; + } + float3_t vFrom; + switch(tolower(cAxis)) + { + case 'x': + vFrom.x = fVal; + break; + case 'y': + vFrom.y = fVal; + break; + case 'z': + vFrom.z = fVal; + break; + } + SMQuaternion qRot(vFrom, m_vNorm); + m_pObject->setOrient(qRot * SMQuaternion(m_vNorm, m_fRotAngle)); + } + else + { + m_pObject->setOrient(SMQuaternion(m_fRotAngle, 'y')); + } m_pObject->setSelected(true); m_pObject->create(); + if(!SMIsZero(SMVector3Length2(m_vNorm)) && !m_pEditable->getClassKV(m_sClassName.c_str(), "no_height_adj")) + { + float3 vMin, vMax; + m_pObject->getBound(&vMin, &vMax); + + float3 avPoints[] = { + float3(vMin), + float3(vMin.x, vMin.y, vMax.z), + float3(vMin.x, vMax.y, vMin.z), + float3(vMin.x, vMax.y, vMax.z), + float3(vMax.x, vMin.y, vMin.z), + float3(vMax.x, vMin.y, vMax.z), + float3(vMax.x, vMax.y, vMin.z), + float3(vMax), + }; + //printf("%.2f, %.2f, %.2f\n", vNorm.x, vNorm.y, vNorm.z); + + float fProj = FLT_MAX; + for(UINT i = 0, l = ARRAYSIZE(avPoints); i < l; ++i) + { + float fTmp = SMVector3Dot(avPoints[i], m_vNorm); + //printf("%.2f\n", fTmp); + if(fTmp < fProj) + { + fProj = fTmp; + } + } + + fProj = SMVector3Dot(m_vPos, m_vNorm) - fProj; + //printf("%.2f, %.2f, %.2f\n%.2f, %.2f, %.2f\n%.2f, %.2f, %.2f\n%f\n\n", vPos.x, vPos.y, vPos.z, vMin.x, vMin.y, vMin.z, vMax.x, vMax.y, vMax.z, fProj); + if(fProj > 0) + { + m_pObject->setPos((float3)(m_vPos + m_vNorm * fProj)); + } + } + + const char *szMaterialField = m_pEditable->getClassKV(m_sClassName.c_str(), "material_field"); + if(szMaterialField) + { + m_sMaterial = g_pEditor->getMaterialBrowser()->getCurrentMaterial(); + m_pObject->setKV(szMaterialField, m_sMaterial.c_str()); + } + g_pEditor->addObject(m_pObject); XUpdatePropWindow(); diff --git a/source/terrax/CommandCreate.h b/source/terrax/CommandCreate.h index 7b72e90d6951e71d3c9341e4bf8d7b3caa57e828..ba0b86dee61cb72cf1a9bb2e7985b64f7ac614f0 100644 --- a/source/terrax/CommandCreate.h +++ b/source/terrax/CommandCreate.h @@ -11,7 +11,7 @@ class CCommandCreate final: public IXUnknownImplementation<IXEditorCommand> { public: - CCommandCreate(const float3_t &vPos, const char *szTypeName, const char *szClassName, bool useRandomScaleYaw=false); + CCommandCreate(const float3_t &vPos, const float3_t &vNorm, const char *szTypeName, const char *szClassName, bool useRandomScaleYaw = false); ~CCommandCreate(); bool XMETHODCALLTYPE exec() override; @@ -34,11 +34,13 @@ public: protected: float3_t m_vPos; - SMQuaternion m_qOrient; + float3_t m_vNorm; + float m_fRotAngle = 0.0f; float m_fScale = 1.0f; String m_sClassName; IXEditable *m_pEditable = NULL; IXEditorObject *m_pObject = NULL; + String m_sMaterial; }; #endif diff --git a/source/terrax/Editor.cpp b/source/terrax/Editor.cpp index e17b7ce2adc3b572aa9e263cca13c1cfda303392..51185b8270e370ecc459640a978b514bc7400f05 100644 --- a/source/terrax/Editor.cpp +++ b/source/terrax/Editor.cpp @@ -38,6 +38,8 @@ CEditor::CEditor(IXCore *pCore): pCore->getPluginManager()->registerInterface(IXCOLORPICKER_GUID, &m_colorPicker); m_pSceneTreeWindow = new CSceneTreeWindow(this, pCore); + + registerResourceBrowser(new CResourceBrowser()); } CEditor::~CEditor() diff --git a/source/terrax/Editor.h b/source/terrax/Editor.h index fb18b38d0b571fa69c8fce49d0453603c580b7ce..3f15126b872c664856d8b2da1629f4a8945e3cf8 100644 --- a/source/terrax/Editor.h +++ b/source/terrax/Editor.h @@ -15,6 +15,7 @@ #include "ColorGradientEditorDialog.h" #include "ColorPicker.h" #include "SceneTreeWindow.h" +#include "ResourceBrowser.h" #define GIZMO_TYPES() \ GTO(Handle)\ diff --git a/source/terrax/GizmoRotate.cpp b/source/terrax/GizmoRotate.cpp index 763ce2076e5ccc4cefe0995d7f4a877d7f5e7348..f76dba18a05bed8aec9b22bfc12b94902ecdb572 100644 --- a/source/terrax/GizmoRotate.cpp +++ b/source/terrax/GizmoRotate.cpp @@ -1,5 +1,6 @@ #include "GizmoRotate.h" #include "Editor.h" +#include "terrax.h" CGizmoRotate::CGizmoRotate(CEditor *pEditor): m_pEditor(pEditor) @@ -266,7 +267,10 @@ void CGizmoRotate::setWorldRay(const float3 &vRayOrigin, const float3 &vRayDir) fAngle = SM_2PI - fAngle; } - fAngle = round_step(fAngle, 5.0f * SM_PIDIV180); + if(g_xConfig.m_bSnapGrid) + { + fAngle = round_step(fAngle, 5.0f * SM_PIDIV180); + } SAFE_CALL(m_pCallback, onRotate, m_vCurDir, fAngle, this) else diff --git a/source/terrax/GroupObject.cpp b/source/terrax/GroupObject.cpp index 5beb78e13a37a1a825bd2c00fd5406de316446e1..5fa1a1aa1a60ef5e00e2cd63c3326ec7ac12e930 100644 --- a/source/terrax/GroupObject.cpp +++ b/source/terrax/GroupObject.cpp @@ -303,7 +303,7 @@ void CGroupObject::addChildObject(IXEditorObject *pObject) ICompoundObject *pOldContainer = XTakeObject(pObject, this); assert(pOldContainer == NULL); - //add_ref(pObject); + add_ref(pObject); m_aObjects.push_back({pObject, m_qOrient.Conjugate() * (pObject->getPos() - m_vPos), pObject->getOrient() * m_qOrient.Conjugate()}); g_pEditor->onObjectAdded(pObject); @@ -323,7 +323,7 @@ void CGroupObject::removeChildObject(IXEditorObject *pObject) g_pEditor->onObjectRemoved(pObject); - //mem_release(pObject); + mem_release(pObject); if(!m_aObjects.size()) { diff --git a/source/terrax/PropertyWindow.cpp b/source/terrax/PropertyWindow.cpp index 07f5fd470d3462dbfe258ca4a945ef9f92aac320..475f2471c523e876fa3eba1e55e7639d3c323cc2 100644 --- a/source/terrax/PropertyWindow.cpp +++ b/source/terrax/PropertyWindow.cpp @@ -63,6 +63,7 @@ static UINT g_uEditorDlgIds[] = { IDD_PROPEDIT_TEXT, IDD_PROPEDIT_TEXT, IDD_PROPEDIT_TEXT, + IDD_PROPEDIT_TEXT, }; static_assert(ARRAYSIZE(g_uEditorDlgIds) == XPET__LAST, "g_uEditorDlgIds must match X_PROP_EDITOR_TYPE"); @@ -515,7 +516,7 @@ INT_PTR CALLBACK CPropertyWindow::dlgProc(HWND hWnd, UINT msg, WPARAM wParam, LP memset(&ofn, 0, sizeof(OPENFILENAMEW)); ofn.lStructSize = sizeof(OPENFILENAMEW); - ofn.hwndOwner = NULL; + ofn.hwndOwner = m_hDlgWnd; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = szFilter; @@ -819,7 +820,7 @@ void CPropertyWindow::initEditor(X_PROP_EDITOR_TYPE type, const void *pData, con { HWND hCombo = GetDlgItem(hEditorDlg, IDC_OPE_COMBO); ComboBox_ResetContent(hCombo); - edt_kv *pKV = (edt_kv*)pData; + kv_t *pKV = (kv_t*)pData; while(pKV->szKey) { ComboBox_AddString(hCombo, pKV->szKey); diff --git a/source/terrax/PropertyWindow.h b/source/terrax/PropertyWindow.h index 9047f29ce296a1cbb3b513e0ef1095741b25f932..2f8db9f8154cb4f20569f33891098f0abba36205 100644 --- a/source/terrax/PropertyWindow.h +++ b/source/terrax/PropertyWindow.h @@ -105,13 +105,7 @@ protected: X_PROP_FIELD field; String sValue; }; - - struct edt_kv - { - const char *szKey; - const char *szValue; - }; - + AssotiativeArray<AAString, prop_s> m_aPropFields; X_PROP_EDITOR_TYPE m_editorActive = XPET__LAST; diff --git a/source/terrax/ResourceBrowser.cpp b/source/terrax/ResourceBrowser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f0be58de59bbfd1f595898efd1bd4e7537348d6 --- /dev/null +++ b/source/terrax/ResourceBrowser.cpp @@ -0,0 +1,26 @@ +#include "ResourceBrowser.h" +#include "terrax.h" + +UINT XMETHODCALLTYPE CResourceBrowser::getResourceTypeCount() +{ + return(1); +} +const char* XMETHODCALLTYPE CResourceBrowser::getResourceType(UINT uId) +{ + if(uId == 0) + { + return("material"); + } + + return(NULL); +} + +void XMETHODCALLTYPE CResourceBrowser::browse(const char *szType, const char *szOldValue, IXEditorResourceBrowserCallback *pCallback) +{ + m_callback.init(pCallback); + g_pMaterialBrowser->browse(&m_callback); +} +void XMETHODCALLTYPE CResourceBrowser::cancel() +{ + g_pMaterialBrowser->abort(); +} diff --git a/source/terrax/ResourceBrowser.h b/source/terrax/ResourceBrowser.h new file mode 100644 index 0000000000000000000000000000000000000000..04b530571e612b34d2ab3904d5d6de3979039b22 --- /dev/null +++ b/source/terrax/ResourceBrowser.h @@ -0,0 +1,46 @@ +#ifndef __RESOURCEBROWSER_H +#define __RESOURCEBROWSER_H + +#include <xcommon/editor/IXEditorExtension.h> +#include "MaterialBrowser.h" + +class CResourceMaterialBrowserCallback: public IMaterialBrowserCallback +{ +public: + void init(IXEditorResourceBrowserCallback *pCallback) + { + SAFE_CALL(m_pCallback, onCancelled); + m_pCallback = pCallback; + } + void onSelected(const char *szName) override + { + SAFE_CALL(m_pCallback, onSelected, szName); + m_pCallback = NULL; + } + void onCancel() override + { + SAFE_CALL(m_pCallback, onCancelled); + m_pCallback = NULL; + } + + +private: + IXEditorResourceBrowserCallback *m_pCallback = NULL; +}; + +//############################################################################# + +class CResourceBrowser: public IXUnknownImplementation<IXEditorResourceBrowser> +{ +public: + UINT XMETHODCALLTYPE getResourceTypeCount() override; + const char* XMETHODCALLTYPE getResourceType(UINT uId) override; + + void XMETHODCALLTYPE browse(const char *szType, const char *szOldValue, IXEditorResourceBrowserCallback *pCallback) override; + void XMETHODCALLTYPE cancel() override; + +private: + CResourceMaterialBrowserCallback m_callback; +}; + +#endif diff --git a/source/terrax/mainWindow.cpp b/source/terrax/mainWindow.cpp index 2b9b8270d6d0ea98f04ab754583d675e0cc27b04..763ed59dfcc6e89cb97f7dec07fc4299f6543749 100644 --- a/source/terrax/mainWindow.cpp +++ b/source/terrax/mainWindow.cpp @@ -972,7 +972,7 @@ guid = {9D7D2E62-24C7-42B7-8D83-8448FC4604F0} mem_release(pConfig); } -static CCommandCreate* CreateObjectAtPosition(const float3 &vPos, bool bDeselectAll) +static CCommandCreate* CreateObjectAtPosition(const float3 &vPos, const float3 &vNorm, bool bDeselectAll) { int iSel1 = ComboBox_GetCurSel(g_hComboTypesWnd); int iLen1 = ComboBox_GetLBTextLen(g_hComboTypesWnd, iSel1); @@ -989,7 +989,7 @@ static CCommandCreate* CreateObjectAtPosition(const float3 &vPos, bool bDeselect SendMessage(g_hWndMain, WM_COMMAND, MAKEWPARAM(ID_EDIT_CLEARSELECTION, 0), (LPARAM)0); } - CCommandCreate *pCmd = new CCommandCreate(vPos, szTypeName, szClassName, Button_GetCheck(g_hCheckboxRandomScaleYawWnd)); + CCommandCreate *pCmd = new CCommandCreate(vPos, vNorm, szTypeName, szClassName, Button_GetCheck(g_hCheckboxRandomScaleYawWnd)); g_pUndoManager->execCommand(pCmd); return(pCmd); @@ -1947,7 +1947,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case IDC_CTRL_RETURN: if(g_xState.bCreateMode) { - CreateObjectAtPosition(g_xState.vCreateOrigin, GetKeyState(VK_CONTROL) >= 0); + CreateObjectAtPosition(g_xState.vCreateOrigin, float3(0.0f), GetKeyState(VK_CONTROL) >= 0); g_xState.bCreateMode = false; } @@ -2085,6 +2085,18 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) XSetXformType(X2DXF_ROTATE); break; + case ID_XFORM_MODE_CENTER: + g_xConfig.m_bUsePivot = false; + CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot); + CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot); + + break; + case ID_XFORM_MODE_PIVOT: + g_xConfig.m_bUsePivot = true; + CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot); + CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot); + break; + case IDC_TIE_TO_OBJECT: if(IsWindowEnabled(g_hButtonToEntityWnd)) { @@ -3273,40 +3285,8 @@ LRESULT CALLBACK RenderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP vPos = s_aRaytracedItems[0].vPos; vNorm = SMVector3Normalize(s_aRaytracedItems[0].vNorm); - IXEditorObject *pObj = CreateObjectAtPosition(vPos, GetKeyState(VK_CONTROL) >= 0)->getObject(); - float3 vMin, vMax; - pObj->getBound(&vMin, &vMax); - - float3 avPoints[] = { - float3(vMin), - float3(vMin.x, vMin.y, vMax.z), - float3(vMin.x, vMax.y, vMin.z), - float3(vMin.x, vMax.y, vMax.z), - float3(vMax.x, vMin.y, vMin.z), - float3(vMax.x, vMin.y, vMax.z), - float3(vMax.x, vMax.y, vMin.z), - float3(vMax), - }; - //printf("%.2f, %.2f, %.2f\n", vNorm.x, vNorm.y, vNorm.z); - - float fProj = FLT_MAX; - for(UINT i = 0, l = ARRAYSIZE(avPoints); i < l; ++i) - { - float fTmp = SMVector3Dot(avPoints[i], vNorm); - //printf("%.2f\n", fTmp); - if(fTmp < fProj) - { - fProj = fTmp; - } - } - - fProj = SMVector3Dot(vPos, vNorm) - fProj; - //printf("%.2f, %.2f, %.2f\n%.2f, %.2f, %.2f\n%.2f, %.2f, %.2f\n%f\n\n", vPos.x, vPos.y, vPos.z, vMin.x, vMin.y, vMin.z, vMax.x, vMax.y, vMax.z, fProj); - if(fProj > 0) - { - pObj->setPos((float3)(vPos + vNorm * fProj)); - } - + IXEditorObject *pObj = CreateObjectAtPosition(vPos, vNorm, GetKeyState(VK_CONTROL) >= 0)->getObject(); + s_aRaytracedItems.clearFast(); } } @@ -3418,7 +3398,7 @@ LRESULT CALLBACK RenderWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP { // create rotate command s_pRotateCmd = new CCommandRotate(GetKeyState(VK_SHIFT) < 0); - s_pRotateCmd->setStartOrigin((g_xState.vSelectionBoundMax + g_xState.vSelectionBoundMin) * 0.5f * vMask, float3(1.0f) - vMask); + s_pRotateCmd->setStartOrigin((g_xConfig.m_bUsePivot ? g_xState.vSelectionPivot : (g_xState.vSelectionBoundMax + g_xState.vSelectionBoundMin) * 0.5f) * vMask, float3(1.0f) - vMask); s_pRotateCmd->setStartPos(vStartPos); XEnumerateObjects([&](IXEditorObject *pObj, bool isProxy, ICompoundObject *pParent){ if(pObj->isSelected()/* && (g_xConfig.m_bIgnoreGroups ? !isProxy : !pParent)*/) @@ -4487,7 +4467,7 @@ case editor_type: if(pItem->m_pObj == pObj && !fstrcmp(pItem->m_field.szKey, pField->szKey)) \ { \ pItem->m_uUpdateRevision = g_uPropGizmoUpdateRevision; \ - pItem->init(); \ + pItem->init(pField->pEditorData); \ return; \ } \ } \ @@ -4496,7 +4476,7 @@ case editor_type: pCb->m_pObj = pObj; \ pCb->m_field = *pField; \ pCb->m_uUpdateRevision = g_uPropGizmoUpdateRevision; \ - pCb->init(); \ + pCb->init(pField->pEditorData); \ g_aPropGizmo##gizmo##Items.push_back(pCb); \ } \ break @@ -4519,7 +4499,7 @@ XDECLARE_PROP_GIZMO(Radius, void XMETHODCALLTYPE onChange(float fNewRadius, IXEd char tmp[16]; sprintf(tmp, "%f", fNewRadius); m_pCommand->setKV(m_field.szKey, tmp); -}, void init() +}, void init(const void *pEditorData) { m_pGizmo->setPos(m_pObj->getPos()); float fRadius; @@ -4536,13 +4516,69 @@ XDECLARE_PROP_GIZMO(Handle, void XMETHODCALLTYPE moveTo(const float3 &vNewPos, I char tmp[64]; sprintf(tmp, "%f %f %f", vTmp.x, vTmp.y, vTmp.z); m_pCommand->setKV(m_field.szKey, tmp); -}, void init() +}, void init(const void *pEditorData) { float3_t vec; if(sscanf(m_pObj->getKV(m_field.szKey), "%f %f %f", &vec.x, &vec.y, &vec.z)) { m_pGizmo->setPos(m_pObj->getOrient() * vec + m_pObj->getPos()); } + + if(pEditorData) + { + kv_t *pKV = (kv_t*)pEditorData; + bool bLockPlane = false; + bool bLockAxis = false; + bool bLockLocal = true; + char axis = 0; + while(pKV->szKey) + { + if(!strcmp(pKV->szKey, "lock")) + { + if(!strcmp(pKV->szValue, "plane")) + { + bLockPlane = true; + } + else if(!strcmp(pKV->szValue, "axis")) + { + bLockAxis = true; + } + } + else if(!strcmp(pKV->szKey, "axis")) + { + axis = pKV->szValue[0]; + } + else if(!strcmp(pKV->szKey, "local")) + { + if(pKV->szValue[0] != '0') + { + bLockLocal = true; + } + } + ++pKV; + } + + if((bLockPlane || bLockAxis) && axis) + { + float3_t vAxis(axis == 'x' ? 1.0f : 0.0, axis == 'y' ? 1.0f : 0.0, axis == 'z' ? 1.0f : 0.0); + if(bLockLocal) + { + vAxis = m_pObj->getOrient() * vAxis; + } + if(bLockPlane) + { + m_pGizmo->lockInPlane(vAxis); + } + else + { + m_pGizmo->lockInDir(vAxis); + } + } + else + { + m_pGizmo->unLock(); + } + } }); UINT g_uPropGizmoUpdateRevision = 0; @@ -4729,7 +4765,7 @@ void XUpdateGizmos() { if(Button_GetCheck(g_hABArrowButton) && g_xState.bHasSelection) { - float3 vPos = (g_xState.vSelectionBoundMin + g_xState.vSelectionBoundMax) * 0.5f; + float3 vPos = g_xConfig.m_bUsePivot ? g_xState.vSelectionPivot : (g_xState.vSelectionBoundMin + g_xState.vSelectionBoundMax) * 0.5f; g_pGizmoMove->setPos(vPos); if(!g_gizmoRotateCallback.isActive()) { @@ -4849,7 +4885,7 @@ HWND CreateToolbar(HWND hWndParent) { // Declare and initialize local constants. const int ImageListID = 0; - const int numButtons = 8; + const int numButtons = 10; const int bitmapSize = 16; const DWORD buttonStyles = BTNS_AUTOSIZE; @@ -4909,6 +4945,9 @@ HWND CreateToolbar(HWND hWndParent) {MAKELONG(1, ImageListID), ID_XFORM_TRANSLATE, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Move [W]"}, {MAKELONG(2, ImageListID), ID_XFORM_ROTATE, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Rotate [R]"}, {0, 0, TBSTATE_ENABLED, BTNS_SEP, 0L, 0}, + {MAKELONG(8, ImageListID), ID_XFORM_MODE_CENTER, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Use center"}, + {MAKELONG(9, ImageListID), ID_XFORM_MODE_PIVOT, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Use pivot"}, + {0, 0, TBSTATE_ENABLED, BTNS_SEP, 0L, 0}, {MAKELONG(6, ImageListID), ID_TOOLS_GROUP, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Group selected [Ctrl+G]"}, {MAKELONG(7, ImageListID), ID_TOOLS_UNGROUP, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Ungroup selected [Ctrl+U]"}, {MAKELONG(5, ImageListID), ID_IGNORE_GROUPS, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)"Toggle group ignore [Ctrl+W]"}, diff --git a/source/terrax/resource.h b/source/terrax/resource.h index bdbd2937659c1c2521c71074b5341ca191f4b3a6..8b0c2331f8a92e4071aab850ebda31aae84af055 100644 Binary files a/source/terrax/resource.h and b/source/terrax/resource.h differ diff --git a/source/terrax/resource/toolbar1.bmp b/source/terrax/resource/toolbar1.bmp index dbb1f0b437a3419ade41cafc9834107c00921d22..eb7529bf301a6746730ae4c975acc9601860c34a 100644 Binary files a/source/terrax/resource/toolbar1.bmp and b/source/terrax/resource/toolbar1.bmp differ diff --git a/source/terrax/resource/toolbar2.bmp b/source/terrax/resource/toolbar2.bmp index 5051572bc72dbbd0c0b1a1af2b48dcd827182f2d..e581a1f306259eb0cd1bb90037e55cad7a7a2a8e 100644 Binary files a/source/terrax/resource/toolbar2.bmp and b/source/terrax/resource/toolbar2.bmp differ diff --git a/source/terrax/terrax.cpp b/source/terrax/terrax.cpp index 3c256fea72a642fd489ff99dbf2b43a3c2ff8ea8..3a73567ac904ab6f2d5fc1e6b3a97cc438d8095a 100644 --- a/source/terrax/terrax.cpp +++ b/source/terrax/terrax.cpp @@ -736,6 +736,8 @@ int main(int argc, char **argv) g_pXformEventChannel = pEngine->getCore()->getEventChannel<XEventEditorXformType>(EVENT_EDITOR_XFORM_TYPE_GUID); XSetXformType(X2DXF_SCALE); + CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot); + CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot); RECT rcTopLeft; GetClientRect(g_hTopLeftWnd, &rcTopLeft); @@ -825,7 +827,7 @@ int main(int argc, char **argv) g_pEditor->registerResourceBrowser(pResourceBrowser); mem_release(pResourceBrowser); // XInitTool(pResourceBrowser, pEditable); - } + } } } @@ -932,6 +934,33 @@ int main(int argc, char **argv) } } + XUpdateStatusGrid(); + + szVal = pCfg->getKey("terrax", "ignore_groups"); + if(szVal) + { + int iVal = 0; + if(sscanf(szVal, "%d", &iVal)) + { + g_xConfig.m_bIgnoreGroups = iVal != 0; + CheckToolbarButton(ID_IGNORE_GROUPS, g_xConfig.m_bIgnoreGroups); + } + } + + szVal = pCfg->getKey("terrax", "xform_use_pivot"); + if(szVal) + { + int iVal = 0; + if(sscanf(szVal, "%d", &iVal)) + { + g_xConfig.m_bUsePivot = iVal != 0; + CheckToolbarButton(ID_XFORM_MODE_PIVOT, g_xConfig.m_bUsePivot); + CheckToolbarButton(ID_XFORM_MODE_CENTER, !g_xConfig.m_bUsePivot); + } + } + + + for(UINT i = 0; i < 4; ++i) { float3 vec; @@ -1079,6 +1108,12 @@ int main(int argc, char **argv) sprintf_s(szVal, "%d", g_xConfig.m_bShowGrid ? 1 : 0); pCfg->set("terrax", "grid_show", szVal); + sprintf_s(szVal, "%d", g_xConfig.m_bIgnoreGroups ? 1 : 0); + pCfg->set("terrax", "ignore_groups", szVal); + + sprintf_s(szVal, "%d", g_xConfig.m_bUsePivot ? 1 : 0); + pCfg->set("terrax", "xform_use_pivot", szVal); + pCfg->save(); mem_release(pCfg); @@ -2617,21 +2652,53 @@ void XUpdateSelectionBound() g_xState.bHasSelection = false; float3 vMin, vMax; + extern HWND g_hABArrowButton; + bool bTrackPivotObject = g_xConfig.m_bUsePivot && g_xState.activeWindow == XWP_TOP_LEFT && Button_GetCheck(g_hABArrowButton); + float fBestDist = FLT_MAX; + XEnumerateObjects([&](IXEditorObject *pObj, bool isProxy, ICompoundObject *pParent){ if(pObj->isSelected()/* && !(g_xConfig.m_bIgnoreGroups && isProxy)*/) { pObj->getBound(&vMin, &vMax); + + if(bTrackPivotObject) + { + // raytest + g_xState.vWorldRayStart; + g_xState.vWorldRayDir; + if(pObj->hasVisualModel()) + { + float3 vPos; + if(pObj->rayTest(g_xState.vWorldRayStart, g_xState.vWorldRayStart + g_xState.vWorldRayDir * 1000.0f, &vPos)) + { + float fDist2 = SMVector3Length2(g_xState.vWorldRayStart - vPos); + if(fDist2 < fBestDist) + { + fBestDist = fDist2; + g_xState.pPivotSource = pObj; + } + } + } + } + if(!g_xState.bHasSelection) { g_xState.bHasSelection = true; g_xState.vSelectionBoundMax = vMax; g_xState.vSelectionBoundMin = vMin; + g_xState.vSelectionPivot = pObj->getPos(); } else { g_xState.vSelectionBoundMax = (float3)SMVectorMax(g_xState.vSelectionBoundMax, vMax); g_xState.vSelectionBoundMin = (float3)SMVectorMin(g_xState.vSelectionBoundMin, vMin); } + + if(g_xState.pPivotSource == pObj) + { + g_xState.vSelectionPivot = pObj->getPos(); + } + return(XEOR_SKIP_CHILDREN); } return(XEOR_CONTINUE); diff --git a/source/terrax/terrax.h b/source/terrax/terrax.h index 0f512ce832315f45f4e01dfa3faa42037ee384f1..0185bbc4a1dc142404940dce31f3b53946d79143 100644 --- a/source/terrax/terrax.h +++ b/source/terrax/terrax.h @@ -92,6 +92,7 @@ struct CTerraXConfig bool m_bDottedGrid = false; float m_fGridOpacity = 0.5f; bool m_bIgnoreGroups = false; + bool m_bUsePivot = false; X_VIEWPORT_LAYOUT m_xViewportLayout = XVIEW_2X2; }; @@ -108,6 +109,8 @@ struct CTerraXState: public TerraXState bool bHasSelection = false; float3_t vSelectionBoundMin; float3_t vSelectionBoundMax; + float3_t vSelectionPivot; + IXEditorObject *pPivotSource = NULL; X_2DXFORM_TYPE xformType = X2DXF_SCALE; @@ -299,6 +302,7 @@ bool XIsMouseInBound(X_WINDOW_POS wnd, const float3_t &vBoundMin, const float3_t void XUpdatePropWindow(); void XUpdateGizmos(); +void XUpdateStatusGrid(); void XInitTypesCombo(); diff --git a/source/xParticles/Editable.h b/source/xParticles/Editable.h index af162845fe61a18fbfdc016fd118a546abdc6f4c..bbfe486f44d41ad889888c5f3b0df3289089b280 100644 --- a/source/xParticles/Editable.h +++ b/source/xParticles/Editable.h @@ -79,6 +79,11 @@ public: return(false); } + const char* XMETHODCALLTYPE getClassKV(const char *szClassName, const char *szKey) override + { + return(NULL); + } + private: CEditorObject* getObjectByGUID(const XGUID &guid); diff --git a/source/xSound/SoundSystem.cpp b/source/xSound/SoundSystem.cpp index fbd7d88ca98b20507b44714a95f52f1c2897144d..7a79ed7a4448d0801b231a9ec0ba788c13d00695 100644 --- a/source/xSound/SoundSystem.cpp +++ b/source/xSound/SoundSystem.cpp @@ -243,9 +243,9 @@ IXAudioCodecTarget* CSoundSystem::getCodecTarget(const char *szName) for(MapCodec::Iterator i = m_mapCodecs.begin(); i; ++i) { - if(m_mapCodecs[i.first]->open(szPath, "", &pTarget, false)) + if(m_mapCodecs[*i.first]->open(szPath, "", &pTarget, false)) { - pCodec = m_mapCodecs[i.first]; + pCodec = m_mapCodecs[*i.first]; break; } } @@ -411,8 +411,8 @@ IXAudioCodec* CSoundSystem::getCodecSave() { for(MapCodec::Iterator i = m_mapCodecs.begin(); i; ++i) { - if(m_mapCodecs[i.first]->canSave()) - return m_mapCodecs[i.first]; + if(m_mapCodecs[*i.first]->canSave()) + return m_mapCodecs[*i.first]; } return NULL; diff --git a/source/xUI/UIControl.h b/source/xUI/UIControl.h index 34803a3db394e2cda656209e536171a1cab9f954..fab4481021018a395ef4510d7d216d83fd7b949f 100644 --- a/source/xUI/UIControl.h +++ b/source/xUI/UIControl.h @@ -274,7 +274,7 @@ protected: gui::dom::IDOMnode *m_pNode = NULL; gui::dom::IDOMnode *m_pInputNode = NULL; gui::dom::IDOMnode *m_pContainerNode = NULL; - const ULONG m_id; + const UINT m_id; const String m_sName; friend class CUIWindow; diff --git a/source/xUI/UIViewport.cpp b/source/xUI/UIViewport.cpp index 989921e9b303b116db11a8f351c3a6adaac8ec87..495b9560f381daa598a714ad30deb99776ff3455 100644 --- a/source/xUI/UIViewport.cpp +++ b/source/xUI/UIViewport.cpp @@ -46,5 +46,5 @@ void XMETHODCALLTYPE CUIViewport::setSize(float fSizeX, float fSizeY) { BaseClass::setSize(fSizeX, fSizeY); - m_pRenderTarget->resize((UINT)fSizeX, (UINT)fSizeY); + m_pRenderTarget->resize(fSizeX > 1.0f ? (UINT)fSizeX : 1, fSizeY > 1.0f ? (UINT)fSizeY : 1); } diff --git a/source/xUI/UIWindow.cpp b/source/xUI/UIWindow.cpp index 4fa6863ed70af72eb8a495facfa4295db91fd1cb..a6ae35a5e2741262a33708894ba7c31cc7700207 100644 --- a/source/xUI/UIWindow.cpp +++ b/source/xUI/UIWindow.cpp @@ -108,9 +108,22 @@ CUIWindow::CUIWindow(CXUI *pXUI, const XWINDOW_DESC *pWindowDesc, IUIWindow *pPa m_pXWindowCallback = new CWindowCallback(this, m_pDesktopStack); m_pXWindow = pXUI->getWindowSystem()->createWindow(pWindowDesc, m_pXWindowCallback, pXParent); - createSwapChain((UINT)pWindowDesc->iSizeX, (UINT)pWindowDesc->iSizeY); + float fScale = m_pXWindow->getScale(); + + if(fScale != 1.0f) + { + XWINDOW_DESC newDesc = *pWindowDesc; + newDesc.iSizeX *= fScale; + newDesc.iSizeY *= fScale; + m_pXWindow->update(&newDesc); + createSwapChain((UINT)newDesc.iSizeX, (UINT)newDesc.iSizeY); + } + else + { + createSwapChain((UINT)pWindowDesc->iSizeX, (UINT)pWindowDesc->iSizeY); + } - m_pDesktopStack->setScale(m_pXWindow->getScale()); + m_pDesktopStack->setScale(fScale); m_pControl = new CUIWindowControl(this, 0); } diff --git a/source/xcommon/IXModelLoader.h b/source/xcommon/IXModelLoader.h index f6e99f3a7fc81cd1e8341dfd7d6103fac1f9a133..f4bb15d67c9290efd11d93e33808714829bbd16b 100644 --- a/source/xcommon/IXModelLoader.h +++ b/source/xcommon/IXModelLoader.h @@ -33,7 +33,7 @@ struct XModelInfo //! количество костей UINT uBoneCount; - //! количетсов анимаций + //! количество анимаций UINT uAnimationCount; //! габариты в метрах diff --git a/source/xcommon/XEvents.h b/source/xcommon/XEvents.h index fc73870971bcc377ed16e646b9d90a5a76425af9..09b2ae9f8172df10d57b6dccef02e38c1f14e9b7 100644 --- a/source/xcommon/XEvents.h +++ b/source/xcommon/XEvents.h @@ -95,6 +95,8 @@ struct XEventLevel TYPE_LOAD_END, //!< Уровень полностью загружен TYPE_UNLOAD_BEGIN, //!< До начала выгрузки уровня TYPE_UNLOAD_END, //!< Уровень полностью выгружен + + TYPE_LOAD_WAIT_ASYNC_TASKS, //!< Ожидание завершения асинхронных задач } type; const char *szLevelName; }; diff --git a/source/xcommon/editor/IXEditable.h b/source/xcommon/editor/IXEditable.h index 17cd062da54204939f4feff6707b9018f3f4d3db..abcd208faa589896dfe7271dd0a4bff7d7c60398 100644 --- a/source/xcommon/editor/IXEditable.h +++ b/source/xcommon/editor/IXEditable.h @@ -58,7 +58,7 @@ public: virtual const char* XMETHODCALLTYPE getName() = 0; virtual UINT XMETHODCALLTYPE getClassCount() = 0; virtual const char* XMETHODCALLTYPE getClass(UINT id) = 0; - + virtual void XMETHODCALLTYPE startup(IGXDevice *pDevice) = 0; virtual void XMETHODCALLTYPE shutdown() = 0; @@ -72,6 +72,7 @@ public: virtual bool XMETHODCALLTYPE canUseModel(const char *szClass) = 0; + virtual const char* XMETHODCALLTYPE getClassKV(const char *szClassName, const char *szKey) = 0; }; diff --git a/source/xcommon/editor/IXEditorObject.h b/source/xcommon/editor/IXEditorObject.h index b1643d970bec41590c397ed6122c0340bdf92191..156c7e2dd35d6a65ba9c4f4ed9564369ea539b15 100644 --- a/source/xcommon/editor/IXEditorObject.h +++ b/source/xcommon/editor/IXEditorObject.h @@ -16,6 +16,7 @@ enum X_PROP_EDITOR_TYPE XPET_RADIUS, XPET_COLOR, XPET_HDRCOLOR, + XPET_SIZE, //XPET_YESNO, XPET__LAST diff --git a/source/xcommon/resource/IXDecal.h b/source/xcommon/resource/IXDecal.h new file mode 100644 index 0000000000000000000000000000000000000000..67038ed8f4d4febb2d62e7248b1459d922034050 --- /dev/null +++ b/source/xcommon/resource/IXDecal.h @@ -0,0 +1,44 @@ +#ifndef __IXDECAL_H +#define __IXDECAL_H + +#include <gdefines.h> +#include "IXResourceModel.h" + +class IXDecal: public IXUnknown +{ +public: + virtual float3 XMETHODCALLTYPE getPosition() const = 0; + virtual void XMETHODCALLTYPE setPosition(const float3 &vPos) = 0; + + virtual SMQuaternion XMETHODCALLTYPE getOrientation() const = 0; + virtual void XMETHODCALLTYPE setOrientation(const SMQuaternion &qRot) = 0; + + virtual float3 XMETHODCALLTYPE getLocalBoundMin() const = 0; + virtual float3 XMETHODCALLTYPE getLocalBoundMax() const = 0; + virtual SMAABB XMETHODCALLTYPE getLocalBound() const = 0; + + virtual bool XMETHODCALLTYPE isEnabled() const = 0; + virtual void XMETHODCALLTYPE enable(bool yesNo) = 0; + + virtual bool XMETHODCALLTYPE rayTest(const float3 &vStart, const float3 &vEnd, float3 *pvOut = NULL, float3 *pvNormal = NULL) = 0; + + virtual void XMETHODCALLTYPE setLayer(UINT uLayer) = 0; + virtual UINT XMETHODCALLTYPE getLayer() = 0; + + virtual void XMETHODCALLTYPE setHeight(float fHeight) = 0; + virtual float XMETHODCALLTYPE getHeight() = 0; + + virtual void XMETHODCALLTYPE setTextureRangeU(const float2_t &vRange) = 0; + virtual float2_t XMETHODCALLTYPE getTextureRangeU() = 0; + + virtual void XMETHODCALLTYPE setTextureRangeV(const float2_t &vRange) = 0; + virtual float2_t XMETHODCALLTYPE getTextureRangeV() = 0; + + virtual void XMETHODCALLTYPE setMaterial(const char *szMaterial) = 0; + + virtual void XMETHODCALLTYPE setCorner(UINT uCorner, const float2_t &vCorner) = 0; + + virtual void XMETHODCALLTYPE setLeakAllowed(bool yesNo) = 0; +}; + +#endif diff --git a/source/xcommon/resource/IXDecalProvider.h b/source/xcommon/resource/IXDecalProvider.h new file mode 100644 index 0000000000000000000000000000000000000000..38420258815f8383c1d60823f6254c6a031263da --- /dev/null +++ b/source/xcommon/resource/IXDecalProvider.h @@ -0,0 +1,34 @@ +#ifndef __IXDECALPROVIDER_H +#define __IXDECALPROVIDER_H + +#include "IXDecal.h" + +// {878E7A1E-86D4-4967-9A6D-6054B4363119} +#define IXDECALPROVIDER_GUID DEFINE_XGUID(0x878e7a1e, 0x86d4, 0x4967, 0x9a, 0x6d, 0x60, 0x54, 0xb4, 0x36, 0x31, 0x19) + +enum XDECAL_TYPE +{ + XDT_CONCRETE = 0, + XDT_METAL, + XDT_GLASS, + XDT_PLASTIC, + XDT_WOOD, + XDT_FLESH, + XDT_EARTH, + XDT_BLOOD_BIG, + + XDT__COUNT +}; + +// Implemented in anim plugin +class IXDecalProvider: public IXUnknown +{ +public: + // create custom decal + virtual bool XMETHODCALLTYPE newDecal(IXDecal **ppDecal) = 0; + + // create temporal standard decal at point/normal + virtual void XMETHODCALLTYPE shootDecal(XDECAL_TYPE type, const float3 &vWorldPos, const float3 &vNormal, float fScale = 1.0f, const float3 *pvSAxis = NULL) = 0; +}; + +#endif diff --git a/source/xcsg/BrushMesh.cpp b/source/xcsg/BrushMesh.cpp index 79a1ee1f1d0f7bf6a4ca164cd4e4205f8e86a4a5..a1a1cba20a0eddd206731d1522bdc051bebf964e 100644 --- a/source/xcsg/BrushMesh.cpp +++ b/source/xcsg/BrushMesh.cpp @@ -64,7 +64,7 @@ void CBrushMesh::enable(bool yesNo) } } -void CBrushMesh::buildMesh(CMeshBuilder *pBuilder) +void CBrushMesh::buildMesh(CMeshBuilder *pBuilder, bool bOnlyComputeCounts) { Array<UINT> aSubsets(m_aMaterials.size()); fora(i, m_aMaterials) @@ -82,6 +82,14 @@ void CBrushMesh::buildMesh(CMeshBuilder *pBuilder) TextureInfo &texInfo = face.texInfo; Subset &subset = pBuilder->getSubset(aSubsets[texInfo.uMatId]); + if(bOnlyComputeCounts) + { + UINT uEdges = face.aEdges.size(); + subset.uVertexCount += uEdges; + subset.uIndexCount += (uEdges - 2) * 3; + continue; + } + bool isFirst = true; UINT uFirstIdx; UINT uNextIdx; @@ -161,7 +169,9 @@ void CBrushMesh::buildModel(bool bBuildPhysbox) } CMeshBuilder meshBuilder; - buildMesh(&meshBuilder); + buildMesh(&meshBuilder, true); + meshBuilder.allocArrays(); + buildMesh(&meshBuilder, false); // create model IXResourceManager *pRM = m_pCore->getResourceManager(); @@ -2417,6 +2427,16 @@ UINT CMeshBuilder::getSubsetIndexForMaterial(const char *szMat) return(idx); } +void CMeshBuilder::allocArrays() +{ + fora(i, m_aSubsets) + { + Subset &ss = m_aSubsets[i]; + ss.aIndices.reserve(ss.uIndexCount); + ss.aVertices.reserve(ss.uVertexCount); + } +} + UINT CMeshBuilder::getSubsetCount() { return(m_aMaterials.size()); diff --git a/source/xcsg/BrushMesh.h b/source/xcsg/BrushMesh.h index 352c664c13babbabdfd9b977f855d312d13e7bca..39f72d434e2938a725d9cc3558aa89daa86b2e68 100644 --- a/source/xcsg/BrushMesh.h +++ b/source/xcsg/BrushMesh.h @@ -34,6 +34,9 @@ struct Subset { Array<XResourceModelStaticVertex> aVertices; Array<UINT> aIndices; + + UINT uVertexCount = 0; + UINT uIndexCount = 0; }; class CMeshBuilder @@ -48,6 +51,8 @@ public: void buildResource(IXResourceModelStatic *pResource, UINT idx = UINT_MAX); + void allocArrays(); + private: Array<Subset> m_aSubsets; Array<String> m_aMaterials; @@ -101,7 +106,7 @@ public: bool findInternalFace(Array<float3_t> &aDest); bool fillInternalFace(const Array<float3_t> &aSrc); - void buildMesh(CMeshBuilder *pBuilder); + void buildMesh(CMeshBuilder *pBuilder, bool bOnlyComputeCounts); void buildPhysbox(IXResourceModel *pResource); void setFinalized(bool set); diff --git a/source/xcsg/Combiner.cpp b/source/xcsg/Combiner.cpp index 5ce45e31280fa73e5d34f79ce0d3b6bc1dc5a599..63f0cdad3a07a1f863b7805b74b76f4fcea61f17 100644 --- a/source/xcsg/Combiner.cpp +++ b/source/xcsg/Combiner.cpp @@ -33,7 +33,17 @@ void CCombiner::build() if(!aObjects[i]->getModel()) { hasObjects = true; - aObjects[i]->buildMesh(&meshBuilder); + aObjects[i]->buildMesh(&meshBuilder, true); + } + } + + meshBuilder.allocArrays(); + + fora(i, aObjects) + { + if(!aObjects[i]->getModel()) + { + aObjects[i]->buildMesh(&meshBuilder, false); aObjects[i]->buildPhysbox(pResource); } } diff --git a/source/xcsg/Editable.h b/source/xcsg/Editable.h index 8d88c2d9b081b928410d7b1bd7868b17cde75ba1..6df39e2befc5f1b11dd02eb6d368ad5be5c9cddf 100644 --- a/source/xcsg/Editable.h +++ b/source/xcsg/Editable.h @@ -103,6 +103,11 @@ public: void onModelDestroy(CEditorModel *pModel); void onModelRestored(CEditorModel *pModel); + const char* XMETHODCALLTYPE getClassKV(const char *szClassName, const char *szKey) override + { + return(NULL); + } + SX_ALIGNED_OP_MEM(); CVertexTool* getVertexTool(); diff --git a/source/xcsg/EditorModel.cpp b/source/xcsg/EditorModel.cpp index 273ae59f49a0fbd6f64bf562b16800db3234a4df..28fca2c72af565a15fc7f2217158bf330724e45c 100644 --- a/source/xcsg/EditorModel.cpp +++ b/source/xcsg/EditorModel.cpp @@ -60,7 +60,14 @@ void XMETHODCALLTYPE CEditorModel::getResource(IXResourceModel **ppOut) CMeshBuilder meshBuilder; fora(i, m_aObjects) { - m_aObjects[i]->buildMesh(&meshBuilder); + m_aObjects[i]->buildMesh(&meshBuilder, true); + } + + meshBuilder.allocArrays(); + + fora(i, m_aObjects) + { + m_aObjects[i]->buildMesh(&meshBuilder, false); m_aObjects[i]->buildPhysbox(pResource); } diff --git a/source/xcsg/EditorObject.cpp b/source/xcsg/EditorObject.cpp index e168c533cfc99678ed213f1ca686c5f2cfd59edc..47fcd5fd42c778cf08088ea9119d1dd30eb6b4f8 100644 --- a/source/xcsg/EditorObject.cpp +++ b/source/xcsg/EditorObject.cpp @@ -144,7 +144,7 @@ bool XMETHODCALLTYPE CEditorObject::rayTest(const float3 &vStart, const float3 & if(bReturnNearestPoint) { float3 vOut, vMinOut, vNormal, *pNormal = NULL; - if(pNormal) + if(pvNormal) { pNormal = &vNormal; } @@ -691,11 +691,11 @@ void CEditorObject::removeBrush(UINT idx) m_aBrushes.erase(idx); } -void CEditorObject::buildMesh(CMeshBuilder *pBuilder) +void CEditorObject::buildMesh(CMeshBuilder *pBuilder, bool bOnlyComputeCounts) { fora(i, m_aBrushes) { - m_aBrushes[i]->buildMesh(pBuilder); + m_aBrushes[i]->buildMesh(pBuilder, bOnlyComputeCounts); } } diff --git a/source/xcsg/EditorObject.h b/source/xcsg/EditorObject.h index 938a3fb44a0f6f5a1e8dc9628d944015c03df351..365cf5c270ff22c3825f22d3c32552af246cba47 100644 --- a/source/xcsg/EditorObject.h +++ b/source/xcsg/EditorObject.h @@ -103,7 +103,7 @@ public: return(m_pModel); } - void buildMesh(CMeshBuilder *pBuilder); + void buildMesh(CMeshBuilder *pBuilder, bool bOnlyComputeCounts); void buildPhysbox(IXResourceModel *pResource); bool isRemoved()