diff options
| author | Tomas Bzatek <tbzatek@redhat.com> | 2023-12-19 18:44:41 +0100 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@redhat.com> | 2023-12-19 18:44:41 +0100 |
| commit | 6d1c2e642eaf673e9e88ab08eacd5a6873659bcd (patch) | |
| tree | 543869d8721acb0fc022c27bef9caa7b4c00b8de | |
| parent | 6fc6478ac4af06a080731b92231303b2a81921b6 (diff) | |
| download | tuxcmd-modules-6d1c2e642eaf673e9e88ab08eacd5a6873659bcd.tar.xz | |
unrar: Update unrar sources to v6.2.12
Unmodified unrarsrc-6.2.12 sources.
169 files changed, 23138 insertions, 15692 deletions
diff --git a/unrar/unrar/UnRAR.vcxproj b/unrar/unrar/UnRAR.vcxproj new file mode 100644 index 0000000..512bcf1 --- /dev/null +++ b/unrar/unrar/UnRAR.vcxproj @@ -0,0 +1,279 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.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>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{95CC809B-03FC-4EDB-BB20-FD07A698C05F}</ProjectGuid>
+ <RootNamespace>UnRAR</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <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>
+ <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>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>build\unrar32\$(Configuration)\</OutDir>
+ <IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>build\unrar64\$(Configuration)\</OutDir>
+ <IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>build\unrar32\$(Configuration)\</OutDir>
+ <IntDir>build\unrar32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>build\unrar64\$(Configuration)\</OutDir>
+ <IntDir>build\unrar64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>false</GenerateManifest>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>UNRAR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="archive.cpp" />
+ <ClCompile Include="arcread.cpp" />
+ <ClCompile Include="blake2s.cpp" />
+ <ClCompile Include="cmddata.cpp" />
+ <ClCompile Include="consio.cpp" />
+ <ClCompile Include="crc.cpp" />
+ <ClCompile Include="crypt.cpp" />
+ <ClCompile Include="encname.cpp" />
+ <ClCompile Include="errhnd.cpp" />
+ <ClCompile Include="extinfo.cpp" />
+ <ClCompile Include="extract.cpp" />
+ <ClCompile Include="filcreat.cpp" />
+ <ClCompile Include="file.cpp" />
+ <ClCompile Include="filefn.cpp" />
+ <ClCompile Include="filestr.cpp" />
+ <ClCompile Include="find.cpp" />
+ <ClCompile Include="getbits.cpp" />
+ <ClCompile Include="global.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="hash.cpp" />
+ <ClCompile Include="headers.cpp" />
+ <ClCompile Include="isnt.cpp" />
+ <ClCompile Include="list.cpp" />
+ <ClCompile Include="match.cpp" />
+ <ClCompile Include="options.cpp" />
+ <ClCompile Include="pathfn.cpp" />
+ <ClCompile Include="qopen.cpp" />
+ <ClCompile Include="rar.cpp" />
+ <ClCompile Include="rarpch.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="rarvm.cpp" />
+ <ClCompile Include="rawread.cpp" />
+ <ClCompile Include="rdwrfn.cpp" />
+ <ClCompile Include="recvol.cpp" />
+ <ClCompile Include="resource.cpp" />
+ <ClCompile Include="rijndael.cpp" />
+ <ClCompile Include="rs.cpp" />
+ <ClCompile Include="rs16.cpp" />
+ <ClCompile Include="scantree.cpp" />
+ <ClCompile Include="secpassword.cpp" />
+ <ClCompile Include="sha1.cpp" />
+ <ClCompile Include="sha256.cpp" />
+ <ClCompile Include="smallfn.cpp" />
+ <ClCompile Include="strfn.cpp" />
+ <ClCompile Include="strlist.cpp" />
+ <ClCompile Include="system.cpp" />
+ <ClCompile Include="threadpool.cpp" />
+ <ClCompile Include="timefn.cpp" />
+ <ClCompile Include="ui.cpp" />
+ <ClCompile Include="unicode.cpp" />
+ <ClCompile Include="unpack.cpp" />
+ <ClCompile Include="volume.cpp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file diff --git a/unrar/unrar/UnRARDll.vcxproj b/unrar/unrar/UnRARDll.vcxproj new file mode 100644 index 0000000..72cecd8 --- /dev/null +++ b/unrar/unrar/UnRARDll.vcxproj @@ -0,0 +1,420 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.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_nocrypt|Win32">
+ <Configuration>release_nocrypt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="release_nocrypt|x64">
+ <Configuration>release_nocrypt</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>
+ <PropertyGroup Label="Globals">
+ <ProjectName>UnRAR</ProjectName>
+ <ProjectGuid>{E815C46C-36C4-499F-BBC2-E772C6B17971}</ProjectGuid>
+ <RootNamespace>UnRAR</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v140_xp</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'" Label="PropertySheets">
+ <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>
+ <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>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.24720.0</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>build\unrardll32\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>build\unrardll64\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>build\unrardll32\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>build\unrardll64\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
+ <OutDir>build\unrardll32\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll32\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
+ <OutDir>build\unrardll64\$(Configuration)\</OutDir>
+ <IntDir>build\unrardll64\$(Configuration)\obj\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <GenerateManifest>true</GenerateManifest>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>Cdecl</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <AdditionalOptions>/SAFESEH %(AdditionalOptions)</AdditionalOptions>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
+ <Midl>
+ <TargetEnvironment>X64</TargetEnvironment>
+ </Midl>
+ <ClCompile>
+ <AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <PreprocessorDefinitions>RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>false</MinimalRebuild>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>rar.hpp</PrecompiledHeaderFile>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CallingConvention>StdCall</CallingConvention>
+ <DisableSpecificWarnings>4007;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ <Link>
+ <OutputFile>$(OutDir)unrar.dll</OutputFile>
+ <ModuleDefinitionFile>dll_nocrypt.def</ModuleDefinitionFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration />
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="archive.cpp" />
+ <ClCompile Include="arcread.cpp" />
+ <ClCompile Include="blake2s.cpp" />
+ <ClCompile Include="cmddata.cpp" />
+ <ClCompile Include="consio.cpp" />
+ <ClCompile Include="crc.cpp" />
+ <ClCompile Include="crypt.cpp" />
+ <ClCompile Include="dll.cpp" />
+ <ClCompile Include="encname.cpp" />
+ <ClCompile Include="errhnd.cpp" />
+ <ClCompile Include="extinfo.cpp" />
+ <ClCompile Include="extract.cpp" />
+ <ClCompile Include="filcreat.cpp" />
+ <ClCompile Include="file.cpp" />
+ <ClCompile Include="filefn.cpp" />
+ <ClCompile Include="filestr.cpp" />
+ <ClCompile Include="find.cpp" />
+ <ClCompile Include="getbits.cpp" />
+ <ClCompile Include="global.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="hash.cpp" />
+ <ClCompile Include="headers.cpp" />
+ <ClCompile Include="isnt.cpp" />
+ <ClCompile Include="match.cpp" />
+ <ClCompile Include="options.cpp" />
+ <ClCompile Include="pathfn.cpp" />
+ <ClCompile Include="qopen.cpp" />
+ <ClCompile Include="rar.cpp" />
+ <ClCompile Include="rarpch.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='release_nocrypt|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="rarvm.cpp" />
+ <ClCompile Include="rawread.cpp" />
+ <ClCompile Include="rdwrfn.cpp" />
+ <ClCompile Include="rijndael.cpp" />
+ <ClCompile Include="rs.cpp" />
+ <ClCompile Include="rs16.cpp" />
+ <ClCompile Include="scantree.cpp" />
+ <ClCompile Include="secpassword.cpp" />
+ <ClCompile Include="sha1.cpp" />
+ <ClCompile Include="sha256.cpp" />
+ <ClCompile Include="smallfn.cpp" />
+ <ClCompile Include="strfn.cpp" />
+ <ClCompile Include="strlist.cpp" />
+ <ClCompile Include="system.cpp" />
+ <ClCompile Include="threadpool.cpp" />
+ <ClCompile Include="timefn.cpp" />
+ <ClCompile Include="ui.cpp" />
+ <ClCompile Include="unicode.cpp" />
+ <ClCompile Include="unpack.cpp" />
+ <ClCompile Include="volume.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="rar.hpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="dll.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file diff --git a/unrar/unrar/acknow.txt b/unrar/unrar/acknow.txt new file mode 100644 index 0000000..ec2c2c7 --- /dev/null +++ b/unrar/unrar/acknow.txt @@ -0,0 +1,59 @@ + ACKNOWLEDGMENTS
+
+* We used "Screaming Fast Galois Field Arithmetic Using Intel
+ SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
+ and Ethan L. Miller to improve Reed-Solomon coding performance.
+ Also we are grateful to Artem Drobanov and Bulat Ziganshin
+ for samples and ideas allowed to make Reed-Solomon coding
+ more efficient.
+
+* RAR4 text compression algorithm is based on Dmitry Shkarin PPMII
+ and Dmitry Subbotin carryless rangecoder public domain source code.
+ You can find it in ftp.elf.stuba.sk/pub/pc/pack.
+
+* RAR encryption includes parts of public domain code
+ from Szymon Stefanek AES and Steve Reid SHA-1 implementations.
+
+* With exception of SFX modules, RAR uses CRC32 function based
+ on Intel Slicing-by-8 algorithm. Original Intel Slicing-by-8 code
+ is available here:
+
+ https://sourceforge.net/projects/slicing-by-8/
+
+ Original Intel Slicing-by-8 code is licensed under BSD License
+ available at http://www.opensource.org/licenses/bsd-license.html
+
+ Copyright (c) 2004-2006 Intel Corporation.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with
+ the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
+ designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
+ and Christian Winnerlein.
+
+* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
+ to significantly improve RAR compression and speed.
diff --git a/unrar/unrar/arccmt.cpp b/unrar/unrar/arccmt.cpp index 6ada2f0..8b7e498 100644 --- a/unrar/unrar/arccmt.cpp +++ b/unrar/unrar/arccmt.cpp @@ -1,57 +1,74 @@ -bool Archive::GetComment(Array<byte> *CmtData,Array<wchar> *CmtDataW) +static bool IsAnsiEscComment(const wchar *Data,size_t Size); + +bool Archive::GetComment(Array<wchar> *CmtData) { if (!MainComment) - return(false); - SaveFilePos SavePos(*this); + return false; + int64 SavePos=Tell(); + bool Success=DoGetComment(CmtData); + Seek(SavePos,SEEK_SET); + return Success; +} + - ushort CmtLength; +bool Archive::DoGetComment(Array<wchar> *CmtData) +{ #ifndef SFX_MODULE - if (OldFormat) + uint CmtLength; + if (Format==RARFMT14) { - Seek(SFXSize+SIZEOF_OLDMHD,SEEK_SET); - CmtLength=GetByte()+(GetByte()<<8); + Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET); + CmtLength=GetByte(); + CmtLength+=(GetByte()<<8); } else #endif { - if (NewMhd.Flags & MHD_COMMENT) + if (MainHead.CommentInHeader) { - Seek(SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD,SEEK_SET); - ReadHeader(); + // Old style (RAR 2.9) archive comment embedded into the main + // archive header. + Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET); + if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT) + return false; } else { - Seek(SFXSize+SIZEOF_MARKHEAD+NewMhd.HeadSize,SEEK_SET); - return(SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData,CmtDataW)!=0); + // Current (RAR 3.0+) version of archive comment. + Seek(GetStartPos(),SEEK_SET); + return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData); } #ifndef SFX_MODULE - if (CommHead.HeadCRC!=HeaderCRC) + // Old style (RAR 2.9) comment header embedded into the main + // archive header. + if (BrokenHeader || CommHead.HeadSize<SIZEOF_COMMHEAD) { - Log(FileName,St(MLogCommHead)); - Alarm(); - return(false); + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; } CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD; #endif } #ifndef SFX_MODULE - if (OldFormat && (OldMhd.Flags & MHD_PACK_COMMENT) || !OldFormat && CommHead.Method!=0x30) + if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30) { - if (!OldFormat && (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER || CommHead.Method > 0x35)) - return(false); + if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35)) + return false; ComprDataIO DataIO; - Unpack Unpack(&DataIO); - Unpack.Init(); DataIO.SetTestMode(true); uint UnpCmtLength; - if (OldFormat) + if (Format==RARFMT14) { -#ifdef NOCRYPT - return(false); +#ifdef RAR_NOCRYPT + return false; #else - UnpCmtLength=GetByte()+(GetByte()<<8); + UnpCmtLength=GetByte(); + UnpCmtLength+=(GetByte()<<8); + if (CmtLength<2) + return false; CmtLength-=2; DataIO.SetCmt13Encryption(); + CommHead.UnpVer=15; #endif } else @@ -59,157 +76,110 @@ bool Archive::GetComment(Array<byte> *CmtData,Array<wchar> *CmtDataW) DataIO.SetFiles(this,NULL); DataIO.EnableShowProgress(false); DataIO.SetPackedSizeToRead(CmtLength); - Unpack.SetDestSize(UnpCmtLength); - Unpack.DoUnpack(CommHead.UnpVer,false); + DataIO.UnpHash.Init(HASH_CRC32,1); + DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet. + + Unpack CmtUnpack(&DataIO); + CmtUnpack.Init(0x10000,false); + CmtUnpack.SetDestSize(UnpCmtLength); + CmtUnpack.DoUnpack(CommHead.UnpVer,false); - if (!OldFormat && ((~DataIO.UnpFileCRC)&0xffff)!=CommHead.CommCRC) + if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC) { - Log(FileName,St(MLogCommBrk)); - Alarm(); - return(false); + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; } else { - unsigned char *UnpData; - uint UnpDataSize; + byte *UnpData; + size_t UnpDataSize; DataIO.GetUnpackedData(&UnpData,&UnpDataSize); - CmtData->Alloc(UnpDataSize); - memcpy(&((*CmtData)[0]),UnpData,UnpDataSize); + if (UnpDataSize>0) + { +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); +#endif + CmtData->Alloc(UnpDataSize+1); + memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar)); + CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size()); + CmtData->Alloc(wcslen(CmtData->Addr(0))); + } } } else { - CmtData->Alloc(CmtLength); - - Read(&((*CmtData)[0]),CmtLength); - if (!OldFormat && CommHead.CommCRC!=(~CRC(0xffffffff,&((*CmtData)[0]),CmtLength)&0xffff)) + if (CmtLength==0) + return false; + Array<byte> CmtRaw(CmtLength); + int ReadSize=Read(&CmtRaw[0],CmtLength); + if (ReadSize>=0 && (uint)ReadSize<CmtLength) // Comment is shorter than declared. { - Log(FileName,St(MLogCommBrk)); - Alarm(); - CmtData->Reset(); - return(false); + CmtLength=ReadSize; + CmtRaw.Alloc(CmtLength); } - } -#endif -#if defined(_WIN_32) && !defined(_WIN_CE) - if (CmtData->Size()>0) - { - int CmtSize=CmtData->Size(); - OemToCharBuff((char *)CmtData->Addr(),(char *)CmtData->Addr(),CmtSize); - if (CmtDataW!=NULL) + if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff)) { - CmtDataW->Alloc(CmtSize+1); - CmtData->Push(0); - CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1); - CmtData->Alloc(CmtSize); - CmtDataW->Alloc(strlenw(CmtDataW->Addr())); + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; } + CmtData->Alloc(CmtLength+1); + CmtRaw.Push(0); +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]); +#endif + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + CmtData->Alloc(wcslen(CmtData->Addr(0))); } #endif - return(CmtData->Size()>0); + return CmtData->Size() > 0; } -int Archive::ReadCommentData(Array<byte> *CmtData,Array<wchar> *CmtDataW) +bool Archive::ReadCommentData(Array<wchar> *CmtData) { - bool Unicode=SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE; - if (!ReadSubData(CmtData,NULL)) - return(0); - int CmtSize=CmtData->Size(); - if (Unicode) - { - CmtSize/=2; - Array<wchar> DataW(CmtSize+1); - RawToWide(CmtData->Addr(),DataW.Addr(),CmtSize); - DataW[CmtSize]=0; - int DestSize=CmtSize*4; - CmtData->Alloc(DestSize+1); - WideToChar(DataW.Addr(),(char *)CmtData->Addr(),DestSize); - (*CmtData)[DestSize]=0; - CmtSize=strlen((char *)CmtData->Addr()); - CmtData->Alloc(CmtSize); - if (CmtDataW!=NULL) + Array<byte> CmtRaw; + if (!ReadSubData(&CmtRaw,NULL,false)) + return false; + size_t CmtSize=CmtRaw.Size(); + CmtRaw.Push(0); + CmtData->Alloc(CmtSize+1); + if (Format==RARFMT50) + UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + else + if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0) { - *CmtDataW=DataW; - CmtDataW->Alloc(CmtSize); + RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2); + (*CmtData)[CmtSize/2]=0; + } - } - else - if (CmtDataW!=NULL) + else { - CmtData->Push(0); - CmtDataW->Alloc(CmtSize+1); - CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1); - CmtData->Alloc(CmtSize); - CmtDataW->Alloc(strlenw(CmtDataW->Addr())); + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); } - return(CmtSize); + CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length. + return true; } void Archive::ViewComment() { -#ifndef GUI if (Cmd->DisableComment) return; - Array<byte> CmtBuf; - if (GetComment(&CmtBuf,NULL)) + Array<wchar> CmtBuf; + if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments. { - int CmtSize=CmtBuf.Size(); - char *ChPtr=(char *)memchr(&CmtBuf[0],0x1A,CmtSize); + size_t CmtSize=CmtBuf.Size(); + wchar *ChPtr=wcschr(&CmtBuf[0],0x1A); if (ChPtr!=NULL) - CmtSize=ChPtr-(char *)&CmtBuf[0]; - mprintf("\n"); - OutComment((char *)&CmtBuf[0],CmtSize); + CmtSize=ChPtr-&CmtBuf[0]; + mprintf(L"\n"); + OutComment(&CmtBuf[0],CmtSize); } -#endif } -#ifndef SFX_MODULE -void Archive::ViewFileComment() -{ - if (!(NewLhd.Flags & LHD_COMMENT) || Cmd->DisableComment || OldFormat) - return; -#ifndef GUI - mprintf(St(MFileComment)); -#endif - const int MaxSize=0x8000; - Array<char> CmtBuf(MaxSize); - SaveFilePos SavePos(*this); - Seek(CurBlockPos+SIZEOF_NEWLHD+NewLhd.NameSize,SEEK_SET); - Int64 SaveCurBlockPos=CurBlockPos; - Int64 SaveNextBlockPos=NextBlockPos; - - int Size=ReadHeader(); - - CurBlockPos=SaveCurBlockPos; - NextBlockPos=SaveNextBlockPos; - - if (Size<7 || CommHead.HeadType!=COMM_HEAD) - return; - if (CommHead.HeadCRC!=HeaderCRC) - { -#ifndef GUI - Log(FileName,St(MLogCommHead)); -#endif - return; - } - if (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER || - CommHead.Method > 0x30 || CommHead.UnpSize > MaxSize) - return; - Read(&CmtBuf[0],CommHead.UnpSize); - if (CommHead.CommCRC!=((~CRC(0xffffffff,&CmtBuf[0],CommHead.UnpSize)&0xffff))) - { - Log(FileName,St(MLogBrokFCmt)); - } - else - { - OutComment(&CmtBuf[0],CommHead.UnpSize); -#ifndef GUI - mprintf("\n"); -#endif - } -} -#endif diff --git a/unrar/unrar/archive.cpp b/unrar/unrar/archive.cpp index 3460f99..25f0c3b 100644 --- a/unrar/unrar/archive.cpp +++ b/unrar/unrar/archive.cpp @@ -1,143 +1,161 @@ #include "rar.hpp" -#ifndef SHELL_EXT #include "arccmt.cpp" -#endif -Archive::Archive(RAROptions *InitCmd) +Archive::Archive(CommandData *InitCmd) { - Cmd=InitCmd==NULL ? &DummyCmd:InitCmd; + Cmd=NULL; // Just in case we'll have an exception in 'new' below. + + DummyCmd=(InitCmd==NULL); + Cmd=DummyCmd ? (new CommandData):InitCmd; + OpenShared=Cmd->OpenShared; - OldFormat=false; + Format=RARFMT_NONE; Solid=false; Volume=false; MainComment=false; Locked=false; Signed=false; - NotFirstVolume=false; + FirstVolume=false; + NewNumbering=false; SFXSize=0; LatestTime.Reset(); Protected=false; Encrypted=false; - BrokenFileHeader=false; + FailedHeaderDecryption=false; + BrokenHeader=false; LastReadBlock=0; CurBlockPos=0; NextBlockPos=0; - RecoveryPos=SIZEOF_MARKHEAD; - RecoverySectors=-1; - memset(&NewMhd,0,sizeof(NewMhd)); - NewMhd.HeadType=MAIN_HEAD; - NewMhd.HeadSize=SIZEOF_NEWMHD; - HeaderCRC=0; + MainHead.Reset(); + CryptHead={}; + EndArcHead.Reset(); + + VolNumber=0; VolWrite=0; AddingFilesSize=0; AddingHeadersSize=0; -#if !defined(SHELL_EXT) && !defined(NOCRYPT) - *HeadersSalt=0; - *SubDataSalt=0; -#endif *FirstVolumeName=0; - *FirstVolumeNameW=0; Splitting=false; NewArchive=false; SilentOpen=false; +#ifdef USE_QOPEN + ProhibitQOpen=false; +#endif + +} + + +Archive::~Archive() +{ + if (DummyCmd) + delete Cmd; } -#ifndef SHELL_EXT void Archive::CheckArc(bool EnableBroken) { if (!IsArchive(EnableBroken)) { - Log(FileName,St(MBadArc),FileName); - ErrHandler.Exit(FATAL_ERROR); + // If FailedHeaderDecryption is set, we already reported that archive + // password is incorrect. + if (!FailedHeaderDecryption) + uiMsg(UIERROR_BADARCHIVE,FileName); + ErrHandler.Exit(RARX_FATAL); } } -#endif -#if !defined(SHELL_EXT) && !defined(SFX_MODULE) -void Archive::CheckOpen(char *Name,wchar *NameW) +#if !defined(SFX_MODULE) +void Archive::CheckOpen(const wchar *Name) { - TOpen(Name,NameW); + TOpen(Name); CheckArc(false); } #endif -bool Archive::WCheckOpen(char *Name,wchar *NameW) +bool Archive::WCheckOpen(const wchar *Name) { - if (!WOpen(Name,NameW)) - return(false); + if (!WOpen(Name)) + return false; if (!IsArchive(false)) { -#ifndef SHELL_EXT - Log(FileName,St(MNotRAR),FileName); -#endif + uiMsg(UIERROR_BADARCHIVE,FileName); Close(); - return(false); + return false; } - return(true); + return true; } -bool Archive::IsSignature(byte *D) +RARFORMAT Archive::IsSignature(const byte *D,size_t Size) { - bool Valid=false; - if (D[0]==0x52) + RARFORMAT Type=RARFMT_NONE; + if (Size>=1 && D[0]==0x52) #ifndef SFX_MODULE - if (D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) - { - OldFormat=true; - Valid=true; - } + if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) + Type=RARFMT14; else #endif - if (D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07 && D[6]==0x00) + if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07) { - OldFormat=false; - Valid=true; + // We check the last signature byte, so we can return a sensible + // warning in case we'll want to change the archive format + // sometimes in the future. + if (D[6]==0) + Type=RARFMT15; + else + if (D[6]==1) + Type=RARFMT50; + else + if (D[6]>1 && D[6]<5) + Type=RARFMT_FUTURE; } - return(Valid); + return Type; } bool Archive::IsArchive(bool EnableBroken) { Encrypted=false; + BrokenHeader=false; // Might be left from previous volume. + #ifndef SFX_MODULE if (IsDevice()) { -#ifndef SHELL_EXT - Log(FileName,St(MInvalidName),FileName); -#endif - return(false); + uiMsg(UIERROR_INVALIDNAME,FileName,FileName); + return false; } #endif - if (Read(MarkHead.Mark,SIZEOF_MARKHEAD)!=SIZEOF_MARKHEAD) - return(false); + if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3) + return false; SFXSize=0; - if (IsSignature(MarkHead.Mark)) + + RARFORMAT Type; + if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE) { - if (OldFormat) - Seek(0,SEEK_SET); + Format=Type; + if (Format==RARFMT14) + Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET); } else { Array<char> Buffer(MAXSFXSIZE); - long CurPos=int64to32(Tell()); + long CurPos=(long)Tell(); int ReadSize=Read(&Buffer[0],Buffer.Size()-16); for (int I=0;I<ReadSize;I++) - if (Buffer[I]==0x52 && IsSignature((byte *)&Buffer[I])) + if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE) { - if (OldFormat && I>0 && CurPos<28 && ReadSize>31) + Format=Type; + if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31) { char *D=&Buffer[28-CurPos]; if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58) @@ -145,98 +163,111 @@ bool Archive::IsArchive(bool EnableBroken) } SFXSize=CurPos+I; Seek(SFXSize,SEEK_SET); - if (!OldFormat) - Read(MarkHead.Mark,SIZEOF_MARKHEAD); + if (Format==RARFMT15 || Format==RARFMT50) + Read(MarkHead.Mark,SIZEOF_MARKHEAD3); break; } if (SFXSize==0) - return(false); + return false; } - ReadHeader(); - SeekToNext(); -#ifndef SFX_MODULE - if (OldFormat) + if (Format==RARFMT_FUTURE) + { + uiMsg(UIERROR_NEWRARFORMAT,FileName); + return false; + } + if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer. { - NewMhd.Flags=OldMhd.Flags & 0x3f; - NewMhd.HeadSize=OldMhd.HeadSize; + if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0) + return false; + MarkHead.HeadSize=SIZEOF_MARKHEAD5; } else + MarkHead.HeadSize=SIZEOF_MARKHEAD3; + +#ifdef RARDLL + // If callback function is not set, we cannot get the password, + // so we skip the initial header processing for encrypted header archive. + // It leads to skipped archive comment, but the rest of archive data + // is processed correctly. + if (Cmd->Callback==NULL) + SilentOpen=true; #endif + + bool HeadersLeft; // Any headers left to read. + bool StartFound=false; // Main or encryption headers found. + // Skip the archive encryption header if any and read the main header. + while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang. { - if (HeaderCRC!=NewMhd.HeadCRC) - { -#ifndef SHELL_EXT - Log(FileName,St(MLogMainHead)); -#endif - Alarm(); - if (!EnableBroken) - return(false); - } + SeekToNext(); + + HEADER_TYPE Type=GetHeaderType(); + // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to + // avoid the password prompt. + StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT; + if (StartFound) + break; } - Volume=(NewMhd.Flags & MHD_VOLUME); - Solid=(NewMhd.Flags & MHD_SOLID)!=0; - MainComment=(NewMhd.Flags & MHD_COMMENT)!=0; - Locked=(NewMhd.Flags & MHD_LOCK)!=0; - Signed=(NewMhd.PosAV!=0); - Protected=(NewMhd.Flags & MHD_PROTECT)!=0; - Encrypted=(NewMhd.Flags & MHD_PASSWORD)!=0; - - if (NewMhd.EncryptVer>UNP_VER) + + + // We should not do it for EnableBroken or we'll get 'not RAR archive' + // messages when extracting encrypted archives with wrong password. + if (FailedHeaderDecryption && !EnableBroken) + return false; + + if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing. { -#ifdef RARDLL - Cmd->DllError=ERAR_UNKNOWN_FORMAT; -#else - ErrHandler.SetErrorCode(WARNING); - #if !defined(SILENT) && !defined(SFX_MODULE) - Log(FileName,St(MUnknownMeth),FileName); - Log(FileName,St(MVerRequired),NewMhd.EncryptVer/10,NewMhd.EncryptVer%10); - #endif -#endif - return(false); + if (!FailedHeaderDecryption) // If not reported a wrong password already. + uiMsg(UIERROR_MHEADERBROKEN,FileName); + if (!EnableBroken) + return false; } -#ifdef RARDLL - SilentOpen=true; -#endif - //if not encrypted, we'll check it below - NotFirstVolume=Encrypted && (NewMhd.Flags & MHD_FIRSTVOLUME)==0; + MainComment=MainHead.CommentInHeader; - if (!SilentOpen || !Encrypted) + // If we process non-encrypted archive or can request a password, + // we set 'first volume' flag based on file attributes below. + // It is necessary for RAR 2.x archives, which did not have 'first volume' + // flag in main header. Also for all RAR formats we need to scan until + // first file header to set "comment" flag when reading service header. + // Unless we are in silent mode, we need to know about presence of comment + // immediately after IsArchive call. + if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable()) { - SaveFilePos SavePos(*this); - Int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + int64 SavePos=Tell(); + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; - NotFirstVolume=false; - while (ReadHeader()) + while (ReadHeader()!=0) { - int HeaderType=GetHeaderType(); - if (HeaderType==NEWSUB_HEAD) + HEADER_TYPE HeaderType=GetHeaderType(); + if (HeaderType==HEAD_SERVICE) { - if (SubHead.CmpName(SUBHEAD_TYPE_CMT)) - MainComment=true; - if ((SubHead.Flags & LHD_SPLIT_BEFORE) || - Volume && (NewMhd.Flags & MHD_FIRSTVOLUME)==0) - NotFirstVolume=true; + // If we have a split service headers, it surely indicates non-first + // volume. But not split service header does not guarantee the first + // volume, because we can have split file after non-split archive + // comment. So we do not quit from loop here. + FirstVolume=Volume && !SubHead.SplitBefore; } else - { - if (HeaderType==FILE_HEAD && ((NewLhd.Flags & LHD_SPLIT_BEFORE)!=0 || - Volume && NewLhd.UnpVer>=29 && (NewMhd.Flags & MHD_FIRSTVOLUME)==0)) - NotFirstVolume=true; - break; - } + if (HeaderType==HEAD_FILE) + { + FirstVolume=Volume && !FileHead.SplitBefore; + break; + } + else + if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header. + break; SeekToNext(); } CurBlockPos=SaveCurBlockPos; NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + Seek(SavePos,SEEK_SET); } - if (!Volume || !NotFirstVolume) - { - strcpy(FirstVolumeName,FileName); - strcpyw(FirstVolumeNameW,FileNameW); - } + if (!Volume || FirstVolume) + wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName)); - return(true); + return true; } @@ -248,20 +279,60 @@ void Archive::SeekToNext() } -#ifndef SFX_MODULE -int Archive::GetRecoverySize(bool Required) + + + + +// Calculate the block size including encryption fields and padding if any. +uint Archive::FullHeaderSize(size_t Size) { - if (!Protected) - return(0); - if (RecoverySectors!=-1 || !Required) - return(RecoverySectors); - SaveFilePos SavePos(*this); - Seek(SFXSize,SEEK_SET); - SearchSubBlock(SUBHEAD_TYPE_RR); - return(RecoverySectors); + if (Encrypted) + { + Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size. + if (Format == RARFMT50) + Size += SIZE_INITV; + else + Size += SIZE_SALT30; + } + return uint(Size); } -#endif +#ifdef USE_QOPEN +bool Archive::Open(const wchar *Name,uint Mode) +{ + // Important if we reuse Archive object and it has virtual QOpen + // file position not matching real. For example, for 'l -v volname'. + QOpen.Unload(); + + return File::Open(Name,Mode); +} + + +int Archive::Read(void *Data,size_t Size) +{ + size_t Result; + if (QOpen.Read(Data,Size,Result)) + return (int)Result; + return File::Read(Data,Size); +} + + +void Archive::Seek(int64 Offset,int Method) +{ + if (!QOpen.Seek(Offset,Method)) + File::Seek(Offset,Method); +} + + +int64 Archive::Tell() +{ + int64 QPos; + if (QOpen.Tell(&QPos)) + return QPos; + return File::Tell(); +} +#endif + diff --git a/unrar/unrar/archive.hpp b/unrar/unrar/archive.hpp index a278aff..c0019ae 100644 --- a/unrar/unrar/archive.hpp +++ b/unrar/unrar/archive.hpp @@ -1,125 +1,147 @@ #ifndef _RAR_ARCHIVE_ #define _RAR_ARCHIVE_ -class Pack; +class PPack; +class RawRead; +class RawWrite; -enum {EN_LOCK=1,EN_VOL=2,EN_FIRSTVOL=4}; +enum NOMODIFY_FLAGS +{ + NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4 +}; + +enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE}; + +enum ADDSUBDATA_FLAGS +{ + ASDF_SPLIT = 1, // Allow to split archive just before header if necessary. + ASDF_COMPRESS = 2, // Allow to compress data following subheader. + ASDF_CRYPT = 4, // Encrypt data after subheader if password is set. + ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode. +}; + +// RAR5 headers must not exceed 2 MB. +#define MAX_HEADER_SIZE_RAR5 0x200000 class Archive:public File { private: - bool IsSignature(byte *D); void UpdateLatestTime(FileHeader *CurBlock); - void ConvertNameCase(char *Name); void ConvertNameCase(wchar *Name); - void ConvertUnknownHeader(); - int ReadOldHeader(); - -#if !defined(SHELL_EXT) && !defined(NOCRYPT) + void ConvertFileHeader(FileHeader *hd); + size_t ReadHeader14(); + size_t ReadHeader15(); + size_t ReadHeader50(); + void ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb); + void RequestArcPassword(RarCheckPassword *SelPwd); + void UnexpEndArcMsg(); + void BrokenHeaderMsg(); + void UnkEncVerMsg(const wchar *Name,const wchar *Info); + bool DoGetComment(Array<wchar> *CmtData); + bool ReadCommentData(Array<wchar> *CmtData); + +#if !defined(RAR_NOCRYPT) CryptData HeadersCrypt; - byte HeadersSalt[SALT_SIZE]; #endif -#ifndef SHELL_EXT ComprDataIO SubDataIO; - byte SubDataSalt[SALT_SIZE]; -#endif - RAROptions *Cmd,DummyCmd; + bool DummyCmd; + CommandData *Cmd; - MarkHeader MarkHead; - OldMainHeader OldMhd; - - int RecoverySectors; - Int64 RecoveryPos; RarTime LatestTime; int LastReadBlock; - int CurHeaderType; + HEADER_TYPE CurHeaderType; bool SilentOpen; +#ifdef USE_QOPEN + QuickOpen QOpen; + bool ProhibitQOpen; +#endif public: - Archive(RAROptions *InitCmd=NULL); + Archive(CommandData *InitCmd=NULL); + ~Archive(); + static RARFORMAT IsSignature(const byte *D,size_t Size); bool IsArchive(bool EnableBroken); - int SearchBlock(int BlockType); - int SearchSubBlock(const char *Type); - int ReadBlock(int BlockType); - void WriteBlock(int BlockType,BaseBlock *wb=NULL); - int PrepareNamesToWrite(char *Name,wchar *NameW,char *DestName,byte *DestNameW); - void SetLhdSize(); - int ReadHeader(); + size_t SearchBlock(HEADER_TYPE HeaderType); + size_t SearchSubBlock(const wchar *Type); + size_t SearchRR(); + size_t ReadHeader(); void CheckArc(bool EnableBroken); - void CheckOpen(char *Name,wchar *NameW=NULL); - bool WCheckOpen(char *Name,wchar *NameW=NULL); - bool TestLock(int Mode); - void MakeTemp(); - void CopyMainHeader(Archive &Src,bool CopySFX=true,char *NameToDisplay=NULL); - bool ProcessToFileHead(Archive &Src,bool LastBlockAdded, - Pack *Pack=NULL,const char *SkipName=NULL); - void TmpToArc(Archive &Src); - void CloseNew(int AdjustRecovery,bool CloseVolume); - void WriteEndBlock(bool CloseVolume); - void CopyFileRecord(Archive &Src); - void CopyArchiveData(Archive &Src); - bool GetComment(Array<byte> *CmtData,Array<wchar> *CmtDataW); + void CheckOpen(const wchar *Name); + bool WCheckOpen(const wchar *Name); + bool GetComment(Array<wchar> *CmtData); void ViewComment(); - void ViewFileComment(); void SetLatestTime(RarTime *NewTime); void SeekToNext(); bool CheckAccess(); bool IsArcDir(); - bool IsArcLabel(); void ConvertAttributes(); - int GetRecoverySize(bool Required); - void VolSubtractHeaderSize(int SubSize); - void AddSubData(byte *SrcData,int DataSize,File *SrcFile,char *Name,bool AllowSplit); - bool ReadSubData(Array<byte> *UnpData,File *DestFile); - int GetHeaderType() {return(CurHeaderType);}; - int ReadCommentData(Array<byte> *CmtData,Array<wchar> *CmtDataW); - void WriteCommentData(byte *Data,int DataSize,bool FileComment); - RAROptions* GetRAROptions() {return(Cmd);} + void VolSubtractHeaderSize(size_t SubSize); + uint FullHeaderSize(size_t Size); + int64 GetStartPos(); + void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile, + const wchar *Name,uint Flags); + bool ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode); + HEADER_TYPE GetHeaderType() {return CurHeaderType;} + CommandData* GetCommandData() {return Cmd;} void SetSilentOpen(bool Mode) {SilentOpen=Mode;} +#if 0 + void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); +#endif +#ifdef USE_QOPEN + bool Open(const wchar *Name,uint Mode=FMF_READ); + int Read(void *Data,size_t Size); + void Seek(int64 Offset,int Method); + int64 Tell(); + void QOpenUnload() {QOpen.Unload();} + void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;} +#endif BaseBlock ShortBlock; - MainHeader NewMhd; - FileHeader NewLhd; + MarkHeader MarkHead; + MainHeader MainHead; + CryptHeader CryptHead; + FileHeader FileHead; EndArcHeader EndArcHead; SubBlockHeader SubBlockHead; FileHeader SubHead; CommentHeader CommHead; ProtectHeader ProtectHead; - AVHeader AVHead; - SignHeader SignHead; - UnixOwnersHeader UOHead; - MacFInfoHeader MACHead; EAHeader EAHead; StreamHeader StreamHead; - Int64 CurBlockPos; - Int64 NextBlockPos; + int64 CurBlockPos; + int64 NextBlockPos; - bool OldFormat; + RARFORMAT Format; bool Solid; bool Volume; bool MainComment; bool Locked; bool Signed; - bool NotFirstVolume; + bool FirstVolume; + bool NewNumbering; bool Protected; bool Encrypted; - uint SFXSize; - bool BrokenFileHeader; + size_t SFXSize; + bool BrokenHeader; + bool FailedHeaderDecryption; - bool Splitting; +#if !defined(RAR_NOCRYPT) + byte ArcSalt[SIZE_SALT50]; +#endif - ushort HeaderCRC; + bool Splitting; - Int64 VolWrite; - Int64 AddingFilesSize; - uint AddingHeadersSize; + uint VolNumber; + int64 VolWrite; + uint64 AddingFilesSize; + uint64 AddingHeadersSize; bool NewArchive; - char FirstVolumeName[NM]; - wchar FirstVolumeNameW[NM]; + wchar FirstVolumeName[NM]; }; + #endif diff --git a/unrar/unrar/arcread.cpp b/unrar/unrar/arcread.cpp index 8dd03fa..6b7dd98 100644 --- a/unrar/unrar/arcread.cpp +++ b/unrar/unrar/arcread.cpp @@ -1,71 +1,162 @@ #include "rar.hpp" -int Archive::SearchBlock(int BlockType) +size_t Archive::ReadHeader() { - int Size,Count=0; + // Once we failed to decrypt an encrypted block, there is no reason to + // attempt to do it further. We'll never be successful and only generate + // endless errors. + if (FailedHeaderDecryption) + return 0; + + CurBlockPos=Tell(); + + // Other developers asked us to initialize it to suppress "may be used + // uninitialized" warning in code below in some compilers. + size_t ReadSize=0; + + switch(Format) + { +#ifndef SFX_MODULE + case RARFMT14: + ReadSize=ReadHeader14(); + break; +#endif + case RARFMT15: + ReadSize=ReadHeader15(); + break; + case RARFMT50: + ReadSize=ReadHeader50(); + break; + } + + // It is important to check ReadSize>0 here, because it is normal + // for RAR2 and RAR3 archives without end of archive block to have + // NextBlockPos==CurBlockPos after the end of archive has reached. + if (ReadSize>0 && NextBlockPos<=CurBlockPos) + { + BrokenHeaderMsg(); + ReadSize=0; + } + + if (ReadSize==0) + CurHeaderType=HEAD_UNKNOWN; + + return ReadSize; +} + + +size_t Archive::SearchBlock(HEADER_TYPE HeaderType) +{ + size_t Size,Count=0; while ((Size=ReadHeader())!=0 && - (BlockType==ENDARC_HEAD || GetHeaderType()!=ENDARC_HEAD)) + (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC)) { if ((++Count & 127)==0) Wait(); - if (GetHeaderType()==BlockType) - return(Size); + if (GetHeaderType()==HeaderType) + return Size; SeekToNext(); } - return(0); + return 0; } -int Archive::SearchSubBlock(const char *Type) +size_t Archive::SearchSubBlock(const wchar *Type) { - int Size; - while ((Size=ReadHeader())!=0 && GetHeaderType()!=ENDARC_HEAD) + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC) { - if (GetHeaderType()==NEWSUB_HEAD && SubHead.CmpName(Type)) - return(Size); + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type)) + return Size; SeekToNext(); } - return(0); + return 0; } -int Archive::ReadHeader() +size_t Archive::SearchRR() { - CurBlockPos=Tell(); + // If locator extra field is available for recovery record, let's utilize it. + if (MainHead.Locator && MainHead.RROffset!=0) + { + uint64 CurPos=Tell(); + Seek(MainHead.RROffset,SEEK_SET); + size_t Size=ReadHeader(); + if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR)) + return Size; + Seek(CurPos,SEEK_SET); + } + // Otherwise scan the entire archive to find the recovery record. + return SearchSubBlock(SUBHEAD_TYPE_RR); +} -#ifndef SFX_MODULE - if (OldFormat) - return(ReadOldHeader()); -#endif +void Archive::UnexpEndArcMsg() +{ + int64 ArcSize=FileLength(); + + // If block positions are equal to file size, this is not an error. + // It can happen when we reached the end of older RAR 1.5 archive, + // which did not have the end of archive block. + // We can't replace this check by checking that read size is exactly 0 + // in the beginning of file header, because in this case the read position + // still can be beyond the end of archive. + if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) + { + uiMsg(UIERROR_UNEXPEOF,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + } +} + + +void Archive::BrokenHeaderMsg() +{ + uiMsg(UIERROR_HEADERBROKEN,FileName); + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); +} + + +void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info) +{ + uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info); + ErrHandler.SetErrorCode(RARX_WARNING); +} + + +// Return f in case of signed integer overflow or negative parameters +// or v1+v2 otherwise. We use it for file offsets, which are signed +// for compatibility with off_t in POSIX file functions and third party code. +// Signed integer overflow is the undefined behavior according to +// C++ standard and it causes fuzzers to complain. +inline int64 SafeAdd(int64 v1,int64 v2,int64 f) +{ + return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f; +} + + +size_t Archive::ReadHeader15() +{ RawRead Raw(this); - bool Decrypt=Encrypted && CurBlockPos>=SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD; + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3; if (Decrypt) { -#if defined(SHELL_EXT) || defined(NOCRYPT) - return(0); +#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. + return 0; #else - if (Read(HeadersSalt,SALT_SIZE)!=SALT_SIZE) - return(0); - if (*Cmd->Password==0) -#ifdef RARDLL - if (Cmd->Callback==NULL || - Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1) - { - Close(); - ErrHandler.Exit(USER_BREAK); - } + RequestArcPassword(NULL); -#else - if (!GetPassword(PASSWORD_ARCHIVE,FileName,Cmd->Password,sizeof(Cmd->Password))) - { - Close(); - ErrHandler.Exit(USER_BREAK); - } -#endif - HeadersCrypt.SetCryptKeys(Cmd->Password,HeadersSalt,false,false,NewMhd.EncryptVer>=36); + byte Salt[SIZE_SALT30]; + if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) + { + UnexpEndArcMsg(); + return 0; + } + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL); Raw.SetCrypt(&HeadersCrypt); #endif } @@ -73,169 +164,244 @@ int Archive::ReadHeader() Raw.Read(SIZEOF_SHORTBLOCKHEAD); if (Raw.Size()==0) { - Int64 ArcSize=FileLength(); - if (CurBlockPos>ArcSize || NextBlockPos>ArcSize) - { - #ifndef SHELL_EXT - Log(FileName,St(MLogUnexpEOF)); - #endif - ErrHandler.SetErrorCode(WARNING); - } - return(0); + UnexpEndArcMsg(); + return 0; } - Raw.Get(ShortBlock.HeadCRC); - byte HeadType; - Raw.Get(HeadType); - ShortBlock.HeadType=(HEADER_TYPE)HeadType; - Raw.Get(ShortBlock.Flags); - Raw.Get(ShortBlock.HeadSize); + ShortBlock.HeadCRC=Raw.Get2(); + + ShortBlock.Reset(); + + uint HeaderType=Raw.Get1(); + ShortBlock.Flags=Raw.Get2(); + ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0; + ShortBlock.HeadSize=Raw.Get2(); + + ShortBlock.HeaderType=(HEADER_TYPE)HeaderType; if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD) { -#ifndef SHELL_EXT - Log(FileName,St(MLogFileHead),"???"); -#endif - BrokenFileHeader=true; - ErrHandler.SetErrorCode(CRC_ERROR); - return(0); + BrokenHeaderMsg(); + return 0; } - if (ShortBlock.HeadType==COMM_HEAD) + // For simpler further processing we map header types common + // for RAR 1.5 and 5.0 formats to RAR 5.0 values. It does not include + // header types specific for RAR 1.5 - 4.x only. + switch(ShortBlock.HeaderType) + { + case HEAD3_MAIN: ShortBlock.HeaderType=HEAD_MAIN; break; + case HEAD3_FILE: ShortBlock.HeaderType=HEAD_FILE; break; + case HEAD3_SERVICE: ShortBlock.HeaderType=HEAD_SERVICE; break; + case HEAD3_ENDARC: ShortBlock.HeaderType=HEAD_ENDARC; break; + } + CurHeaderType=ShortBlock.HeaderType; + + if (ShortBlock.HeaderType==HEAD3_CMT) + { + // Old style (up to RAR 2.9) comment header embedded into main + // or file header. We must not read the entire ShortBlock.HeadSize here + // to not break the comment processing logic later. Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD); + } else - if (ShortBlock.HeadType==MAIN_HEAD && (ShortBlock.Flags & MHD_COMMENT)!=0) - Raw.Read(SIZEOF_NEWMHD-SIZEOF_SHORTBLOCKHEAD); + if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0) + { + // Old style (up to RAR 2.9) main archive comment embedded into + // the main archive header found. While we can read the entire + // ShortBlock.HeadSize here and remove this part of "if", it would be + // waste of memory, because we'll read and process this comment data + // in other function anyway and we do not need them here now. + Raw.Read(SIZEOF_MAINHEAD3-SIZEOF_SHORTBLOCKHEAD); + } else Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD); - NextBlockPos=CurBlockPos+ShortBlock.HeadSize; + NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); - switch(ShortBlock.HeadType) + switch(ShortBlock.HeaderType) { - case MAIN_HEAD: - *(BaseBlock *)&NewMhd=ShortBlock; - Raw.Get(NewMhd.HighPosAV); - Raw.Get(NewMhd.PosAV); - if (NewMhd.Flags & MHD_ENCRYPTVER) - Raw.Get(NewMhd.EncryptVer); - break; - case ENDARC_HEAD: - *(BaseBlock *)&EndArcHead=ShortBlock; - if (EndArcHead.Flags & EARC_DATACRC) - Raw.Get(EndArcHead.ArcDataCRC); - if (EndArcHead.Flags & EARC_VOLNUMBER) - Raw.Get(EndArcHead.VolNumber); + case HEAD_MAIN: + MainHead.Reset(); + *(BaseBlock *)&MainHead=ShortBlock; + MainHead.HighPosAV=Raw.Get2(); + MainHead.PosAV=Raw.Get4(); + + Volume=(MainHead.Flags & MHD_VOLUME)!=0; + Solid=(MainHead.Flags & MHD_SOLID)!=0; + Locked=(MainHead.Flags & MHD_LOCK)!=0; + Protected=(MainHead.Flags & MHD_PROTECT)!=0; + Encrypted=(MainHead.Flags & MHD_PASSWORD)!=0; + Signed=MainHead.PosAV!=0 || MainHead.HighPosAV!=0; + MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0; + + // Only for encrypted 3.0+ archives. 2.x archives did not have this + // flag, so for non-encrypted archives, we'll set it later based on + // file attributes. + FirstVolume=(MainHead.Flags & MHD_FIRSTVOLUME)!=0; + + NewNumbering=(MainHead.Flags & MHD_NEWNUMBERING)!=0; break; - case FILE_HEAD: - case NEWSUB_HEAD: + case HEAD_FILE: + case HEAD_SERVICE: { - FileHeader *hd=ShortBlock.HeadType==FILE_HEAD ? &NewLhd:&SubHead; + bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; + FileHeader *hd=FileBlock ? &FileHead:&SubHead; + hd->Reset(); + *(BaseBlock *)hd=ShortBlock; - Raw.Get(hd->PackSize); - Raw.Get(hd->UnpSize); - Raw.Get(hd->HostOS); - Raw.Get(hd->FileCRC); - Raw.Get(hd->FileTime); - Raw.Get(hd->UnpVer); - Raw.Get(hd->Method); - Raw.Get(hd->NameSize); - Raw.Get(hd->FileAttr); - if (hd->Flags & LHD_LARGE) + + hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0; + hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0; + hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0; + hd->SaltSet=(hd->Flags & LHD_SALT)!=0; + + // RAR versions earlier than 2.0 do not set the solid flag + // in file header. They use only a global solid archive flag. + hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0; + + hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; + hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); + hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0; + hd->Version=(hd->Flags & LHD_VERSION)!=0; + + hd->DataSize=Raw.Get4(); + uint LowUnpSize=Raw.Get4(); + hd->HostOS=Raw.Get1(); + + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + + uint FileTime=Raw.Get4(); + hd->UnpVer=Raw.Get1(); + + hd->Method=Raw.Get1()-0x30; + size_t NameSize=Raw.Get2(); + hd->FileAttr=Raw.Get4(); + + // RAR15 did not use the special dictionary size to mark dirs. + if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0) + hd->Dir=true; + + hd->CryptMethod=CRYPT_NONE; + if (hd->Encrypted) + switch(hd->UnpVer) + { + case 13: hd->CryptMethod=CRYPT_RAR13; break; + case 15: hd->CryptMethod=CRYPT_RAR15; break; + case 20: + case 26: hd->CryptMethod=CRYPT_RAR20; break; + default: hd->CryptMethod=CRYPT_RAR30; break; + } + + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOS<HOST_MAX) + hd->HSType=HSYS_WINDOWS; + + hd->RedirType=FSREDIR_NONE; + + // RAR 4.x Unix symlink. + if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000) + { + hd->RedirType=FSREDIR_UNIXSYMLINK; + *hd->RedirName=0; + } + + hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0; + + hd->LargeFile=(hd->Flags & LHD_LARGE)!=0; + + uint HighPackSize,HighUnpSize; + if (hd->LargeFile) { - Raw.Get(hd->HighPackSize); - Raw.Get(hd->HighUnpSize); + HighPackSize=Raw.Get4(); + HighUnpSize=Raw.Get4(); + hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff); } - else + else { - hd->HighPackSize=hd->HighUnpSize=0; - if (hd->UnpSize==0xffffffff) - { - hd->UnpSize=int64to32(INT64MAX); - hd->HighUnpSize=int64to32(INT64MAX>>32); - } + HighPackSize=HighUnpSize=0; + // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates + // that we do not know the unpacked file size and must unpack it + // until we find the end of file marker in compressed data. + hd->UnknownUnpSize=(LowUnpSize==0xffffffff); } - hd->FullPackSize=int32to64(hd->HighPackSize,hd->PackSize); - hd->FullUnpSize=int32to64(hd->HighUnpSize,hd->UnpSize); + hd->PackSize=INT32TO64(HighPackSize,hd->DataSize); + hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize); + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; char FileName[NM*4]; - int NameSize=Min(hd->NameSize,sizeof(FileName)-1); - Raw.Get((byte *)FileName,NameSize); - FileName[NameSize]=0; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; - strncpyz(hd->FileName,FileName,ASIZE(hd->FileName)); - - if (hd->HeadType==NEWSUB_HEAD) + if (FileBlock) { - int DataSize=hd->HeadSize-hd->NameSize-SIZEOF_NEWLHD; - if (hd->Flags & LHD_SALT) - DataSize-=SALT_SIZE; - if (DataSize>0) + *hd->FileName=0; + if ((hd->Flags & LHD_UNICODE)!=0) { - hd->SubData.Alloc(DataSize); - Raw.Get(&hd->SubData[0],DataSize); - if (hd->CmpName(SUBHEAD_TYPE_RR)) - { - byte *D=&hd->SubData[8]; - RecoverySectors=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24); - } + EncodeFileName NameCoder; + size_t Length=strlen(FileName); + Length++; + if (ReadNameSize>Length) + NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length, + ReadNameSize-Length,hd->FileName, + ASIZE(hd->FileName)); } + + if (*hd->FileName==0) + ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM); + +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); +#endif + ConvertFileHeader(hd); } else - if (hd->HeadType==FILE_HEAD) + { + CharToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Calculate the size of optional data. + int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3); + if ((hd->Flags & LHD_SALT)!=0) + DataSize-=SIZE_SALT30; + + if (DataSize>0) { - if (hd->Flags & LHD_UNICODE) - { - EncodeFileName NameCoder; - int Length=strlen(FileName); - if (Length==hd->NameSize) - { - UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1); - WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1); - ExtToInt(hd->FileName,hd->FileName); - } - else - { - Length++; - NameCoder.Decode(FileName,(byte *)FileName+Length, - hd->NameSize-Length,hd->FileNameW, - sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])); - } - if (*hd->FileNameW==0) - hd->Flags &= ~LHD_UNICODE; - } - else - *hd->FileNameW=0; -#ifndef SFX_MODULE - ConvertNameCase(hd->FileName); - ConvertNameCase(hd->FileNameW); -#endif - ConvertUnknownHeader(); + // Here we read optional additional fields for subheaders. + // They are stored after the file name and before salt. + hd->SubData.Alloc(DataSize); + Raw.GetB(&hd->SubData[0],DataSize); + } - if (hd->Flags & LHD_SALT) - Raw.Get(hd->Salt,SALT_SIZE); - hd->mtime.SetDos(hd->FileTime); - hd->ctime.Reset(); - hd->atime.Reset(); - hd->arctime.Reset(); - if (hd->Flags & LHD_EXTTIME) + + if (hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + } + if ((hd->Flags & LHD_SALT)!=0) + Raw.GetB(hd->Salt,SIZE_SALT30); + hd->mtime.SetDos(FileTime); + if ((hd->Flags & LHD_EXTTIME)!=0) { - ushort Flags; - Raw.Get(Flags); + ushort Flags=Raw.Get2(); RarTime *tbl[4]; - tbl[0]=&NewLhd.mtime; - tbl[1]=&NewLhd.ctime; - tbl[2]=&NewLhd.atime; - tbl[3]=&NewLhd.arctime; + tbl[0]=&FileHead.mtime; + tbl[1]=&FileHead.ctime; + tbl[2]=&FileHead.atime; + tbl[3]=NULL; // Archive time is not used now. for (int I=0;I<4;I++) { RarTime *CurTime=tbl[I]; uint rmode=Flags>>(3-I)*4; - if ((rmode & 8)==0) + if ((rmode & 8)==0 || CurTime==NULL) continue; if (I!=0) { - uint DosTime; - Raw.Get(DosTime); + uint DosTime=Raw.Get4(); CurTime->SetDos(DosTime); } RarLocalTime rlt; @@ -243,108 +409,90 @@ int Archive::ReadHeader() if (rmode & 4) rlt.Second++; rlt.Reminder=0; - int count=rmode&3; - for (int J=0;J<count;J++) + uint count=rmode&3; + for (uint J=0;J<count;J++) { - byte CurByte; - Raw.Get(CurByte); + byte CurByte=Raw.Get1(); rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8)); } + // Convert from 100ns RAR precision to REMINDER_PRECISION. + rlt.Reminder*=RarTime::REMINDER_PRECISION/10000000; CurTime->SetLocal(&rlt); } } - NextBlockPos+=hd->FullPackSize; - bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0; - HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff; + // Set to 0 in case of overflow, so end of ReadHeader cares about it. + NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0); + + bool CRCProcessedOnly=hd->CommentInHeader; + ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); if (hd->HeadCRC!=HeaderCRC) { - if (hd->HeadType==NEWSUB_HEAD) - strcat(hd->FileName,"- ???"); - BrokenFileHeader=true; - ErrHandler.SetErrorCode(WARNING); -#ifndef SHELL_EXT - Log(Archive::FileName,St(MLogFileHead),IntNameToExt(hd->FileName)); - Alarm(); -#endif + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_WARNING); + + // If we have a broken encrypted header, we do not need to display + // the error message here, because it will be displayed for such + // headers later in this function. Also such headers are unlikely + // to have anything sensible in file name field, so it is useless + // to display the file name. + if (!Decrypt) + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); } } break; + case HEAD_ENDARC: + *(BaseBlock *)&EndArcHead=ShortBlock; + EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0; + EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0; + EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0; + EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0; + if (EndArcHead.DataCRC) + EndArcHead.ArcDataCRC=Raw.Get4(); + if (EndArcHead.StoreVolNumber) + VolNumber=EndArcHead.VolNumber=Raw.Get2(); + break; #ifndef SFX_MODULE - case COMM_HEAD: + case HEAD3_CMT: *(BaseBlock *)&CommHead=ShortBlock; - Raw.Get(CommHead.UnpSize); - Raw.Get(CommHead.UnpVer); - Raw.Get(CommHead.Method); - Raw.Get(CommHead.CommCRC); - break; - case SIGN_HEAD: - *(BaseBlock *)&SignHead=ShortBlock; - Raw.Get(SignHead.CreationTime); - Raw.Get(SignHead.ArcNameSize); - Raw.Get(SignHead.UserNameSize); - break; - case AV_HEAD: - *(BaseBlock *)&AVHead=ShortBlock; - Raw.Get(AVHead.UnpVer); - Raw.Get(AVHead.Method); - Raw.Get(AVHead.AVVer); - Raw.Get(AVHead.AVInfoCRC); + CommHead.UnpSize=Raw.Get2(); + CommHead.UnpVer=Raw.Get1(); + CommHead.Method=Raw.Get1(); + CommHead.CommCRC=Raw.Get2(); break; - case PROTECT_HEAD: + case HEAD3_PROTECT: *(BaseBlock *)&ProtectHead=ShortBlock; - Raw.Get(ProtectHead.DataSize); - Raw.Get(ProtectHead.Version); - Raw.Get(ProtectHead.RecSectors); - Raw.Get(ProtectHead.TotalBlocks); - Raw.Get(ProtectHead.Mark,8); + ProtectHead.DataSize=Raw.Get4(); + ProtectHead.Version=Raw.Get1(); + ProtectHead.RecSectors=Raw.Get2(); + ProtectHead.TotalBlocks=Raw.Get4(); + Raw.GetB(ProtectHead.Mark,8); NextBlockPos+=ProtectHead.DataSize; - RecoverySectors=ProtectHead.RecSectors; break; - case SUB_HEAD: + case HEAD3_OLDSERVICE: // RAR 2.9 and earlier. *(BaseBlock *)&SubBlockHead=ShortBlock; - Raw.Get(SubBlockHead.DataSize); + SubBlockHead.DataSize=Raw.Get4(); NextBlockPos+=SubBlockHead.DataSize; - Raw.Get(SubBlockHead.SubType); - Raw.Get(SubBlockHead.Level); + SubBlockHead.SubType=Raw.Get2(); + SubBlockHead.Level=Raw.Get1(); switch(SubBlockHead.SubType) { - case UO_HEAD: - *(SubBlockHeader *)&UOHead=SubBlockHead; - Raw.Get(UOHead.OwnerNameSize); - Raw.Get(UOHead.GroupNameSize); - if (UOHead.OwnerNameSize>NM-1) - UOHead.OwnerNameSize=NM-1; - if (UOHead.GroupNameSize>NM-1) - UOHead.GroupNameSize=NM-1; - Raw.Get((byte *)UOHead.OwnerName,UOHead.OwnerNameSize); - Raw.Get((byte *)UOHead.GroupName,UOHead.GroupNameSize); - UOHead.OwnerName[UOHead.OwnerNameSize]=0; - UOHead.GroupName[UOHead.GroupNameSize]=0; - break; - case MAC_HEAD: - *(SubBlockHeader *)&MACHead=SubBlockHead; - Raw.Get(MACHead.fileType); - Raw.Get(MACHead.fileCreator); - break; - case EA_HEAD: - case BEEA_HEAD: case NTACL_HEAD: *(SubBlockHeader *)&EAHead=SubBlockHead; - Raw.Get(EAHead.UnpSize); - Raw.Get(EAHead.UnpVer); - Raw.Get(EAHead.Method); - Raw.Get(EAHead.EACRC); + EAHead.UnpSize=Raw.Get4(); + EAHead.UnpVer=Raw.Get1(); + EAHead.Method=Raw.Get1(); + EAHead.EACRC=Raw.Get4(); break; case STREAM_HEAD: *(SubBlockHeader *)&StreamHead=SubBlockHead; - Raw.Get(StreamHead.UnpSize); - Raw.Get(StreamHead.UnpVer); - Raw.Get(StreamHead.Method); - Raw.Get(StreamHead.StreamCRC); - Raw.Get(StreamHead.StreamNameSize); - if (StreamHead.StreamNameSize>NM-1) - StreamHead.StreamNameSize=NM-1; - Raw.Get((byte *)StreamHead.StreamName,StreamHead.StreamNameSize); + StreamHead.UnpSize=Raw.Get4(); + StreamHead.UnpVer=Raw.Get1(); + StreamHead.Method=Raw.Get1(); + StreamHead.StreamCRC=Raw.Get4(); + StreamHead.StreamNameSize=Raw.Get2(); + if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName)) + StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1; + Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize); StreamHead.StreamName[StreamHead.StreamNameSize]=0; break; } @@ -352,326 +500,1018 @@ int Archive::ReadHeader() #endif default: if (ShortBlock.Flags & LONG_BLOCK) + NextBlockPos+=Raw.Get4(); + break; + } + + ushort HeaderCRC=Raw.GetCRC15(false); + + // Old AV header does not have header CRC properly set. + // Old Unix owners header didn't include string fields into header size, + // but included them into CRC, so it couldn't be verified with generic + // approach here. + if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN && + ShortBlock.HeaderType!=HEAD3_AV && + (ShortBlock.HeaderType!=HEAD3_OLDSERVICE || SubBlockHead.SubType!=UO_HEAD)) + { + bool Recovered=false; + if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) + { + // Last 7 bytes of recovered volume can contain zeroes, because + // REV files store its own information (volume number, etc.) here. + int64 Length=Tell(); + Seek(Length-7,SEEK_SET); + Recovered=true; + for (int J=0;J<7;J++) + if (GetByte()!=0) + Recovered=false; + } + if (!Recovered) + { + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); + + if (Decrypt) { - uint DataSize; - Raw.Get(DataSize); - NextBlockPos+=DataSize; + uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); + FailedHeaderDecryption=true; + return 0; } - break; + } } - HeaderCRC=~Raw.GetCRC(false)&0xffff; - CurHeaderType=ShortBlock.HeadType; + + return Raw.Size(); +} + + +size_t Archive::ReadHeader50() +{ + RawRead Raw(this); + + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5; + if (Decrypt) { - NextBlockPos+=Raw.PaddedSize()+SALT_SIZE; +#if defined(RAR_NOCRYPT) + return 0; +#else + + if (Cmd->SkipEncrypted) + { + uiMsg(UIMSG_SKIPENCARC,FileName); + FailedHeaderDecryption=true; // Suppress error messages and quit quietly. + return 0; + } - if (ShortBlock.HeadCRC!=HeaderCRC) + byte HeadersInitV[SIZE_INITV]; + if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) { - bool Recovered=false; - if (ShortBlock.HeadType==ENDARC_HEAD && (EndArcHead.Flags & EARC_REVSPACE)!=0) + UnexpEndArcMsg(); + return 0; + } + + // We repeat the password request only for manually entered passwords + // and not for -p<pwd>. Wrong password can be intentionally provided + // in -p<pwd> to not stop batch processing for encrypted archives. + bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); + + RarCheckPassword CheckPwd; + if (CryptHead.UsePswCheck && !BrokenHeader) + CheckPwd.Set(CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,CryptHead.PswCheck); + + while (true) // Repeat the password prompt for wrong passwords. + { + RequestArcPassword(CheckPwd.IsSet() ? &CheckPwd:NULL); + + byte PswCheck[SIZE_PSWCHECK]; + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); + // Verify password validity. If header is damaged, we cannot rely on + // password check value, because it can be damaged too. + if (CryptHead.UsePswCheck && !BrokenHeader && + memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) { - // Last 7 bytes of recovered volume can contain zeroes, because - // REV files store its own information (volume number, etc.) here. - SaveFilePos SavePos(*this); - Int64 Length=Tell(); - Seek(Length-7,SEEK_SET); - Recovered=true; - for (int J=0;J<7;J++) - if (GetByte()!=0) - Recovered=false; + if (GlobalPassword) // For -p<pwd> or Ctrl+P. + { + // This message is used by Android GUI to reset cached passwords. + // Update appropriate code if changed. + uiMsg(UIERROR_BADPSW,FileName,FileName); + FailedHeaderDecryption=true; + ErrHandler.SetErrorCode(RARX_BADPWD); + return 0; + } + else // For passwords entered manually. + { + // This message is used by Android GUI and Windows GUI and SFX to + // reset cached passwords. Update appropriate code if changed. + uiMsg(UIWAIT_BADPSW,FileName,FileName); + Cmd->Password.Clean(); + } + +#ifdef RARDLL + // Avoid new requests for unrar.dll to prevent the infinite loop + // if app always returns the same password. + ErrHandler.SetErrorCode(RARX_BADPWD); + Cmd->DllError=ERAR_BAD_PASSWORD; + ErrHandler.Exit(RARX_BADPWD); +#else + continue; // Request a password again. +#endif } - if (!Recovered) - { -#ifndef SILENT - Log(FileName,St(MEncrBadCRC),FileName); + break; + } + + Raw.SetCrypt(&HeadersCrypt); #endif - Close(); + } - BrokenFileHeader=true; - ErrHandler.SetErrorCode(CRC_ERROR); - return(0); -// ErrHandler.Exit(CRC_ERROR); - } + // Header size must not occupy more than 3 variable length integer bytes + // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5), + // so here we read 4 byte CRC32 followed by 3 bytes or less of header size. + const size_t FirstReadSize=7; // Smallest possible block size. + if (Raw.Read(FirstReadSize)<FirstReadSize) + { + UnexpEndArcMsg(); + return 0; + } + + ShortBlock.Reset(); + ShortBlock.HeadCRC=Raw.Get4(); + uint SizeBytes=Raw.GetVSize(4); + uint64 BlockSize=Raw.GetV(); + + if (BlockSize==0 || SizeBytes==0) + { + BrokenHeaderMsg(); + return 0; + } + + int SizeToRead=int(BlockSize); + SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any. + uint HeaderSize=4+SizeBytes+(uint)BlockSize; + + if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5) + { + BrokenHeaderMsg(); + return 0; + } + + Raw.Read(SizeToRead); + + if (Raw.Size()<HeaderSize) + { + UnexpEndArcMsg(); + return 0; + } + + uint HeaderCRC=Raw.GetCRC50(); + + ShortBlock.HeaderType=(HEADER_TYPE)Raw.GetV(); + ShortBlock.Flags=(uint)Raw.GetV(); + ShortBlock.SkipIfUnknown=(ShortBlock.Flags & HFL_SKIPIFUNKNOWN)!=0; + ShortBlock.HeadSize=HeaderSize; + + CurHeaderType=ShortBlock.HeaderType; + + bool BadCRC=(ShortBlock.HeadCRC!=HeaderCRC); + if (BadCRC) + { + BrokenHeaderMsg(); // Report, but attempt to process. + + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); + + if (Decrypt) + { + uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); + FailedHeaderDecryption=true; + return 0; + } + } + + uint64 ExtraSize=0; + if ((ShortBlock.Flags & HFL_EXTRA)!=0) + { + ExtraSize=Raw.GetV(); + if (ExtraSize>=ShortBlock.HeadSize) + { + BrokenHeaderMsg(); + return 0; } } - if (NextBlockPos<=CurBlockPos) + uint64 DataSize=0; + if ((ShortBlock.Flags & HFL_DATA)!=0) + DataSize=Raw.GetV(); + + NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize); + // Set to 0 in case of overflow, so end of ReadHeader cares about it. + NextBlockPos=SafeAdd(NextBlockPos,DataSize,0); + + switch(ShortBlock.HeaderType) { -#ifndef SHELL_EXT - Log(FileName,St(MLogFileHead),"???"); + case HEAD_CRYPT: + { + *(BaseBlock *)&CryptHead=ShortBlock; + uint CryptVersion=(uint)Raw.GetV(); + if (CryptVersion>CRYPT_VERSION) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"h%u",CryptVersion); + UnkEncVerMsg(FileName,Info); + return 0; + } + uint EncFlags=(uint)Raw.GetV(); + CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0; + CryptHead.Lg2Count=Raw.Get1(); + if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count); + UnkEncVerMsg(FileName,Info); + return 0; + } + + Raw.GetB(CryptHead.Salt,SIZE_SALT50); + if (CryptHead.UsePswCheck) + { + Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK); + + byte csum[SIZE_PSWCHECK_CSUM]; + Raw.GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + } + Encrypted=true; + } + break; + case HEAD_MAIN: + { + MainHead.Reset(); + *(BaseBlock *)&MainHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + + Volume=(ArcFlags & MHFL_VOLUME)!=0; + Solid=(ArcFlags & MHFL_SOLID)!=0; + Locked=(ArcFlags & MHFL_LOCK)!=0; + Protected=(ArcFlags & MHFL_PROTECT)!=0; + Signed=false; + NewNumbering=true; + + if ((ArcFlags & MHFL_VOLNUMBER)!=0) + VolNumber=(uint)Raw.GetV(); + else + VolNumber=0; + FirstVolume=Volume && VolNumber==0; + + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead); + +#ifdef USE_QOPEN + if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) + { + // We seek to QO block in the end of archive when processing + // QOpen.Load, so we need to preserve current block positions + // to not break normal archive processing by calling function. + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; + + QOpen.Init(this,false); + QOpen.Load(MainHead.QOpenOffset); + + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + } +#endif + } + break; + case HEAD_FILE: + case HEAD_SERVICE: + { + FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead; + hd->Reset(); // Clear hash, time fields and other stuff like flags. + *(BaseBlock *)hd=ShortBlock; + + bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; + + hd->LargeFile=true; + + hd->PackSize=DataSize; + hd->FileFlags=(uint)Raw.GetV(); + hd->UnpSize=Raw.GetV(); + + hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0; + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; + + hd->MaxSize=Max(hd->PackSize,hd->UnpSize); + hd->FileAttr=(uint)Raw.GetV(); + if ((hd->FileFlags & FHFL_UTIME)!=0) + hd->mtime.SetUnix((time_t)Raw.Get4()); + + hd->FileHash.Type=HASH_NONE; + if ((hd->FileFlags & FHFL_CRC32)!=0) + { + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + } + + hd->RedirType=FSREDIR_NONE; + + uint CompInfo=(uint)Raw.GetV(); + hd->Method=(CompInfo>>7) & 7; + + // "+ 50" to not mix with old RAR format algorithms. For example, + // we may need to use the compression algorithm 15 in the future, + // but it was already used in RAR 1.5 and Unpack needs to distinguish + // them. + hd->UnpVer=(CompInfo & 0x3f) + 50; + if (hd->UnpVer!=50) // Only 5.0 compression is known now. + hd->UnpVer=VER_UNKNOWN; + + hd->HostOS=(byte)Raw.GetV(); + size_t NameSize=(size_t)Raw.GetV(); + hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0; + + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST5_UNIX) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOS==HOST5_WINDOWS) + hd->HSType=HSYS_WINDOWS; + + hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0; + hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0; + hd->SubBlock=(hd->Flags & HFL_CHILD)!=0; + hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0; + hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; + hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); + + char FileName[NM*4]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + + UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Should do it before converting names, because extra fields can + // affect name processing, like in case of NTFS streams. + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,hd); + + if (FileBlock) + { +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); #endif - BrokenFileHeader=true; - ErrHandler.SetErrorCode(CRC_ERROR); - return(0); + ConvertFileHeader(hd); + } + + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + + + if (BadCRC) // Add the file name to broken header message displayed above. + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); + } + break; + case HEAD_ENDARC: + { + *(BaseBlock *)&EndArcHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0; + EndArcHead.StoreVolNumber=false; + EndArcHead.DataCRC=false; + EndArcHead.RevSpace=false; + } + break; } - return(Raw.Size()); + + return Raw.Size(); } -#ifndef SFX_MODULE -int Archive::ReadOldHeader() +#if !defined(RAR_NOCRYPT) +void Archive::RequestArcPassword(RarCheckPassword *CheckPwd) { - RawRead Raw(this); - if (CurBlockPos<=SFXSize) + if (!Cmd->Password.IsSet()) { - Raw.Read(SIZEOF_OLDMHD); - Raw.Get(OldMhd.Mark,4); - Raw.Get(OldMhd.HeadSize); - Raw.Get(OldMhd.Flags); - NextBlockPos=CurBlockPos+OldMhd.HeadSize; - CurHeaderType=MAIN_HEAD; +#ifdef RARDLL + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + } + if (!Cmd->Password.IsSet()) + { + Close(); + Cmd->DllError=ERAR_MISSING_PASSWORD; + ErrHandler.Exit(RARX_USERBREAK); + } +#else + if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password,CheckPwd)) + { + Close(); + uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on. + ErrHandler.Exit(RARX_USERBREAK); + } +#endif + Cmd->ManualPassword=true; } - else +} +#endif + + +void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,const BaseBlock *bb) +{ + // Read extra data from the end of block skipping any fields before it. + size_t ExtraStart=Raw->Size()-ExtraSize; + if (ExtraStart<Raw->GetPos()) + return; + Raw->SetPos(ExtraStart); + while (Raw->DataLeft()>=2) { - OldFileHeader OldLhd; - Raw.Read(SIZEOF_OLDLHD); - NewLhd.HeadType=FILE_HEAD; - Raw.Get(NewLhd.PackSize); - Raw.Get(NewLhd.UnpSize); - Raw.Get(OldLhd.FileCRC); - Raw.Get(NewLhd.HeadSize); - Raw.Get(NewLhd.FileTime); - Raw.Get(OldLhd.FileAttr); - Raw.Get(OldLhd.Flags); - Raw.Get(OldLhd.UnpVer); - Raw.Get(OldLhd.NameSize); - Raw.Get(OldLhd.Method); - - NewLhd.Flags=OldLhd.Flags|LONG_BLOCK; - NewLhd.UnpVer=(OldLhd.UnpVer==2) ? 13 : 10; - NewLhd.Method=OldLhd.Method+0x30; - NewLhd.NameSize=OldLhd.NameSize; - NewLhd.FileAttr=OldLhd.FileAttr; - NewLhd.FileCRC=OldLhd.FileCRC; - NewLhd.FullPackSize=NewLhd.PackSize; - NewLhd.FullUnpSize=NewLhd.UnpSize; - - NewLhd.mtime.SetDos(NewLhd.FileTime); - NewLhd.ctime.Reset(); - NewLhd.atime.Reset(); - NewLhd.arctime.Reset(); - - Raw.Read(OldLhd.NameSize); - Raw.Get((byte *)NewLhd.FileName,OldLhd.NameSize); - NewLhd.FileName[OldLhd.NameSize]=0; - ConvertNameCase(NewLhd.FileName); - *NewLhd.FileNameW=0; + int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative. + if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft()) + break; + size_t NextPos=size_t(Raw->GetPos()+FieldSize); + uint64 FieldType=Raw->GetV(); - if (Raw.Size()!=0) - NextBlockPos=CurBlockPos+NewLhd.HeadSize+NewLhd.PackSize; - CurHeaderType=FILE_HEAD; + FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields. + + if (FieldSize<0) // FieldType is longer than expected extra field size. + break; + + if (bb->HeaderType==HEAD_MAIN) + { + MainHeader *hd=(MainHeader *)bb; + switch(FieldType) + { + case MHEXTRA_LOCATOR: + { + hd->Locator=true; + uint Flags=(uint)Raw->GetV(); + if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->QOpenOffset=Offset+CurBlockPos; + } + if ((Flags & MHEXTRA_LOCATOR_RR)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->RROffset=Offset+CurBlockPos; + } + } + break; + case MHEXTRA_METADATA: + { + uint Flags=(uint)Raw->GetV(); + if ((Flags & MHEXTRA_METADATA_NAME)!=0) + { + uint64 NameSize=Raw->GetV(); + if (NameSize>0 && NameSize<0x10000) // Prevent excessive allocation. + { + std::vector<char> NameU((size_t)NameSize); // UTF-8 name. + Raw->GetB(&NameU[0],(size_t)NameSize); + // If starts from 0, the name was longer than reserved space + // when saving this extra field. + if (NameU[0]!=0) + { + NameU.push_back(0); + std::vector<wchar> NameW(NameU.size()*4); + UtfToWide(&NameU[0],&NameW[0],NameW.size()); + hd->OrigName.assign(&NameW[0]); + } + } + } + if ((Flags & MHEXTRA_METADATA_CTIME)!=0) + if ((Flags & MHEXTRA_METADATA_UNIXTIME)!=0) + if ((Flags & MHEXTRA_METADATA_UNIX_NS)!=0) + hd->OrigTime.SetUnixNS(Raw->Get8()); + else + hd->OrigTime.SetUnix((time_t)Raw->Get4()); + else + hd->OrigTime.SetWin(Raw->Get8()); + } + break; + } + } + + if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE) + { + FileHeader *hd=(FileHeader *)bb; + switch(FieldType) + { + case FHEXTRA_CRYPT: + { + FileHeader *hd=(FileHeader *)bb; + uint EncVersion=(uint)Raw->GetV(); + if (EncVersion>CRYPT_VERSION) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"x%u",EncVersion); + UnkEncVerMsg(hd->FileName,Info); + } + else + { + uint Flags=(uint)Raw->GetV(); + hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0; + hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; + hd->Lg2Count=Raw->Get1(); + if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + { + wchar Info[20]; + swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count); + UnkEncVerMsg(hd->FileName,Info); + } + Raw->GetB(hd->Salt,SIZE_SALT50); + Raw->GetB(hd->InitV,SIZE_INITV); + if (hd->UsePswCheck) + { + Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); + + // It is important to know if password check data is valid. + // If it is damaged and header CRC32 fails to detect it, + // archiver would refuse to decompress a possibly valid file. + // Since we want to be sure distinguishing a wrong password + // or corrupt file data, we use 64-bit password check data + // and to control its validity we use 32 bits of password + // check data SHA-256 additionally to 32-bit header CRC32. + byte csum[SIZE_PSWCHECK_CSUM]; + Raw->GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + + // RAR 5.21 and earlier set PswCheck field in service records to 0 + // even if UsePswCheck was present. + if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) + hd->UsePswCheck=0; + } + hd->SaltSet=true; + hd->CryptMethod=CRYPT_RAR50; + hd->Encrypted=true; + } + } + break; + case FHEXTRA_HASH: + { + FileHeader *hd=(FileHeader *)bb; + uint Type=(uint)Raw->GetV(); + if (Type==FHEXTRA_HASH_BLAKE2) + { + hd->FileHash.Type=HASH_BLAKE2; + Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + } + } + break; + case FHEXTRA_HTIME: + if (FieldSize>=5) + { + byte Flags=(byte)Raw->GetV(); + bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0) + if (UnixTime) + hd->mtime.SetUnix(Raw->Get4()); + else + hd->mtime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0) + if (UnixTime) + hd->ctime.SetUnix(Raw->Get4()); + else + hd->ctime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0) + if (UnixTime) + hd->atime.SetUnix((time_t)Raw->Get4()); + else + hd->atime.SetWin(Raw->Get8()); + if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds. + { + uint ns; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->mtime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->ctime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->atime.Adjust(ns); + } + } + break; + case FHEXTRA_VERSION: + if (FieldSize>=1) + { + Raw->GetV(); // Skip flags field. + uint Version=(uint)Raw->GetV(); + if (Version!=0) + { + hd->Version=true; + + wchar VerText[20]; + swprintf(VerText,ASIZE(VerText),L";%u",Version); + wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName)); + } + } + break; + case FHEXTRA_REDIR: + { + hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); + uint Flags=(uint)Raw->GetV(); + hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; + size_t NameSize=(size_t)Raw->GetV(); + + char UtfName[NM*4]; + *UtfName=0; + if (NameSize<ASIZE(UtfName)-1) + { + Raw->GetB(UtfName,NameSize); + UtfName[NameSize]=0; + } +#ifdef _WIN_ALL + UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName)); +#endif + UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName)); + } + break; + case FHEXTRA_UOWNER: + { + uint Flags=(uint)Raw->GetV(); + hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0; + hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0; + *hd->UnixOwnerName=*hd->UnixGroupName=0; + if ((Flags & FHEXTRA_UOWNER_UNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixOwnerName)-1); + Raw->GetB(hd->UnixOwnerName,Length); + hd->UnixOwnerName[Length]=0; + } + if ((Flags & FHEXTRA_UOWNER_GNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixGroupName)-1); + Raw->GetB(hd->UnixGroupName,Length); + hd->UnixGroupName[Length]=0; + } +#ifdef _UNIX + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uid_t)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(gid_t)Raw->GetV(); +#else + // Need these fields in Windows too for 'list' command, + // but uid_t and gid_t are not defined. + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uint)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(uint)Raw->GetV(); +#endif + hd->UnixOwnerSet=true; + } + break; + case FHEXTRA_SUBDATA: + { + // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than + // required. It did not hurt extraction, because UnRAR 5.21 + // and earlier ignored this field and set FieldSize as data left + // in entire extra area. But now we set the correct field size + // and set FieldSize based on the actual extra record size, + // so we need to adjust it for those older archives here. + // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE + // and always is last in extra area. So since its size is by 1 + // less than needed, we always have 1 byte left in extra area, + // which fact we use here to detect such archives. + if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1) + FieldSize++; + + // We cannot allocate too much memory here, because above + // we check FieldSize againt Raw size and we control that Raw size + // is sensible when reading headers. + hd->SubData.Alloc((size_t)FieldSize); + Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize); + } + break; + } + } + + Raw->SetPos(NextPos); } - return(NextBlockPos>CurBlockPos ? Raw.Size():0); } -#endif -void Archive::ConvertNameCase(char *Name) +#ifndef SFX_MODULE +size_t Archive::ReadHeader14() { - if (Cmd->ConvertNames==NAMES_UPPERCASE) + RawRead Raw(this); + if (CurBlockPos<=(int64)SFXSize) { - IntToExt(Name,Name); - strupper(Name); - ExtToInt(Name,Name); + Raw.Read(SIZEOF_MAINHEAD14); + MainHead.Reset(); + byte Mark[4]; + Raw.GetB(Mark,4); + uint HeadSize=Raw.Get2(); + if (HeadSize<7) + return false; + byte Flags=Raw.Get1(); + NextBlockPos=CurBlockPos+HeadSize; + CurHeaderType=HEAD_MAIN; + + Volume=(Flags & MHD_VOLUME)!=0; + Solid=(Flags & MHD_SOLID)!=0; + Locked=(Flags & MHD_LOCK)!=0; + MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0; + MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0; } - if (Cmd->ConvertNames==NAMES_LOWERCASE) + else { - IntToExt(Name,Name); - strlower(Name); - ExtToInt(Name,Name); + Raw.Read(SIZEOF_FILEHEAD14); + FileHead.Reset(); + + FileHead.HeaderType=HEAD_FILE; + FileHead.DataSize=Raw.Get4(); + FileHead.UnpSize=Raw.Get4(); + FileHead.FileHash.Type=HASH_RAR14; + FileHead.FileHash.CRC32=Raw.Get2(); + FileHead.HeadSize=Raw.Get2(); + if (FileHead.HeadSize<21) + return false; + uint FileTime=Raw.Get4(); + FileHead.FileAttr=Raw.Get1(); + FileHead.Flags=Raw.Get1()|LONG_BLOCK; + FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10; + size_t NameSize=Raw.Get1(); + FileHead.Method=Raw.Get1(); + + FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0; + FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0; + FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0; + FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE; + + FileHead.PackSize=FileHead.DataSize; + FileHead.WinSize=0x10000; + FileHead.Dir=(FileHead.FileAttr & 0x10)!=0; + + FileHead.HostOS=HOST_MSDOS; + FileHead.HSType=HSYS_WINDOWS; + + FileHead.mtime.SetDos(FileTime); + + Raw.Read(NameSize); + + char FileName[NM]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + IntToExt(FileName,FileName,ASIZE(FileName)); + CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName)); + ConvertNameCase(FileHead.FileName); + ConvertFileHeader(&FileHead); + + if (Raw.Size()!=0) + NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize; + CurHeaderType=HEAD_FILE; } + return NextBlockPos>CurBlockPos ? Raw.Size() : 0; } +#endif #ifndef SFX_MODULE void Archive::ConvertNameCase(wchar *Name) { if (Cmd->ConvertNames==NAMES_UPPERCASE) - strupperw(Name); + wcsupper(Name); if (Cmd->ConvertNames==NAMES_LOWERCASE) - strlowerw(Name); + wcslower(Name); } #endif bool Archive::IsArcDir() { - return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY); -} - - -bool Archive::IsArcLabel() -{ - return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8)); + return FileHead.Dir; } void Archive::ConvertAttributes() { -#if defined(_WIN_32) || defined(_EMX) - switch(NewLhd.HostOS) - { - case HOST_MSDOS: - case HOST_OS2: - case HOST_WIN32: - break; - case HOST_UNIX: - case HOST_BEOS: - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x10; - else - NewLhd.FileAttr=0x20; - break; - default: - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x10; - else - NewLhd.FileAttr=0x20; - break; - } +#if defined(_WIN_ALL) || defined(_EMX) + if (FileHead.HSType!=HSYS_WINDOWS) + FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20; #endif #ifdef _UNIX + // umask defines which permission bits must not be set by default + // when creating a file or directory. The typical default value + // for the process umask is S_IWGRP | S_IWOTH (octal 022), + // resulting in 0644 mode for new files. + // Normally umask is applied automatically when creating a file, + // but we set attributes with chmod later, so we need to calculate + // resulting attributes here. We do it only for non-Unix archives. + // We restore native Unix attributes as is, because it can be backup. static mode_t mask = (mode_t) -1; if (mask == (mode_t) -1) { + // umask call returns the current umask value. Argument (022) is not + // really important here. mask = umask(022); + + // Restore the original umask value, which was changed to 022 above. umask(mask); } - switch(NewLhd.HostOS) + + switch(FileHead.HSType) { - case HOST_MSDOS: - case HOST_OS2: - case HOST_WIN32: - if (NewLhd.FileAttr & 0x10) - NewLhd.FileAttr=0x41ff & ~mask; - else - if (NewLhd.FileAttr & 1) - NewLhd.FileAttr=0x8124 & ~mask; + case HSYS_WINDOWS: + { + // Mapping MSDOS, OS/2 and Windows file attributes to Unix. + + if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY + { + // For directories we use 0777 mask. + FileHead.FileAttr=0777 & ~mask; + } else - NewLhd.FileAttr=0x81b6 & ~mask; + if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY + { + // For read only files we use 0444 mask with 'w' bits turned off. + FileHead.FileAttr=0444 & ~mask; + } + else + { + // umask does not set +x for regular files, so we use 0666 + // instead of 0777 as for directories. + FileHead.FileAttr=0666 & ~mask; + } + } break; - case HOST_UNIX: - case HOST_BEOS: + case HSYS_UNIX: break; default: - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x41ff & ~mask; + if (FileHead.Dir) + FileHead.FileAttr=0x41ff & ~mask; else - NewLhd.FileAttr=0x81b6 & ~mask; + FileHead.FileAttr=0x81b6 & ~mask; break; } #endif } -void Archive::ConvertUnknownHeader() +void Archive::ConvertFileHeader(FileHeader *hd) { - if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10)) - NewLhd.Flags|=LHD_DIRECTORY; - if (NewLhd.HostOS>=HOST_MAX) - { - if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY) - NewLhd.FileAttr=0x10; + if (hd->HSType==HSYS_UNKNOWN) + if (hd->Dir) + hd->FileAttr=0x10; else - NewLhd.FileAttr=0x20; - } - for (char *s=NewLhd.FileName;*s!=0;s=charnext(s)) + hd->FileAttr=0x20; + +#ifdef _WIN_ALL + if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. + ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName)); +#endif + + for (wchar *s=hd->FileName;*s!=0;s++) { - if (*s=='/' || *s=='\\') - *s=CPATHDIVIDER; -#if defined(_APPLE) && !defined(UNICODE_SUPPORTED) - if ((byte)*s<32 || (byte)*s>127) +#ifdef _UNIX + // Backslash is the invalid character for Windows file headers, + // but it can present in Unix file names extracted in Unix. + if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS) *s='_'; #endif -#if defined(_WIN_32) || defined(_EMX) +#if defined(_WIN_ALL) || defined(_EMX) + // RAR 5.0 archives do not use '\' as path separator, so if we see it, + // it means that it is a part of Unix file name, which we cannot + // extract in Windows. + if (*s=='\\' && Format==RARFMT50) + *s='_'; + // ':' in file names is allowed in Unix, but not in Windows. // Even worse, file data will be written to NTFS stream on NTFS, - // so automatic name correction on file create error in extraction - // routine does not work. Let's better replace ':' now. - if (NewLhd.HostOS==HOST_UNIX && *s==':') + // so automatic name correction on file create error in extraction + // routine does not work. In Windows and DOS versions we better + // replace ':' now. + if (*s==':') *s='_'; #endif + // This code must be performed only after other path separator checks, + // because it produces backslashes illegal for some of checks above. + // Backslash is allowed in file names in Unix, but not in Windows. + // Still, RAR 4.x uses backslashes as path separator even in Unix. + // Forward slash is not allowed in both systems. In RAR 5.0 we use + // the forward slash as universal path separator. + if (*s=='/' || *s=='\\' && Format!=RARFMT50) + *s=CPATHDIVIDER; } +} - for (wchar *s=NewLhd.FileNameW;*s!=0;s++) - { - if (*s=='/' || *s=='\\') - *s=CPATHDIVIDER; -#if defined(_WIN_32) || defined(_EMX) - // ':' in file names is allowed in Unix, but not in Windows. - // Even worse, file data will be written to NTFS stream on NTFS, - // so automatic name correction on file create error in extraction - // routine does not work. Let's better replace ':' now. - if (NewLhd.HostOS==HOST_UNIX && *s==':') - *s='_'; -#endif - } +int64 Archive::GetStartPos() +{ + int64 StartPos=SFXSize+MarkHead.HeadSize; + if (Format==RARFMT15) + StartPos+=MainHead.HeadSize; + else // RAR 5.0. + StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize); + return StartPos; } -#ifndef SHELL_EXT -bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile) +bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode) { - if (HeaderCRC!=SubHead.HeadCRC) + if (BrokenHeader) { -#ifndef SHELL_EXT - Log(FileName,St(MSubHeadCorrupt)); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); - return(false); + uiMsg(UIERROR_SUBHEADERBROKEN,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return false; } - if (SubHead.Method<0x30 || SubHead.Method>0x35 || SubHead.UnpVer>/*PACK_VER*/36) + if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK)) { -#ifndef SHELL_EXT - Log(FileName,St(MSubHeadUnknown)); -#endif - return(false); + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; } - if (SubHead.PackSize==0 && (SubHead.Flags & LHD_SPLIT_AFTER)==0) - return(true); + if (SubHead.PackSize==0 && !SubHead.SplitAfter) + return true; SubDataIO.Init(); Unpack Unpack(&SubDataIO); - Unpack.Init(); + Unpack.Init(SubHead.WinSize,false); if (DestFile==NULL) { - UnpData->Alloc(SubHead.UnpSize); - SubDataIO.SetUnpackToMemory(&(*UnpData)[0],SubHead.UnpSize); + if (SubHead.UnpSize>0x1000000) + { + // Prevent the excessive allocation. When reading to memory, normally + // this function operates with reasonably small blocks, such as + // the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream. + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; + } + if (UnpData==NULL) + SubDataIO.SetTestMode(true); + else + { + UnpData->Alloc((size_t)SubHead.UnpSize); + SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize); + } } - if (SubHead.Flags & LHD_PASSWORD) - if (*Cmd->Password) - SubDataIO.SetEncryption(SubHead.UnpVer,Cmd->Password, - (SubHead.Flags & LHD_SALT) ? SubHead.Salt:NULL,false, - SubHead.UnpVer>=36); + if (SubHead.Encrypted) + if (Cmd->Password.IsSet()) + SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password, + SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV, + SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck); else - return(false); + return false; + SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1); SubDataIO.SetPackedSizeToRead(SubHead.PackSize); SubDataIO.EnableShowProgress(false); SubDataIO.SetFiles(this,DestFile); - SubDataIO.UnpVolume=(SubHead.Flags & LHD_SPLIT_AFTER); + SubDataIO.SetTestMode(TestMode); + SubDataIO.UnpVolume=SubHead.SplitAfter; SubDataIO.SetSubHeader(&SubHead,NULL); Unpack.SetDestSize(SubHead.UnpSize); - if (SubHead.Method==0x30) + if (SubHead.Method==0) CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize); else Unpack.DoUnpack(SubHead.UnpVer,false); - if (SubHead.FileCRC!=~SubDataIO.UnpFileCRC) + if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL)) { -#ifndef SHELL_EXT - Log(FileName,St(MSubHeadDataCRC),SubHead.FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName); + ErrHandler.SetErrorCode(RARX_CRC); if (UnpData!=NULL) UnpData->Reset(); - return(false); + return false; } - return(true); + return true; } -#endif diff --git a/unrar/unrar/array.hpp b/unrar/unrar/array.hpp index 1130bfb..ac786f7 100644 --- a/unrar/unrar/array.hpp +++ b/unrar/unrar/array.hpp @@ -7,28 +7,38 @@ template <class T> class Array { private: T *Buffer; - int BufSize; - int AllocSize; + size_t BufSize; + size_t AllocSize; + size_t MaxSize; public: Array(); - Array(int Size); + Array(size_t Size); + Array(const Array &Src); // Copy constructor. ~Array(); inline void CleanData(); - inline T& operator [](int Item); - inline int Size(); - void Add(int Items); - void Alloc(int Items); + inline T& operator [](size_t Item) const; + inline T* operator + (size_t Pos); + inline size_t Size(); // Returns the size in items, not in bytes. + void Add(size_t Items); + void Alloc(size_t Items); void Reset(); - void operator = (Array<T> &Src); + void SoftReset(); + Array<T>& operator = (const Array<T> &Src); void Push(T Item); - T* Addr() {return(Buffer);} + void Append(T *Item,size_t Count); + T* Addr(size_t Item) {return Buffer+Item;} + void SetMaxSize(size_t Size) {MaxSize=Size;} + T* Begin() {return Buffer;} + T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} }; + template <class T> void Array<T>::CleanData() { Buffer=NULL; BufSize=0; AllocSize=0; + MaxSize=0; } @@ -38,52 +48,72 @@ template <class T> Array<T>::Array() } -template <class T> Array<T>::Array(int Size) +template <class T> Array<T>::Array(size_t Size) { - Buffer=(T *)rarmalloc(sizeof(T)*Size); - if (Buffer==NULL && Size!=0) - ErrHandler.MemoryError(); + CleanData(); + Add(Size); +} - AllocSize=BufSize=Size; + +// Copy constructor in case we need to pass an object as value. +template <class T> Array<T>::Array(const Array &Src) +{ + CleanData(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); } template <class T> Array<T>::~Array() { if (Buffer!=NULL) - rarfree(Buffer); + free(Buffer); +} + + +template <class T> inline T& Array<T>::operator [](size_t Item) const +{ + return Buffer[Item]; } -template <class T> inline T& Array<T>::operator [](int Item) +template <class T> inline T* Array<T>::operator +(size_t Pos) { - return(Buffer[Item]); + return Buffer+Pos; } -template <class T> inline int Array<T>::Size() +template <class T> inline size_t Array<T>::Size() { - return(BufSize); + return BufSize; } -template <class T> void Array<T>::Add(int Items) +template <class T> void Array<T>::Add(size_t Items) { BufSize+=Items; if (BufSize>AllocSize) { - int Suggested=AllocSize+AllocSize/4+32; - int NewSize=Max(BufSize,Suggested); + if (MaxSize!=0 && BufSize>MaxSize) + { + ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize); + ErrHandler.MemoryError(); + } + + size_t Suggested=AllocSize+AllocSize/4+32; + size_t NewSize=Max(BufSize,Suggested); - Buffer=(T *)rarrealloc(Buffer,NewSize*sizeof(T)); - if (Buffer==NULL) + T *NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); + if (NewBuffer==NULL) ErrHandler.MemoryError(); + Buffer=NewBuffer; AllocSize=NewSize; } } -template <class T> void Array<T>::Alloc(int Items) +template <class T> void Array<T>::Alloc(size_t Items) { if (Items>AllocSize) Add(Items-BufSize); @@ -96,7 +126,7 @@ template <class T> void Array<T>::Reset() { if (Buffer!=NULL) { - rarfree(Buffer); + free(Buffer); Buffer=NULL; } BufSize=0; @@ -104,12 +134,21 @@ template <class T> void Array<T>::Reset() } -template <class T> void Array<T>::operator =(Array<T> &Src) +// Reset buffer size, but preserve already allocated memory if any, +// so we can reuse it without wasting time to allocation. +template <class T> void Array<T>::SoftReset() +{ + BufSize=0; +} + + +template <class T> Array<T>& Array<T>::operator =(const Array<T> &Src) { Reset(); Alloc(Src.BufSize); if (Src.BufSize!=0) memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); + return *this; } @@ -119,4 +158,12 @@ template <class T> void Array<T>::Push(T Item) (*this)[Size()-1]=Item; } + +template <class T> void Array<T>::Append(T *Items,size_t Count) +{ + size_t CurSize=Size(); + Add(Count); + memcpy(Buffer+CurSize,Items,Count*sizeof(T)); +} + #endif diff --git a/unrar/unrar/beosea.cpp b/unrar/unrar/beosea.cpp deleted file mode 100644 index 86eb7d4..0000000 --- a/unrar/unrar/beosea.cpp +++ /dev/null @@ -1,113 +0,0 @@ - - -void ExtractBeEA(Archive &Arc,char *FileName) -{ - if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER) - { - Log(Arc.FileName,St(MEAUnknHeader),FileName); - return; - } - - ComprDataIO DataIO; - Unpack Unpack(&DataIO); - Unpack.Init(); - - Array<byte> UnpData(Arc.EAHead.UnpSize); - DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); - DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); - DataIO.EnableShowProgress(false); - DataIO.SetFiles(&Arc,NULL); - Unpack.SetDestSize(Arc.EAHead.UnpSize); - Unpack.DoUnpack(Arc.EAHead.UnpVer,false); - - if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - int fd = open(FileName,O_WRONLY); - if (fd==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - return; - } - - int AttrPos=0; - while (AttrPos<Arc.EAHead.UnpSize) - { - unsigned char *CurItem=&UnpData[AttrPos]; - int NameSize=CurItem[0]+((int)CurItem[1]<<8); - int Type=CurItem[2]+((int)CurItem[3]<<8)+((int)CurItem[4]<<16)+((int)CurItem[5]<<24); - int Size=CurItem[6]+((int)CurItem[7]<<8)+((int)CurItem[8]<<16)+((int)CurItem[9]<<24); - char Name[1024]; - if (NameSize>=sizeof(Name)) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - memcpy(Name,CurItem+10,NameSize); - Name[NameSize]=0; - if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - AttrPos+=10+NameSize+Size; - } - close(fd); - mprintf(St(MShowEA)); -} - - -void ExtractBeEANew(Archive &Arc,char *FileName) -{ - Array<byte> SubData; - if (!Arc.ReadSubData(&SubData,NULL)) - return; - - int fd = open(FileName,O_WRONLY); - if (fd==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - return; - } - - int AttrPos=0; - while (AttrPos<Arc.EAHead.UnpSize) - { - unsigned char *CurItem=&SubData[AttrPos]; - int NameSize=CurItem[0]+((int)CurItem[1]<<8); - int Type=CurItem[2]+((int)CurItem[3]<<8)+((int)CurItem[4]<<16)+((int)CurItem[5]<<24); - int Size=CurItem[6]+((int)CurItem[7]<<8)+((int)CurItem[8]<<16)+((int)CurItem[9]<<24); - char Name[1024]; - if (NameSize>=sizeof(Name)) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - memcpy(Name,CurItem+10,NameSize); - Name[NameSize]=0; - if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - break; - } - AttrPos+=10+NameSize+Size; - } - close(fd); - mprintf(St(MShowEA)); -} - diff --git a/unrar/unrar/blake2s.cpp b/unrar/unrar/blake2s.cpp new file mode 100644 index 0000000..317603d --- /dev/null +++ b/unrar/unrar/blake2s.cpp @@ -0,0 +1,183 @@ +// Based on public domain code written in 2012 by Samuel Neves + +#include "rar.hpp" + +#ifdef USE_SSE +#include "blake2s_sse.cpp" +#endif + +static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth); +static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ); +static void blake2s_final( blake2s_state *S, byte *digest ); + +#include "blake2sp.cpp" + +static const uint32 blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +static const byte blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static inline void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = ~0U; +} + + +/* Some helper functions, not necessarily useful */ +static inline void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = ~0U; +} + + +static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + + +/* init2 xors IV with input parameter block */ +void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth) +{ +#ifdef USE_SSE + if (_SSE_Version>=SSE_SSE2) + blake2s_init_sse(); +#endif + + S->init(); // Clean data. + for( int i = 0; i < 8; ++i ) + S->h[i] = blake2s_IV[i]; + + S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block. + S->h[2] ^= node_offset; + S->h[3] ^= (node_depth<<16)|0x20000000; +} + + +#define G(r,i,m,a,b,c,d) \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); + + +static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + uint32 m[16]; + uint32 v[16]; + + for( size_t i = 0; i < 16; ++i ) + m[i] = RawGet4( block + i * 4 ); + + for( size_t i = 0; i < 8; ++i ) + v[i] = S->h[i]; + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows. + { + G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]); + G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]); + G(r,2,m,v[ 2],v[ 6],v[10],v[14]); + G(r,3,m,v[ 3],v[ 7],v[11],v[15]); + G(r,4,m,v[ 0],v[ 5],v[10],v[15]); + G(r,5,m,v[ 1],v[ 6],v[11],v[12]); + G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]); + G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]); + } + + for( size_t i = 0; i < 8; ++i ) + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; +} + + +void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ) +{ + while( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = 2 * BLAKE2S_BLOCKBYTES - left; + + if( inlen > fill ) + { + memcpy( S->buf + left, in, fill ); // Fill buffer + S->buflen += fill; + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + +#ifdef USE_SSE +#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode. + if (_SSE_Version>=SSE_SSE2) +#else + if (_SSE_Version>=SSE_SSSE3) +#endif + blake2s_compress_sse( S, S->buf ); + else + blake2s_compress( S, S->buf ); // Compress +#else + blake2s_compress( S, S->buf ); // Compress +#endif + + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left + S->buflen -= BLAKE2S_BLOCKBYTES; + in += fill; + inlen -= fill; + } + else // inlen <= fill + { + memcpy( S->buf + left, in, (size_t)inlen ); + S->buflen += (size_t)inlen; // Be lazy, do not compress + in += inlen; + inlen = 0; + } + } +} + + +void blake2s_final( blake2s_state *S, byte *digest ) +{ + if( S->buflen > BLAKE2S_BLOCKBYTES ) + { + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); + S->buflen -= BLAKE2S_BLOCKBYTES; + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen ); + } + + blake2s_increment_counter( S, ( uint32 )S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( int i = 0; i < 8; ++i ) /* Output full hash */ + RawPut4( S->h[i], digest + 4 * i ); +} + diff --git a/unrar/unrar/blake2s.hpp b/unrar/unrar/blake2s.hpp new file mode 100644 index 0000000..06e396a --- /dev/null +++ b/unrar/unrar/blake2s.hpp @@ -0,0 +1,107 @@ +// Based on public domain code written in 2012 by Samuel Neves +#ifndef _RAR_BLAKE2_ +#define _RAR_BLAKE2_ + +#define BLAKE2_DIGEST_SIZE 32 +#define BLAKE2_THREADS_NUMBER 8 + +enum blake2s_constant +{ + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32 +}; + + +// Alignment to 64 improves performance of both SSE and non-SSE versions. +// Alignment to n*16 is required for SSE version, so we selected 64. +// We use the custom alignment scheme instead of __declspec(align(x)), +// because it is less compiler dependent. Also the compiler directive +// does not help if structure is a member of class allocated through +// 'new' operator. +struct blake2s_state +{ + // Use constexpr instead of enums, because otherwise clang -std=c++20 + // issues a warning about "arithmetic between different enumeration types" + // in ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT] declaration. + static constexpr size_t BLAKE_ALIGNMENT = 64; + + // buffer and uint32 h[8], t[2], f[2]; + // 2 * BLAKE2S_BLOCKBYTES is the buf size in blake2_code_20140114.zip. + // It might differ in later versions. + static constexpr size_t BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES; + + byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT]; + + byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES]. + uint32 *h, *t, *f; // uint32 h[8], t[2], f[2]. + + size_t buflen; + byte last_node; + + blake2s_state() + { + set_pointers(); + } + + // Required when we declare and assign in the same command. + blake2s_state(blake2s_state &st) + { + set_pointers(); + *this=st; + } + + void set_pointers() + { + // Set aligned pointers. Must be done in constructor, not in Init(), + // so assignments like 'blake2sp_state res=blake2ctx' work correctly + // even if blake2sp_init is not called for 'res'. + buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT); + h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES); + t = h + 8; + f = t + 2; + } + + void init() + { + memset( ubuf, 0, sizeof( ubuf ) ); + buflen = 0; + last_node = 0; + } + + // Since we use pointers, the default = would work incorrectly. + blake2s_state& operator = (blake2s_state &st) + { + if (this != &st) + { + memcpy(buf, st.buf, BLAKE_DATA_SIZE); + buflen = st.buflen; + last_node = st.last_node; + } + return *this; + } +}; + + +#ifdef RAR_SMP +class ThreadPool; +#endif + +struct blake2sp_state +{ + blake2s_state S[8]; + blake2s_state R; + byte buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + +#ifdef RAR_SMP + ThreadPool *ThPool; + uint MaxThreads; +#endif +}; + +void blake2sp_init( blake2sp_state *S ); +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ); +void blake2sp_final( blake2sp_state *S, byte *digest ); + +#endif + diff --git a/unrar/unrar/blake2s_sse.cpp b/unrar/unrar/blake2s_sse.cpp new file mode 100644 index 0000000..1a02f21 --- /dev/null +++ b/unrar/unrar/blake2s_sse.cpp @@ -0,0 +1,129 @@ +// Based on public domain code written in 2012 by Samuel Neves + +extern const byte blake2s_sigma[10][16]; + +// Initialization vector. +static __m128i blake2s_IV_0_3, blake2s_IV_4_7; + +#ifdef _WIN_64 +// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro. +static __m128i crotr8, crotr16; +#endif + +static void blake2s_init_sse() +{ + // We cannot initialize these 128 bit variables in place when declaring + // them globally, because global scope initialization is performed before + // our SSE check and it would make code incompatible with older non-SSE2 + // CPUs. Also we cannot initialize them as static inside of function + // using these variables, because SSE static initialization is not thread + // safe: first thread starts initialization and sets "init done" flag even + // if it is not done yet, second thread can attempt to access half-init + // SSE data. So we moved init code here. + + blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A ); + blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ); + +#ifdef _WIN_64 + crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); + crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); +#endif +} + + +#define LOAD(p) _mm_load_si128( (__m128i *)(p) ) +#define STORE(p,r) _mm_store_si128((__m128i *)(p), r) + +#ifdef _WIN_32 +// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient +// to not use _mm_shuffle_epi8 here. +#define mm_rotr_epi32(r, c) ( \ + _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#else +#define mm_rotr_epi32(r, c) ( \ + c==8 ? _mm_shuffle_epi8(r,crotr8) \ + : c==16 ? _mm_shuffle_epi8(r,crotr16) \ + : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#endif + + +#define G1(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 16); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 12); + +#define G2(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 8); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 7); + +#define DIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) ); + +#define UNDIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) ); + +#ifdef _WIN_64 + // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load + // from stack operations, which are slower than this code. + #define _mm_set_epi32(i3,i2,i1,i0) \ + _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \ + _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3))) +#endif + +// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode +// and about the same in x64 mode in our test. Perhaps depends on compiler. +// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather +// instructions here, but they did not show any speed gain on i7-6700K. +#define SSE_ROUND(m,row,r) \ +{ \ + __m128i buf; \ + buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + DIAGONALIZE(row[0],row[1],row[2],row[3]); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \ +} + + +static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + __m128i row[4]; + __m128i ff0, ff1; + + const uint32 *m = ( uint32 * )block; + + row[0] = ff0 = LOAD( &S->h[0] ); + row[1] = ff1 = LOAD( &S->h[4] ); + + row[2] = blake2s_IV_0_3; + row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) ); + SSE_ROUND( m, row, 0 ); + SSE_ROUND( m, row, 1 ); + SSE_ROUND( m, row, 2 ); + SSE_ROUND( m, row, 3 ); + SSE_ROUND( m, row, 4 ); + SSE_ROUND( m, row, 5 ); + SSE_ROUND( m, row, 6 ); + SSE_ROUND( m, row, 7 ); + SSE_ROUND( m, row, 8 ); + SSE_ROUND( m, row, 9 ); + STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) ); + STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) ); + return 0; +} diff --git a/unrar/unrar/blake2sp.cpp b/unrar/unrar/blake2sp.cpp new file mode 100644 index 0000000..da64588 --- /dev/null +++ b/unrar/unrar/blake2sp.cpp @@ -0,0 +1,153 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Written in 2012 by Samuel Neves <sneves@dei.uc.pt> + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along with + this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. +*/ + +#define PARALLELISM_DEGREE 8 + +void blake2sp_init( blake2sp_state *S ) +{ + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + + blake2s_init_param( &S->R, 0, 1 ); // Init root. + + for( uint i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_init_param( &S->S[i], i, 0 ); // Init leaf. + + S->R.last_node = 1; + S->S[PARALLELISM_DEGREE - 1].last_node = 1; +} + + +struct Blake2ThreadData +{ + void Update(); + blake2s_state *S; + const byte *in; + size_t inlen; +}; + + +void Blake2ThreadData::Update() +{ + size_t inlen__ = inlen; + const byte *in__ = ( const byte * )in; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { +#ifdef USE_SSE + // We gain 5% in i7 SSE mode by prefetching next data block. + if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES) + _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0); +#endif + blake2s_update( S, in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } +} + +#ifdef RAR_SMP +THREAD_PROC(Blake2Thread) +{ + Blake2ThreadData *td=(Blake2ThreadData *)Data; + td->Update(); +} +#endif + + +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ) +{ + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + + Blake2ThreadData btd_array[PARALLELISM_DEGREE]; + +#ifdef RAR_SMP + uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads; + + if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here. + ThreadNumber=4; +#else + uint ThreadNumber=1; +#endif + + for (size_t id__=0;id__<PARALLELISM_DEGREE;) + { + for (uint Thread=0;Thread<ThreadNumber && id__<PARALLELISM_DEGREE;Thread++) + { + Blake2ThreadData *btd=btd_array+Thread; + + btd->inlen = inlen; + btd->in = in + id__ * BLAKE2S_BLOCKBYTES; + btd->S = &S->S[id__]; + +#ifdef RAR_SMP + if (ThreadNumber>1) + S->ThPool->AddTask(Blake2Thread,(void*)btd); + else + btd->Update(); +#else + btd->Update(); +#endif + id__++; + } +#ifdef RAR_SMP + if (S->ThPool!=NULL) // Can be NULL in -mt1 mode. + S->ThPool->WaitDone(); +#endif // RAR_SMP + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, (size_t)inlen ); + + S->buflen = left + (size_t)inlen; +} + + +void blake2sp_final( blake2sp_state *S, byte *digest ) +{ + byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2S_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; + + if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; + + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); + } + + blake2s_final( &S->S[i], hash[i] ); + } + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES ); + + blake2s_final( &S->R, digest ); +} diff --git a/unrar/unrar/cmddata.cpp b/unrar/unrar/cmddata.cpp index 1f17cce..ffc5ff9 100644 --- a/unrar/unrar/cmddata.cpp +++ b/unrar/unrar/cmddata.cpp @@ -1,128 +1,159 @@ #include "rar.hpp" +#include "cmdfilter.cpp" +#include "cmdmix.cpp" + CommandData::CommandData() { - FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL; Init(); } -CommandData::~CommandData() -{ - Close(); -} - - void CommandData::Init() { - Close(); + RAROptions::Init(); *Command=0; *ArcName=0; - *ArcNameW=0; FileLists=false; NoMoreSwitches=false; - TimeConverted=false; - FileArgs=new StringList; - ExclArgs=new StringList; - InclArgs=new StringList; - StoreArgs=new StringList; - ArcNames=new StringList; + ListMode=RCLM_AUTO; + + BareOutput=false; + + + FileArgs.Reset(); + ExclArgs.Reset(); + InclArgs.Reset(); + ArcNames.Reset(); + StoreArgs.Reset(); + Password.Clean(); + NextVolSizes.clear(); } -void CommandData::Close() +// Return the pointer to next position in the string and store dynamically +// allocated command line parameter in Par. +static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par) { - delete FileArgs; - delete ExclArgs; - delete InclArgs; - delete StoreArgs; - delete ArcNames; - FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL; - NextVolSizes.Reset(); + const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0); + if (NextCmd==NULL) + return NULL; + size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero. + *Par=(wchar *)malloc(ParSize*sizeof(wchar)); + if (*Par==NULL) + return NULL; + return GetCmdParam(CmdLine,*Par,ParSize); +} + + +#if !defined(SFX_MODULE) +void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) +{ + *Command=0; + NoMoreSwitches=false; +#ifdef CUSTOM_CMDLINE_PARSER + // In Windows we may prefer to implement our own command line parser + // to avoid replacing \" by " in standard parser. Such replacing corrupts + // destination paths like "dest path\" in extraction commands. + const wchar *CmdLine=GetCommandLine(); + + wchar *Par; + for (bool FirstParam=true;;FirstParam=false) + { + if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL) + break; + if (!FirstParam) // First parameter is the executable name. + if (Preprocess) + PreprocessArg(Par); + else + ParseArg(Par); + free(Par); + } +#else + Array<wchar> Arg; + for (int I=1;I<argc;I++) + { + Arg.Alloc(strlen(argv[I])+1); + CharToWide(argv[I],&Arg[0],Arg.Size()); + if (Preprocess) + PreprocessArg(&Arg[0]); + else + ParseArg(&Arg[0]); + } +#endif + if (!Preprocess) + ParseDone(); } +#endif #if !defined(SFX_MODULE) -void CommandData::ParseArg(char *Arg,wchar *ArgW) +void CommandData::ParseArg(wchar *Arg) { if (IsSwitch(*Arg) && !NoMoreSwitches) - if (Arg[1]=='-') + if (Arg[1]=='-' && Arg[2]==0) NoMoreSwitches=true; else - ProcessSwitch(&Arg[1],(ArgW!=NULL && *ArgW!=0 ? &ArgW[1]:NULL)); + ProcessSwitch(Arg+1); else if (*Command==0) { - strncpyz(Command,Arg,ASIZE(Command)); - if (ArgW!=NULL) - strncpyw(CommandW,ArgW,sizeof(CommandW)/sizeof(CommandW[0])); - if (etoupper(*Command)=='S') + wcsncpyz(Command,Arg,ASIZE(Command)); + + + *Command=toupperw(*Command); + // 'I' and 'S' commands can contain case sensitive strings after + // the first character, so we must not modify their case. + // 'S' can contain SFX name, which case is important in Unix. + if (*Command!='I' && *Command!='S') + wcsupper(Command); + if (*Command=='P') // Enforce -idq for print command. { - const char *SFXName=Command[1] ? Command+1:DefSFXName; - if (PointToName(SFXName)!=SFXName || FileExist(SFXName)) - strcpy(SFXModule,SFXName); - else - GetConfigName(SFXName,SFXModule,true); + MsgStream=MSG_ERRONLY; + SetConsoleMsgStream(MSG_ERRONLY); } -#ifndef GUI - *Command=etoupper(*Command); - if (*Command!='I' && *Command!='S') - strupper(Command); -#endif } else if (*ArcName==0) - { - strncpyz(ArcName,Arg,ASIZE(ArcName)); - if (ArgW!=NULL) - strncpyzw(ArcNameW,ArgW,ASIZE(ArcNameW)); - } + wcsncpyz(ArcName,Arg,ASIZE(ArcName)); else { - size_t Length=strlen(Arg); - char EndChar=Length==0 ? 0:Arg[Length-1]; - char CmdChar=etoupper(*Command); - bool Add=strchr("AFUM",CmdChar)!=NULL; + // Check if last character is the path separator. + size_t Length=wcslen(Arg); + wchar EndChar=Length==0 ? 0:Arg[Length-1]; + bool EndSeparator=IsDriveDiv(EndChar) || IsPathDiv(EndChar); + + wchar CmdChar=toupperw(*Command); + bool Add=wcschr(L"AFUM",CmdChar)!=NULL; bool Extract=CmdChar=='X' || CmdChar=='E'; - if ((IsDriveDiv(EndChar) || IsPathDiv(EndChar)) && !Add) - { - strncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); - if (ArgW!=NULL) - strncpyzw(ExtrPathW,ArgW,ASIZE(ExtrPathW)); - } + bool Repair=CmdChar=='R' && Command[1]==0; + if (EndSeparator && !Add) + wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); else - if ((Add || CmdChar=='T') && *Arg!='@') - FileArgs->AddString(Arg); + if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS)) + FileArgs.AddString(Arg); else { - struct FindData FileData; - bool Found=FindFile::FastFind(Arg,NULL,&FileData); - if (!Found && *Arg=='@' && !IsWildcard(Arg)) + FindData FileData; + bool Found=FindFile::FastFind(Arg,&FileData); + if ((!Found || ListMode==RCLM_ACCEPT_LISTS) && + ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1)) { FileLists=true; - RAR_CHARSET Charset=FilelistCharset; + ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true); -#if defined(_WIN_32) && !defined(GUI) - // for compatibility reasons we use OEM encoding - // in Win32 console version by default - - if (Charset==RCH_DEFAULT) - Charset=RCH_OEM; -#endif - - ReadTextFile(Arg+1,FileArgs,false,true,Charset,true,true,true); } - else - if (Found && FileData.IsDir && Extract && *ExtrPath==0) + else // We use 'destpath\' when extracting and reparing. + if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0) { - strcpy(ExtrPath,Arg); - AddEndSlash(ExtrPath); + wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath)); + AddEndSlash(ExtrPath,ASIZE(ExtrPath)); } else - FileArgs->AddString(Arg); + FileArgs.AddString(Arg); } } } @@ -131,252 +162,181 @@ void CommandData::ParseArg(char *Arg,wchar *ArgW) void CommandData::ParseDone() { - if (FileArgs->ItemsCount()==0 && !FileLists) - FileArgs->AddString(MASKALL); - char CmdChar=etoupper(*Command); - bool Extract=CmdChar=='X' || CmdChar=='E'; + if (FileArgs.ItemsCount()==0 && !FileLists) + FileArgs.AddString(MASKALL); + wchar CmdChar=toupperw(Command[0]); + bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P'; if (Test && Extract) - Test=false; - BareOutput=(CmdChar=='L' || CmdChar=='V') && Command[1]=='B'; + Test=false; // Switch '-t' is senseless for 'X', 'E', 'P' commands. + + // Suppress the copyright message and final end of line for 'lb' and 'vb'. + if ((CmdChar=='L' || CmdChar=='V') && Command[1]=='B') + BareOutput=true; } -#if !defined(SFX_MODULE) && !defined(_WIN_CE) +#if !defined(SFX_MODULE) void CommandData::ParseEnvVar() { char *EnvStr=getenv("RAR"); if (EnvStr!=NULL) - ProcessSwitchesString(EnvStr); + { + Array<wchar> EnvStrW(strlen(EnvStr)+1); + CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size()); + ProcessSwitchesString(&EnvStrW[0]); + } } #endif -// return 'false' if -cfg- is present and preprocess switches -// which must be processed before the rest of command line - -#ifndef SFX_MODULE -bool CommandData::IsConfigEnabled(int argc,char *argv[]) +#if !defined(SFX_MODULE) +// Preprocess those parameters, which must be processed before the rest of +// command line. Return 'false' to stop further processing. +void CommandData::PreprocessArg(const wchar *Arg) { - bool ConfigEnabled=true; - for (int I=1;I<argc;I++) - if (IsSwitch(*argv[I])) + if (IsSwitch(Arg[0]) && !NoMoreSwitches) + { + Arg++; + if (Arg[0]=='-' && Arg[1]==0) // Switch "--". + NoMoreSwitches=true; + if (wcsicomp(Arg,L"cfg-")==0) + ConfigDisabled=true; + if (wcsnicomp(Arg,L"ilog",4)==0) { - if (stricomp(&argv[I][1],"cfg-")==0) - ConfigEnabled=false; -#ifndef GUI - if (strnicomp(&argv[I][1],"ilog",4)==0) - { - // ensure that correct log file name is already set - // if we need to report an error when processing the command line - ProcessSwitch(&argv[I][1]); - InitLogOptions(LogName); - } -#endif - if (strnicomp(&argv[I][1],"sc",2)==0) - { - // process -cs before reading any file lists - ProcessSwitch(&argv[I][1]); - } + // Ensure that correct log file name is already set + // if we need to report an error when processing the command line. + ProcessSwitch(Arg); + InitLogOptions(LogName,ErrlogCharset); + } + if (wcsnicomp(Arg,L"sc",2)==0) + { + // Process -sc before reading any file lists. + ProcessSwitch(Arg); + if (*LogName!=0) + InitLogOptions(LogName,ErrlogCharset); } - return(ConfigEnabled); + } + else + if (*Command==0) + wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini. } #endif -#if !defined(GUI) && !defined(SFX_MODULE) -void CommandData::ReadConfig(int argc,char *argv[]) +#if !defined(SFX_MODULE) +void CommandData::ReadConfig() { StringList List; if (ReadTextFile(DefConfigName,&List,true)) { - char *Str; + wchar *Str; while ((Str=List.GetString())!=NULL) { - while (isspace(*Str)) + while (IsSpace(*Str)) Str++; - if (strnicomp(Str,"switches=",9)==0) + if (wcsnicomp(Str,L"switches=",9)==0) ProcessSwitchesString(Str+9); + if (*Command!=0) + { + wchar Cmd[16]; + wcsncpyz(Cmd,Command,ASIZE(Cmd)); + wchar C0=toupperw(Cmd[0]); + wchar C1=toupperw(Cmd[1]); + if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V') + Cmd[1]=0; + if (C0=='R' && (C1=='R' || C1=='V')) + Cmd[2]=0; + wchar SwName[16+ASIZE(Cmd)]; + swprintf(SwName,ASIZE(SwName),L"switches_%ls=",Cmd); + size_t Length=wcslen(SwName); + if (wcsnicomp(Str,SwName,Length)==0) + ProcessSwitchesString(Str+Length); + } } } } #endif -#if !defined(SFX_MODULE) && !defined(_WIN_CE) -void CommandData::ProcessSwitchesString(char *Str) +#if !defined(SFX_MODULE) +void CommandData::ProcessSwitchesString(const wchar *Str) { - while (*Str) + wchar *Par; + while ((Str=AllocCmdParam(Str,&Par))!=NULL) { - while (!IsSwitch(*Str) && *Str!=0) - Str++; - if (*Str==0) - break; - char *Next=Str; - while (!(Next[0]==' ' && IsSwitch(Next[1])) && *Next!=0) - Next++; - char NextChar=*Next; - *Next=0; - ProcessSwitch(Str+1); - *Next=NextChar; - Str=Next; + if (IsSwitch(*Par)) + ProcessSwitch(Par+1); + free(Par); } } #endif #if !defined(SFX_MODULE) -void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) +void CommandData::ProcessSwitch(const wchar *Switch) { - switch(etoupper(Switch[0])) + switch(toupperw(Switch[0])) { - case 'I': - if (strnicomp(&Switch[1],"LOG",3)==0) - { - strncpyz(LogName,Switch[4] ? Switch+4:DefLogName,ASIZE(LogName)); - break; - } - if (stricomp(&Switch[1],"SND")==0) - { - Sound=true; - break; - } - if (stricomp(&Switch[1],"ERR")==0) - { - MsgStream=MSG_STDERR; - break; - } - if (strnicomp(&Switch[1],"EML",3)==0) - { - strncpyz(EmailTo,Switch[4] ? Switch+4:"@",ASIZE(EmailTo)); - EmailTo[sizeof(EmailTo)-1]=0; - break; - } - if (stricomp(&Switch[1],"NUL")==0) - { - MsgStream=MSG_NULL; - break; - } - if (etoupper(Switch[1])=='D') - { - for (int I=2;Switch[I]!=0;I++) - switch(etoupper(Switch[I])) - { - case 'Q': - MsgStream=MSG_ERRONLY; - break; - case 'C': - DisableCopyright=true; - break; - case 'D': - DisableDone=true; - break; - case 'P': - DisablePercentage=true; - break; - } - break; - } - if (stricomp(&Switch[1],"OFF")==0) - { - Shutdown=true; - break; - } - break; - case 'T': - switch(etoupper(Switch[1])) - { - case 'K': - ArcTime=ARCTIME_KEEP; - break; - case 'L': - ArcTime=ARCTIME_LATEST; - break; - case 'O': - FileTimeBefore.SetAgeText(Switch+2); - break; - case 'N': - FileTimeAfter.SetAgeText(Switch+2); - break; - case 'B': - FileTimeBefore.SetIsoText(Switch+2); - break; - case 'A': - FileTimeAfter.SetIsoText(Switch+2); - break; - case 'S': - { - EXTTIME_MODE Mode=EXTTIME_HIGH3; - bool CommonMode=Switch[2]>='0' && Switch[2]<='4'; - if (CommonMode) - Mode=(EXTTIME_MODE)(Switch[2]-'0'); - if (Switch[2]=='-') - Mode=EXTTIME_NONE; - if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0) - xmtime=xctime=xatime=Mode; - else - { - if (Switch[3]>='0' && Switch[3]<='4') - Mode=(EXTTIME_MODE)(Switch[3]-'0'); - if (Switch[3]=='-') - Mode=EXTTIME_NONE; - switch(etoupper(Switch[2])) - { - case 'M': - xmtime=Mode; - break; - case 'C': - xctime=Mode; - break; - case 'A': - xatime=Mode; - break; - case 'R': - xarctime=Mode; - break; - } - } - } - break; - case '-': - Test=false; - break; - case 0: - Test=true; - break; - default: - BadSwitch(Switch); - break; - } + case '@': + ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS; break; case 'A': - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'C': ClearArc=true; break; case 'D': - AppendArcNameToPath=true; + if (Switch[2]==0) + AppendArcNameToPath=APPENDARCNAME_DESTPATH; + else + if (Switch[2]=='1') + AppendArcNameToPath=APPENDARCNAME_OWNSUBDIR; + else + if (Switch[2]=='2') + AppendArcNameToPath=APPENDARCNAME_OWNDIR; break; +#ifndef SFX_MODULE case 'G': if (Switch[2]=='-' && Switch[3]==0) GenerateArcName=0; else + if (toupperw(Switch[2])=='F') + wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask)); + else + { + GenerateArcName=true; + wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); + } + break; +#endif + case 'I': + IgnoreGeneralAttr=true; + break; + case 'M': + switch(toupperw(Switch[2])) { - GenerateArcName=true; - strncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); + case 0: + case 'S': + ArcMetadata=ARCMETA_SAVE; + break; + case 'R': + ArcMetadata=ARCMETA_RESTORE; + break; + default: + BadSwitch(Switch); + break; } break; - case 'N': //reserved for archive name + case 'N': // Reserved for archive name. break; case 'O': AddArcOnly=true; break; case 'P': - strcpy(ArcPath,Switch+2); - if (SwitchW!=NULL && *SwitchW!=0) - strcpyw(ArcPathW,SwitchW+2); + wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath)); break; case 'S': SyncFiles=true; @@ -386,9 +346,24 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; } break; + case 'C': + if (Switch[2]==0) + switch(toupperw(Switch[1])) + { + case '-': + DisableComment=true; + break; + case 'U': + ConvertNames=NAMES_UPPERCASE; + break; + case 'L': + ConvertNames=NAMES_LOWERCASE; + break; + } + break; case 'D': if (Switch[2]==0) - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'S': DisableSortSolid=true; @@ -401,98 +376,8 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; } break; - case 'O': - switch(etoupper(Switch[1])) - { - case '+': - Overwrite=OVERWRITE_ALL; - break; - case '-': - Overwrite=OVERWRITE_NONE; - break; - case 0: - Overwrite=OVERWRITE_FORCE_ASK; - break; - case 'R': - Overwrite=OVERWRITE_AUTORENAME; - break; - case 'W': - ProcessOwners=true; - break; -#ifdef SAVE_LINKS - case 'L': - SaveLinks=true; - break; -#endif -#ifdef _WIN_32 - case 'S': - SaveStreams=true; - break; - case 'C': - SetCompressedAttr=true; - break; -#endif - default : - BadSwitch(Switch); - break; - } - break; - case 'R': - switch(etoupper(Switch[1])) - { - case 0: - Recurse=RECURSE_ALWAYS; - break; - case '-': - Recurse=0; - break; - case '0': - Recurse=RECURSE_WILDCARDS; - break; -#ifndef _WIN_CE - case 'I': - { - Priority=atoi(Switch+2); - char *ChPtr=strchr(Switch+2,':'); - if (ChPtr!=NULL) - { - SleepTime=atoi(ChPtr+1); - InitSystemOptions(SleepTime); - } - SetPriority(Priority); - } - break; -#endif - } - break; - case 'Y': - AllYes=true; - break; - case 'N': - case 'X': - if (Switch[1]!=0) - { - StringList *Args=etoupper(Switch[0])=='N' ? InclArgs:ExclArgs; - if (Switch[1]=='@' && !IsWildcard(Switch)) - { - RAR_CHARSET Charset=FilelistCharset; - -#if defined(_WIN_32) && !defined(GUI) - // for compatibility reasons we use OEM encoding - // in Win32 console version by default - - if (Charset==RCH_DEFAULT) - Charset=RCH_OEM; -#endif - - ReadTextFile(Switch+2,Args,false,true,Charset,true,true,true); - } - else - Args->AddString(Switch+1); - } - break; case 'E': - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'P': switch(Switch[2]) @@ -509,90 +394,186 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) case '3': ExclPath=EXCL_ABSPATH; break; + case '4': + wcsncpyz(ExclArcPath,Switch+3,ASIZE(ExclArcPath)); + break; } break; - case 'D': - ExclEmptyDir=true; - break; - case 'E': - ProcessEA=false; - break; - case 'N': - NoEndBlock=true; - break; default: if (Switch[1]=='+') { - InclFileAttr=GetExclAttr(&Switch[2]); + InclFileAttr|=GetExclAttr(Switch+2,InclDir); InclAttrSet=true; } else - ExclFileAttr=GetExclAttr(&Switch[1]); + ExclFileAttr|=GetExclAttr(Switch+1,ExclDir); break; } break; - case 'P': + case 'F': if (Switch[1]==0) - { - GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password)); - eprintf("\n"); - } + FreshFiles=true; else - strncpyz(Password,Switch+1,ASIZE(Password)); + BadSwitch(Switch); break; case 'H': - if (etoupper(Switch[1])=='P') + switch (toupperw(Switch[1])) { - EncryptHeaders=true; - if (Switch[2]!=0) - strncpyz(Password,Switch+2,ASIZE(Password)); - else - if (*Password==0) + case 'P': + EncryptHeaders=true; + if (Switch[2]!=0) { - GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password)); - eprintf("\n"); + if (wcslen(Switch+2)>=MAXPASSWORD) + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); + Password.Set(Switch+2); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); } + else + if (!Password.IsSet()) + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL); + eprintf(L"\n"); + } + break; + default : + BadSwitch(Switch); + break; } break; - case 'Z': - strncpyz(CommentFile,Switch[1]!=0 ? Switch+1:"stdin",ASIZE(CommentFile)); + case 'I': + if (wcsnicomp(Switch+1,L"LOG",3)==0) + { + wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName)); + break; + } + if (wcsnicomp(Switch+1,L"SND",3)==0) + { + Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON; + break; + } + if (wcsicomp(Switch+1,L"ERR")==0) + { + MsgStream=MSG_STDERR; + // Set it immediately when parsing the command line, so it also + // affects messages issued while parsing the command line. + SetConsoleMsgStream(MSG_STDERR); + break; + } + if (wcsnicomp(Switch+1,L"EML",3)==0) + { + wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); + break; + } + if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv. + { + VerboseOutput=true; + break; + } + if (wcsicomp(Switch+1,L"NUL")==0) + { + MsgStream=MSG_NULL; + SetConsoleMsgStream(MSG_NULL); + break; + } + if (toupperw(Switch[1])=='D') + { + for (uint I=2;Switch[I]!=0;I++) + switch(toupperw(Switch[I])) + { + case 'Q': + MsgStream=MSG_ERRONLY; + SetConsoleMsgStream(MSG_ERRONLY); + break; + case 'C': + DisableCopyright=true; + break; + case 'D': + DisableDone=true; + break; + case 'P': + DisablePercentage=true; + break; + case 'N': + DisableNames=true; + break; + case 'V': + VerboseOutput=true; + break; + } + break; + } + if (wcsnicomp(Switch+1,L"OFF",3)==0) + { + switch(Switch[4]) + { + case 0: + case '1': + Shutdown=POWERMODE_OFF; + break; + case '2': + Shutdown=POWERMODE_HIBERNATE; + break; + case '3': + Shutdown=POWERMODE_SLEEP; + break; + case '4': + Shutdown=POWERMODE_RESTART; + break; + } + break; + } + if (wcsicomp(Switch+1,L"VER")==0) + { + PrintVersion=true; + break; + } + break; + case 'K': + switch(toupperw(Switch[1])) + { + case 'B': + KeepBroken=true; + break; + case 0: + Lock=true; + break; + } break; case 'M': - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 'C': { - char *Str=Switch+2; + const wchar *Str=Switch+2; if (*Str=='-') - for (int I=0;I<sizeof(FilterModes)/sizeof(FilterModes[0]);I++) + for (uint I=0;I<ASIZE(FilterModes);I++) FilterModes[I].State=FILTER_DISABLE; else - while (*Str) + while (*Str!=0) { int Param1=0,Param2=0; FilterState State=FILTER_AUTO; FilterType Type=FILTER_NONE; - if (isdigit(*Str)) + if (IsDigit(*Str)) { - Param1=atoi(Str); - while (isdigit(*Str)) + Param1=atoiw(Str); + while (IsDigit(*Str)) Str++; } - if (*Str==':' && isdigit(Str[1])) + if (*Str==':' && IsDigit(Str[1])) { - Param2=atoi(++Str); - while (isdigit(*Str)) + Param2=atoiw(++Str); + while (IsDigit(*Str)) Str++; } - switch(etoupper(*(Str++))) + switch(toupperw(*(Str++))) { case 'T': Type=FILTER_PPM; break; case 'E': Type=FILTER_E8; break; case 'D': Type=FILTER_DELTA; break; case 'A': Type=FILTER_AUDIO; break; case 'C': Type=FILTER_RGB; break; - case 'I': Type=FILTER_ITANIUM; break; - case 'L': Type=FILTER_UPCASETOLOW; break; + case 'R': Type=FILTER_ARM; break; } if (*Str=='+' || *Str=='-') State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE; @@ -605,46 +586,39 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) case 'M': break; case 'D': - { - if ((WinSize=atoi(&Switch[2]))==0) - WinSize=0x10000<<(etoupper(Switch[2])-'A'); - else - WinSize*=1024; - if (!CheckWinSize()) - BadSwitch(Switch); - } + break; + case 'E': + if (toupperw(Switch[2])=='S' && Switch[3]==0) + SkipEncrypted=true; break; case 'S': { - char *Names=Switch+2,DefNames[512]; - if (*Names==0) - { - strcpy(DefNames,DefaultStoreList); - Names=DefNames; - } + wchar StoreNames[1024]; + wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames)); + wchar *Names=StoreNames; while (*Names!=0) { - char *End=strchr(Names,';'); + wchar *End=wcschr(Names,';'); if (End!=NULL) *End=0; if (*Names=='.') Names++; - char Mask[NM]; - if (strpbrk(Names,"*?.")==NULL) - sprintf(Mask,"*.%s",Names); + wchar Mask[NM]; + if (wcspbrk(Names,L"*?.")==NULL) + swprintf(Mask,ASIZE(Mask),L"*.%ls",Names); else - strcpy(Mask,Names); - StoreArgs->AddString(Mask); + wcsncpyz(Mask,Names,ASIZE(Mask)); + StoreArgs.AddString(Mask); if (End==NULL) break; Names=End+1; } } break; -#ifdef PACK_SMP +#ifdef RAR_SMP case 'T': - Threads=atoi(Switch+2); - if (Threads>MaxSearchThreads) + Threads=atoiw(Switch+2); + if (Threads>MaxPoolThreads || Threads<1) BadSwitch(Switch); else { @@ -658,121 +632,146 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) break; } break; - case 'V': - switch(etoupper(Switch[1])) + case 'N': + case 'X': + if (Switch[1]!=0) { -#ifdef _WIN_32 - case 'D': - EraseDisk=true; + StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs; + if (Switch[1]=='@' && !IsWildcard(Switch)) + ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true); + else + Args->AddString(Switch+1); + } + break; + case 'O': + switch(toupperw(Switch[1])) + { + case '+': + Overwrite=OVERWRITE_ALL; + break; + case '-': + Overwrite=OVERWRITE_NONE; + break; + case 0: + Overwrite=OVERWRITE_FORCE_ASK; + break; +#ifdef _WIN_ALL + case 'C': + SetCompressedAttr=true; break; #endif + case 'H': + SaveHardLinks=true; + break; + + +#ifdef SAVE_LINKS + case 'L': + SaveSymLinks=true; + if (toupperw(Switch[2])=='A') + AbsoluteLinks=true; + break; +#endif +#ifdef _WIN_ALL case 'N': - OldNumbering=true; + if (toupperw(Switch[2])=='I') + AllowIncompatNames=true; break; +#endif case 'P': - VolumePause=true; + wcsncpyz(ExtrPath,Switch+2,ASIZE(ExtrPath)); + AddEndSlash(ExtrPath,ASIZE(ExtrPath)); break; - case 'E': - if (etoupper(Switch[2])=='R') - VersionControl=atoi(Switch+3)+1; + case 'R': + Overwrite=OVERWRITE_AUTORENAME; break; - case '-': - VolSize=0; +#ifdef _WIN_ALL + case 'S': + SaveStreams=true; break; - default: - { - Int64 NewVolSize=atoil(&Switch[1]); - - if (NewVolSize==0) - NewVolSize=INT64ERR; - else - switch (Switch[strlen(Switch)-1]) - { - case 'f': - case 'F': - switch(int64to32(NewVolSize)) - { - case 360: - NewVolSize=362496; - break; - case 720: - NewVolSize=730112; - break; - case 1200: - NewVolSize=1213952; - break; - case 1440: - NewVolSize=1457664; - break; - case 2880: - NewVolSize=2915328; - break; - } - break; - case 'k': - NewVolSize*=1024; - break; - case 'm': - NewVolSize*=1024*1024; - break; - case 'M': - NewVolSize*=1000*1000; - break; - case 'g': - NewVolSize*=1024*1024; - NewVolSize*=1024; - break; - case 'G': - NewVolSize*=1000*1000; - NewVolSize*=1000; - break; - case 'b': - case 'B': - break; - default: - NewVolSize*=1000; - break; - } - if (VolSize==0) - VolSize=NewVolSize; - else - NextVolSizes.Push(NewVolSize); - } +#endif + case 'W': + ProcessOwners=true; + break; + default : + BadSwitch(Switch); break; } break; - case 'F': + case 'P': if (Switch[1]==0) - FreshFiles=true; + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password,NULL); + eprintf(L"\n"); + } else - BadSwitch(Switch); + { + if (wcslen(Switch+1)>=MAXPASSWORD) + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); + Password.Set(Switch+1); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); + } break; - case 'U': - if (Switch[1]==0) - UpdateFiles=true; +#ifndef SFX_MODULE + case 'Q': + if (toupperw(Switch[1])=='O') + switch(toupperw(Switch[2])) + { + case 0: + QOpenMode=QOPEN_AUTO; + break; + case '-': + QOpenMode=QOPEN_NONE; + break; + case '+': + QOpenMode=QOPEN_ALWAYS; + break; + default: + BadSwitch(Switch); + break; + } else BadSwitch(Switch); break; - case 'W': - strncpyz(TempPath,&Switch[1],ASIZE(TempPath)); - AddEndSlash(TempPath); - break; - case 'S': - if (strnicomp(Switch,"SFX",3)==0) +#endif + case 'R': + switch(toupperw(Switch[1])) { - const char *SFXName=Switch[3] ? Switch+3:DefSFXName; - if (PointToName(SFXName)!=SFXName || FileExist(SFXName)) - strcpy(SFXModule,SFXName); - else - GetConfigName(SFXName,SFXModule,true); + case 0: + Recurse=RECURSE_ALWAYS; + break; + case '-': + Recurse=RECURSE_DISABLE; + break; + case '0': + Recurse=RECURSE_WILDCARDS; + break; + case 'I': + { + Priority=atoiw(Switch+2); + if (Priority<0 || Priority>15) + BadSwitch(Switch); + const wchar *ChPtr=wcschr(Switch+2,':'); + if (ChPtr!=NULL) + { + SleepTime=atoiw(ChPtr+1); + if (SleepTime>1000) + BadSwitch(Switch); + InitSystemOptions(SleepTime); + } + SetPriority(Priority); + } + break; } - if (isdigit(Switch[1])) + break; + case 'S': + if (IsDigit(Switch[1])) { Solid|=SOLID_COUNT; - SolidCount=atoi(&Switch[1]); + SolidCount=atoiw(&Switch[1]); } else - switch(etoupper(Switch[1])) + switch(toupperw(Switch[1])) { case 0: Solid|=SOLID_NORMAL; @@ -789,18 +788,24 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) case 'D': Solid|=SOLID_VOLUME_DEPENDENT; break; + case 'I': + ProhibitConsoleInput(); + wcsncpyz(UseStdin,Switch[2] ? Switch+2:L"stdin",ASIZE(UseStdin)); + break; case 'L': - if (isdigit(Switch[2])) - FileSizeLess=atoil(Switch+2); + if (IsDigit(Switch[2])) + FileSizeLess=atoilw(Switch+2); break; case 'M': - if (isdigit(Switch[2])) - FileSizeMore=atoil(Switch+2); + if (IsDigit(Switch[2])) + FileSizeMore=atoilw(Switch+2); break; case 'C': { + bool AlreadyBad=false; // Avoid reporting "bad switch" several times. + RAR_CHARSET rch=RCH_DEFAULT; - switch(etoupper(Switch[2])) + switch(toupperw(Switch[2])) { case 'A': rch=RCH_ANSI; @@ -811,62 +816,121 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) case 'U': rch=RCH_UNICODE; break; + case 'F': + rch=RCH_UTF8; + break; default : BadSwitch(Switch); + AlreadyBad=true; break; }; - if (Switch[3]==0) - CommentCharset=FilelistCharset=rch; - else - for (int I=3;Switch[I]!=0;I++) - switch(etoupper(Switch[I])) - { - case 'C': - CommentCharset=rch; - break; - case 'L': - FilelistCharset=rch; - break; - default: - BadSwitch(Switch); - break; - } + if (!AlreadyBad) + if (Switch[3]==0) + CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch; + else + for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++) + switch(toupperw(Switch[I])) + { + case 'C': + CommentCharset=rch; + break; + case 'L': + FilelistCharset=rch; + break; + case 'R': + RedirectCharset=rch; + break; + default: + BadSwitch(Switch); + AlreadyBad=true; + break; + } + // Set it immediately when parsing the command line, so it also + // affects messages issued while parsing the command line. + SetConsoleRedirectCharset(RedirectCharset); } break; } break; - case 'C': - if (Switch[2]==0) - switch(etoupper(Switch[1])) - { - case '-': - DisableComment=true; - break; - case 'U': - ConvertNames=NAMES_UPPERCASE; - break; - case 'L': - ConvertNames=NAMES_LOWERCASE; - break; - } - break; - case 'K': - switch(etoupper(Switch[1])) + case 'T': + switch(toupperw(Switch[1])) { + case 'K': + ArcTime=ARCTIME_KEEP; + break; + case 'L': + ArcTime=ARCTIME_LATEST; + break; + case 'O': + SetTimeFilters(Switch+2,true,true); + break; + case 'N': + SetTimeFilters(Switch+2,false,true); + break; case 'B': - KeepBroken=true; + SetTimeFilters(Switch+2,true,false); + break; + case 'A': + SetTimeFilters(Switch+2,false,false); + break; + case 'S': + SetStoreTimeMode(Switch+2); + break; + case '-': + Test=false; break; case 0: - Lock=true; + Test=true; + break; + default: + BadSwitch(Switch); + break; + } + break; + case 'U': + if (Switch[1]==0) + UpdateFiles=true; + else + BadSwitch(Switch); + break; + case 'V': + switch(toupperw(Switch[1])) + { + case 'P': + VolumePause=true; break; + case 'E': + if (toupperw(Switch[2])=='R') + VersionControl=atoiw(Switch+3)+1; + break; + case '-': + VolSize=0; + break; + default: + VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command. + break; + } + break; + case 'W': + wcsncpyz(TempPath,Switch+1,ASIZE(TempPath)); + AddEndSlash(TempPath,ASIZE(TempPath)); + break; + case 'Y': + AllYes=true; + break; + case 'Z': + if (Switch[1]==0) + { + // If comment file is not specified, we read data from stdin. + wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile)); } + else + wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile)); break; -#ifndef GUI case '?' : - OutHelp(); + OutHelp(RARX_SUCCESS); break; -#endif default : BadSwitch(Switch); break; @@ -875,320 +939,60 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW) #endif -#ifndef SFX_MODULE -void CommandData::BadSwitch(char *Switch) +#if !defined(SFX_MODULE) +void CommandData::BadSwitch(const wchar *Switch) { mprintf(St(MUnknownOption),Switch); - ErrHandler.Exit(USER_ERROR); + mprintf(L"\n"); + ErrHandler.Exit(RARX_USERERROR); } #endif -#ifndef GUI -void CommandData::OutTitle() +void CommandData::ProcessCommand() { - if (BareOutput || DisableCopyright) - return; -#if defined(__GNUC__) && defined(SFX_MODULE) - mprintf(St(MCopyrightS)); -#else -#ifndef SILENT - static bool TitleShown=false; - if (TitleShown) - return; - TitleShown=true; - char Version[50]; - int Beta=RARVER_BETA; - if (Beta!=0) - sprintf(Version,"%d.%02d %s %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); - else - sprintf(Version,"%d.%02d",RARVER_MAJOR,RARVER_MINOR); -#ifdef UNRAR - mprintf(St(MUCopyright),Version,RARVER_YEAR); -#else -#endif -#endif -#endif -} -#endif - +#ifndef SFX_MODULE -inline bool CmpMSGID(MSGID i1,MSGID i2) -{ -#ifdef MSGID_INT - return(i1==i2); -#else - // If MSGID is const char*, we cannot compare pointers only. - // Pointers to different instances of same strings can differ, - // so we need to compare complete strings. - return(strcmp(i1,i2)==0); -#endif -} + const wchar *SingleCharCommands=L"FUADPXETK"; + if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0) + OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. -void CommandData::OutHelp() -{ -#if !defined(GUI) && !defined(SILENT) - OutTitle(); - static MSGID Help[]={ -#ifdef SFX_MODULE - MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV -#elif defined(UNRAR) - MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, - MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw, - MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAP, - MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU, - MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, - MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, - MCHelpSwO,MCHelpSwOC,MCHelpSwOR,MCHelpSwOW,MCHelpSwP, - MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSL,MCHelpSwSM,MCHelpSwTA, - MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr, - MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY + const wchar *ArcExt=GetExt(ArcName); +#ifdef _UNIX + if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #else - MRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdA,MCHelpCmdC,MCHelpCmdCF, - MCHelpCmdCH,MCHelpCmdCW,MCHelpCmdD,MCHelpCmdE,MCHelpCmdF,MCHelpCmdI, - MCHelpCmdK,MCHelpCmdL,MCHelpCmdM,MCHelpCmdP,MCHelpCmdR,MCHelpCmdRC, - MCHelpCmdRN,MCHelpCmdRR,MCHelpCmdRV,MCHelpCmdS,MCHelpCmdT,MCHelpCmdU, - MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG, - MCHelpSwAO,MCHelpSwAP,MCHelpSwAS,MCHelpSwAV,MCHelpSwAVm,MCHelpSwCm, - MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDF,MCHelpSwDH,MCHelpSwDR, - MCHelpSwDS,MCHelpSwDW,MCHelpSwEa,MCHelpSwED,MCHelpSwEE,MCHelpSwEN, - MCHelpSwEP,MCHelpSwEP1,MCHelpSwEP2,MCHelpSwEP3,MCHelpSwF,MCHelpSwHP, - MCHelpSwIDP,MCHelpSwIEML,MCHelpSwIERR,MCHelpSwILOG,MCHelpSwINUL, - MCHelpSwIOFF,MCHelpSwISND,MCHelpSwK,MCHelpSwKB,MCHelpSwMn,MCHelpSwMC, - MCHelpSwMD,MCHelpSwMS,MCHelpSwMT,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, - MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOS,MCHelpSwOW, - MCHelpSwP,MCHelpSwPm,MCHelpSwR,MCHelpSwR0,MCHelpSwRI,MCHelpSwRR, - MCHelpSwRV,MCHelpSwS,MCHelpSwSm,MCHelpSwSC,MCHelpSwSFX,MCHelpSwSI, - MCHelpSwSL,MCHelpSwSM,MCHelpSwT,MCHelpSwTA,MCHelpSwTB,MCHelpSwTK, - MCHelpSwTL,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwV, - MCHelpSwVn,MCHelpSwVD,MCHelpSwVER,MCHelpSwVN,MCHelpSwVP,MCHelpSwW, - MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY,MCHelpSwZ + if (ArcExt==NULL) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); #endif - }; - - for (int I=0;I<sizeof(Help)/sizeof(Help[0]);I++) + // Treat arcname.part1 as arcname.part1.rar. + if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) && + !FileExist(ArcName)) { -#ifndef SFX_MODULE -#ifdef DISABLEAUTODETECT - if (Help[I]==MCHelpSwV) - continue; -#endif -#ifndef _WIN_32 - static MSGID Win32Only[]={ - MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF, - MCHelpSwEP2,MCHelpSwOC,MCHelpSwDR,MCHelpSwRI - }; - bool Found=false; - for (int J=0;J<sizeof(Win32Only)/sizeof(Win32Only[0]);J++) - if (CmpMSGID(Help[I],Win32Only[J])) - { - Found=true; - break; - } - if (Found) - continue; -#endif -#if !defined(_UNIX) && !defined(_WIN_32) - if (CmpMSGID(Help[I],MCHelpSwOW)) - continue; -#endif -#if !defined(_WIN_32) && !defined(_EMX) - if (CmpMSGID(Help[I],MCHelpSwAC)) - continue; -#endif -#ifndef SAVE_LINKS - if (CmpMSGID(Help[I],MCHelpSwOL)) - continue; -#endif -#ifndef PACK_SMP - if (CmpMSGID(Help[I],MCHelpSwMT)) - continue; -#endif -#ifndef _BEOS - if (CmpMSGID(Help[I],MCHelpSwEE)) - { -#if defined(_EMX) && !defined(_DJGPP) - if (_osmode != OS2_MODE) - continue; -#else - continue; -#endif - } -#endif -#endif - mprintf(St(Help[I])); + wchar Name[NM]; + wcsncpyz(Name,ArcName,ASIZE(Name)); + wcsncatz(Name,L".rar",ASIZE(Name)); + if (FileExist(Name)) + wcsncpyz(ArcName,Name,ASIZE(ArcName)); } - mprintf("\n"); - ErrHandler.Exit(USER_ERROR); -#endif -} - -bool CommandData::ExclCheckArgs(StringList *Args,char *CheckName,bool CheckFullPath,int MatchMode) -{ - char *Name=ConvertPath(CheckName,NULL); - char FullName[NM],*CurName; - *FullName=0; - Args->Rewind(); - while ((CurName=Args->GetString())!=NULL) -#ifndef SFX_MODULE - if (CheckFullPath && IsFullPath(CurName)) - { - if (*FullName==0) - ConvertNameToFull(CheckName,FullName); - if (CmpName(CurName,FullName,MatchMode)) - return(true); - } - else -#endif - if (CmpName(ConvertPath(CurName,NULL),Name,MatchMode)) - return(true); - return(false); -} - - -bool CommandData::ExclCheck(char *CheckName,bool CheckFullPath) -{ - if (ExclCheckArgs(ExclArgs,CheckName,CheckFullPath,MATCH_WILDSUBPATH)) - return(true); - if (InclArgs->ItemsCount()==0) - return(false); - if (ExclCheckArgs(InclArgs,CheckName,false,MATCH_WILDSUBPATH)) - return(false); - return(true); -} - - - - -#ifndef SFX_MODULE -bool CommandData::TimeCheck(RarTime &ft) -{ - if (FileTimeBefore.IsSet() && ft>=FileTimeBefore) - return(true); - if (FileTimeAfter.IsSet() && ft<=FileTimeAfter) - return(true); -/* - if (FileTimeOlder!=0 || FileTimeNewer!=0) + if (wcschr(L"AFUMD",*Command)==NULL && *UseStdin==0) { - if (!TimeConverted) + if (GenerateArcName) { - if (FileTimeOlder!=0) - FileTimeOlder=SecondsToDosTime(FileTimeOlder); - if (FileTimeNewer!=0) - FileTimeNewer=SecondsToDosTime(FileTimeNewer); - TimeConverted=true; + const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask; + GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false); } - if (FileTimeOlder!=0 && ft>=FileTimeOlder) - return(true); - if (FileTimeNewer!=0 && ft<=FileTimeNewer) - return(true); - } -*/ - return(false); -} -#endif - - -#ifndef SFX_MODULE -bool CommandData::SizeCheck(Int64 Size) -{ - if (FileSizeLess!=INT64ERR && Size>=FileSizeLess) - return(true); - if (FileSizeMore!=INT64ERR && Size<=FileSizeMore) - return(true); - return(false); -} -#endif - - -int CommandData::IsProcessFile(FileHeader &NewLhd,bool *ExactMatch,int MatchType) -{ - if (strlen(NewLhd.FileName)>=NM || strlenw(NewLhd.FileNameW)>=NM) - return(0); - if (ExclCheck(NewLhd.FileName,false)) - return(0); -#ifndef SFX_MODULE - if (TimeCheck(NewLhd.mtime)) - return(0); - if ((NewLhd.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (NewLhd.FileAttr & InclFileAttr)==0) - return(0); - if ((NewLhd.Flags & LHD_WINDOWMASK)!=LHD_DIRECTORY && SizeCheck(NewLhd.FullUnpSize)) - return(0); -#endif - char *ArgName; - wchar *ArgNameW; - FileArgs->Rewind(); - for (int StringCount=1;FileArgs->GetString(&ArgName,&ArgNameW);StringCount++) - { -#ifndef SFX_MODULE - bool Unicode=(NewLhd.Flags & LHD_UNICODE) || ArgNameW!=NULL; - if (Unicode) - { - wchar NameW[NM],ArgW[NM],*NamePtr=NewLhd.FileNameW; - bool CorrectUnicode=true; - if (ArgNameW==NULL) - { - if (!CharToWide(ArgName,ArgW) || *ArgW==0) - CorrectUnicode=false; - ArgNameW=ArgW; - } - if ((NewLhd.Flags & LHD_UNICODE)==0) - { - if (!CharToWide(NewLhd.FileName,NameW) || *NameW==0) - CorrectUnicode=false; - NamePtr=NameW; - } - if (CmpName(ArgNameW,NamePtr,MatchType)) - { - if (ExactMatch!=NULL) - *ExactMatch=stricompcw(ArgNameW,NamePtr)==0; - return(StringCount); - } - if (CorrectUnicode) - continue; - } -#endif - if (CmpName(ArgName,NewLhd.FileName,MatchType)) - { - if (ExactMatch!=NULL) - *ExactMatch=stricompc(ArgName,NewLhd.FileName)==0; - return(StringCount); - } - } - return(0); -} - - -#ifndef GUI -void CommandData::ProcessCommand() -{ -#ifndef SFX_MODULE - - const char *SingleCharCommands="FUADPXETK"; - if (Command[1] && strchr(SingleCharCommands,*Command)!=NULL || *ArcName==0) - OutHelp(); - -#ifdef _UNIX - if (GetExt(ArcName)==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) - strcat(ArcName,".rar"); -#else - if (GetExt(ArcName)==NULL) - strcat(ArcName,".rar"); -#endif - - if (strchr("AFUMD",*Command)==NULL) - { StringList ArcMasks; ArcMasks.AddString(ArcName); - ScanTree Scan(&ArcMasks,Recurse,SaveLinks,SCAN_SKIPDIRS); + ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS); FindData FindData; while (Scan.GetNext(&FindData)==SCAN_SUCCESS) - AddArcName(FindData.Name,FindData.NameW); + AddArcName(FindData.Name); } else - AddArcName(ArcName,NULL); + AddArcName(ArcName); #endif switch(Command[0]) @@ -1197,10 +1001,9 @@ void CommandData::ProcessCommand() case 'X': case 'E': case 'T': - case 'I': { - CmdExtract Extract; - Extract.DoExtract(this); + CmdExtract Extract(this); + Extract.DoExtract(); } break; #ifndef SILENT @@ -1209,77 +1012,72 @@ void CommandData::ProcessCommand() ListArchive(this); break; default: - OutHelp(); + OutHelp(RARX_USERERROR); #endif } if (!BareOutput) - mprintf("\n"); + mprintf(L"\n"); } -#endif -void CommandData::AddArcName(char *Name,wchar *NameW) +void CommandData::AddArcName(const wchar *Name) { - ArcNames->AddString(Name,NameW); + ArcNames.AddString(Name); } -bool CommandData::GetArcName(char *Name,wchar *NameW,int MaxSize) +bool CommandData::GetArcName(wchar *Name,int MaxSize) { - if (!ArcNames->GetString(Name,NameW,NM)) - return(false); - return(true); + return ArcNames.GetString(Name,MaxSize); } bool CommandData::IsSwitch(int Ch) { -#if defined(_WIN_32) || defined(_EMX) - return(Ch=='-' || Ch=='/'); +#if defined(_WIN_ALL) || defined(_EMX) + return Ch=='-' || Ch=='/'; #else - return(Ch=='-'); + return Ch=='-'; #endif } #ifndef SFX_MODULE -uint CommandData::GetExclAttr(char *Str) +uint CommandData::GetExclAttr(const wchar *Str,bool &Dir) { - if (isdigit(*Str)) - return(strtol(Str,NULL,0)); - else + if (IsDigit(*Str)) + return wcstol(Str,NULL,0); + + uint Attr=0; + while (*Str!=0) { - uint Attr; - for (Attr=0;*Str;Str++) - switch(etoupper(*Str)) - { + switch(toupperw(*Str)) + { + case 'D': + Dir=true; + break; #ifdef _UNIX - case 'D': - Attr|=S_IFDIR; - break; - case 'V': - Attr|=S_IFCHR; - break; -#elif defined(_WIN_32) || defined(_EMX) - case 'R': - Attr|=0x1; - break; - case 'H': - Attr|=0x2; - break; - case 'S': - Attr|=0x4; - break; - case 'D': - Attr|=0x10; - break; - case 'A': - Attr|=0x20; - break; + case 'V': + Attr|=S_IFCHR; + break; +#elif defined(_WIN_ALL) || defined(_EMX) + case 'R': + Attr|=0x1; + break; + case 'H': + Attr|=0x2; + break; + case 'S': + Attr|=0x4; + break; + case 'A': + Attr|=0x20; + break; #endif - } - return(Attr); + } + Str++; } + return Attr; } #endif @@ -1289,13 +1087,44 @@ uint CommandData::GetExclAttr(char *Str) #ifndef SFX_MODULE bool CommandData::CheckWinSize() { - static int ValidSize[]={ - 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000 - }; - for (int I=0;I<sizeof(ValidSize)/sizeof(ValidSize[0]);I++) - if (WinSize==ValidSize[I]) - return(true); + // Define 0x100000000 as macro to avoid troubles with older compilers. + const uint64 MaxDictSize=INT32TO64(1,0); + // Limit the dictionary size to 4 GB. + for (uint64 I=0x10000;I<=MaxDictSize;I*=2) + if (WinSize==I) + return true; WinSize=0x400000; - return(false); + return false; +} +#endif + + +#ifndef SFX_MODULE +void CommandData::ReportWrongSwitches(RARFORMAT Format) +{ + if (Format==RARFMT15) + { + if (HashType!=HASH_CRC32) + uiMsg(UIERROR_INCOMPATSWITCH,L"-ht",4); +#ifdef _WIN_ALL + if (SaveSymLinks) + uiMsg(UIERROR_INCOMPATSWITCH,L"-ol",4); +#endif + if (SaveHardLinks) + uiMsg(UIERROR_INCOMPATSWITCH,L"-oh",4); + +#ifdef _WIN_ALL + // Do not report a wrong dictionary size here, because we are not sure + // yet about archive format. We can switch to RAR5 mode later + // if we update RAR5 archive. + + +#endif + if (QOpenMode!=QOPEN_AUTO) + uiMsg(UIERROR_INCOMPATSWITCH,L"-qo",4); + } + if (Format==RARFMT50) + { + } } #endif diff --git a/unrar/unrar/cmddata.hpp b/unrar/unrar/cmddata.hpp index 64a336d..0feb404 100644 --- a/unrar/unrar/cmddata.hpp +++ b/unrar/unrar/cmddata.hpp @@ -1,57 +1,74 @@ #ifndef _RAR_CMDDATA_ #define _RAR_CMDDATA_ -#define DefaultStoreList "7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lzh;mp3;rar;taz;tgz;z;zip" + +#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tbz;tbz2;tgz;txz;xz;z;zip;zipx;zst;tzst" + +enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS}; + +enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1}; class CommandData:public RAROptions { private: - void ProcessSwitchesString(char *Str); - void ProcessSwitch(char *Switch,wchar *SwitchW=NULL); - void BadSwitch(char *Switch); - bool ExclCheckArgs(StringList *Args,char *CheckName,bool CheckFullPath,int MatchMode); - uint GetExclAttr(char *Str); + void ProcessSwitch(const wchar *Switch); + void BadSwitch(const wchar *Switch); + uint GetExclAttr(const wchar *Str,bool &Dir); +#if !defined(SFX_MODULE) + void SetTimeFilters(const wchar *Mod,bool Before,bool Age); + void SetStoreTimeMode(const wchar *S); +#endif bool FileLists; bool NoMoreSwitches; - bool TimeConverted; + RAR_CMD_LIST_MODE ListMode; bool BareOutput; public: CommandData(); - ~CommandData(); void Init(); - void Close(); - void ParseArg(char *Arg,wchar *ArgW); + + void ParseCommandLine(bool Preprocess,int argc, char *argv[]); + void ParseArg(wchar *ArgW); void ParseDone(); void ParseEnvVar(); - void ReadConfig(int argc,char *argv[]); - bool IsConfigEnabled(int argc,char *argv[]); + void ReadConfig(); + void PreprocessArg(const wchar *Arg); + void ProcessSwitchesString(const wchar *Str); void OutTitle(); - void OutHelp(); + void OutHelp(RAR_EXIT ExitCode); bool IsSwitch(int Ch); - bool ExclCheck(char *CheckName,bool CheckFullPath); - bool StoreCheck(char *CheckName); - bool TimeCheck(RarTime &ft); - bool SizeCheck(Int64 Size); - int IsProcessFile(FileHeader &NewLhd,bool *ExactMatch=NULL,int MatchType=MATCH_WILDSUBPATH); + bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList); + static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode); + bool ExclDirByAttr(uint FileAttr); + bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta); + bool SizeCheck(int64 Size); + bool AnyFiltersActive(); + int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, + bool Flags,wchar *MatchedArg,uint MatchedArgSize); void ProcessCommand(); - void AddArcName(char *Name,wchar *NameW); - bool GetArcName(char *Name,wchar *NameW,int MaxSize); + void AddArcName(const wchar *Name); + bool GetArcName(wchar *Name,int MaxSize); bool CheckWinSize(); - int GetRecoverySize(char *Str,int DefSize); + int GetRecoverySize(const wchar *CmdStr,const wchar *Value,int DefSize); + +#ifndef SFX_MODULE + void ReportWrongSwitches(RARFORMAT Format); +#endif + + wchar Command[NM+16]; + + wchar ArcName[NM]; - char Command[NM+16]; - wchar CommandW[NM+16]; + StringList FileArgs; + StringList ExclArgs; + StringList InclArgs; + StringList ArcNames; + StringList StoreArgs; - char ArcName[NM]; - wchar ArcNameW[NM]; + SecPassword Password; - StringList *FileArgs; - StringList *ExclArgs; - StringList *InclArgs; - StringList *ArcNames; - StringList *StoreArgs; + std::vector<int64> NextVolSizes; }; #endif diff --git a/unrar/unrar/cmdfilter.cpp b/unrar/unrar/cmdfilter.cpp new file mode 100644 index 0000000..e0add14 --- /dev/null +++ b/unrar/unrar/cmdfilter.cpp @@ -0,0 +1,354 @@ +// Return 'true' if we need to exclude the file from processing as result +// of -x switch. If CheckInclList is true, we also check the file against +// the include list created with -n switch. +bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList) +{ + if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH)) + return true; + if (!CheckInclList || InclArgs.ItemsCount()==0) + return false; + if (CheckArgs(&InclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH)) + return false; + return true; +} + + +bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode) +{ + wchar *Name=ConvertPath(CheckName,NULL,0); + wchar FullName[NM]; + wchar CurMask[NM]; + *FullName=0; + Args->Rewind(); + while (Args->GetString(CurMask,ASIZE(CurMask))) + { + wchar *LastMaskChar=PointToLastChar(CurMask); + bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. + + if (Dir) + { + // CheckName is a directory. + if (DirMask) + { + // We process the directory and have the directory exclusion mask. + // So let's convert "mask\" to "mask" and process it normally. + + *LastMaskChar=0; + } + else + { + // REMOVED, we want -npath\* to match empty folders too. + // If mask has wildcards in name part and does not have the trailing + // '\' character, we cannot use it for directories. + + // if (IsWildcard(PointToName(CurMask))) + // continue; + } + } + else + { + // If we process a file inside of directory excluded by "dirmask\". + // we want to exclude such file too. So we convert "dirmask\" to + // "dirmask\*". It is important for operations other than archiving + // with -x. When archiving with -x, directory matched by "dirmask\" + // is excluded from further scanning. + + if (DirMask) + wcsncatz(CurMask,L"*",ASIZE(CurMask)); + } + +#ifndef SFX_MODULE + if (CheckFullPath && IsFullPath(CurMask)) + { + // We do not need to do the special "*\" processing here, because + // unlike the "else" part of this "if", now we convert names to full + // format, so they all include the path, which is matched by "*\" + // correctly. Moreover, removing "*\" from mask would break + // the comparison, because now all names have the path. + + if (*FullName==0) + ConvertNameToFull(CheckName,FullName,ASIZE(FullName)); + if (CmpName(CurMask,FullName,MatchMode)) + return true; + } + else +#endif + { + wchar NewName[NM+2],*CurName=Name; + + // Important to convert before "*\" check below, so masks like + // d:*\something are processed properly. + wchar *CmpMask=ConvertPath(CurMask,NULL,0); + + if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1])) + { + // We want "*\name" to match 'name' not only in subdirectories, + // but also in the current directory. We convert the name + // from 'name' to '.\name' to be matched by "*\" part even if it is + // in current directory. + NewName[0]='.'; + NewName[1]=CPATHDIVIDER; + wcsncpyz(NewName+2,Name,ASIZE(NewName)-2); + CurName=NewName; + } + + if (CmpName(CmpMask,CurName,MatchMode)) + return true; + } + } + return false; +} + + + + +#ifndef SFX_MODULE +// Now this function performs only one task and only in Windows version: +// it skips symlinks to directories if -e1024 switch is specified. +// Symlinks are skipped in ScanTree class, so their entire contents +// is skipped too. Without this function we would check the attribute +// only directly before archiving, so we would skip the symlink record, +// but not the contents of symlinked directory. +bool CommandData::ExclDirByAttr(uint FileAttr) +{ +#ifdef _WIN_ALL + if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 && + (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) + return true; +#endif + return false; +} +#endif + + + + +#if !defined(SFX_MODULE) +void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age) +{ + bool ModeOR=false,TimeMods=false; + const wchar *S=Mod; + // Check if any 'mca' modifiers are present, set OR mode if 'o' is present, + // skip modifiers and set S to beginning of time string. Be sure to check + // *S!=0, because termination 0 is a part of string for wcschr. + for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++) + if (*S=='o' || *S=='O') + ModeOR=true; + else + TimeMods=true; + + if (!TimeMods) // Assume 'm' if no modifiers are specified. + Mod=L"m"; + + // Set the specified time for every modifier. Be sure to check *Mod!=0, + // because termination 0 is a part of string for wcschr. This check is + // important when we set Mod to "m" above. + for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++) + switch(toupperw(*Mod)) + { + case 'M': + if (Before) + { + Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S); + FileMtimeBeforeOR=ModeOR; + } + else + { + Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S); + FileMtimeAfterOR=ModeOR; + } + break; + case 'C': + if (Before) + { + Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S); + FileCtimeBeforeOR=ModeOR; + } + else + { + Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S); + FileCtimeAfterOR=ModeOR; + } + break; + case 'A': + if (Before) + { + Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S); + FileAtimeBeforeOR=ModeOR; + } + else + { + Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S); + FileAtimeAfterOR=ModeOR; + } + break; + } +} +#endif + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta) +{ + bool FilterOR=false; + + if (FileMtimeBefore.IsSet()) // Filter present. + if (ftm>=FileMtimeBefore) // Condition not matched. + if (FileMtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileMtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileMtimeAfter.IsSet()) // Filter present. + if (ftm<FileMtimeAfter) // Condition not matched. + if (FileMtimeAfterOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileMtimeAfterOR) + return false; // Include file in OR mode. + + if (FileCtimeBefore.IsSet()) // Filter present. + if (ftc>=FileCtimeBefore) // Condition not matched. + if (FileCtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileCtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileCtimeAfter.IsSet()) // Filter present. + if (ftc<FileCtimeAfter) // Condition not matched. + if (FileCtimeAfterOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileCtimeAfterOR) + return false; // Include file in OR mode. + + if (FileAtimeBefore.IsSet()) // Filter present. + if (fta>=FileAtimeBefore) // Condition not matched. + if (FileAtimeBeforeOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileAtimeBeforeOR) + return false; // Include file in OR mode. + + if (FileAtimeAfter.IsSet()) // Filter present. + if (fta<FileAtimeAfter) // Condition not matched. + if (FileAtimeAfterOR) + FilterOR=true; // Not matched OR filter is present. + else + return true; // Exclude file in AND mode. + else // Condition matched. + if (FileAtimeAfterOR) + return false; // Include file in OR mode. + + return FilterOR; // Exclude if all OR filters are not matched. +} +#endif + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::SizeCheck(int64 Size) +{ + if (Size==INT64NDF) // If called from archive formats like bzip2, not storing the file size. + return false; + if (FileSizeLess!=INT64NDF && Size>=FileSizeLess) + return true; + if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) + return true; + return false; +} +#endif + + + + +// Return 0 if file must not be processed or a number of matched parameter otherwise. +int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, + bool Flags,wchar *MatchedArg,uint MatchedArgSize) +{ + if (MatchedArg!=NULL && MatchedArgSize>0) + *MatchedArg=0; + bool Dir=FileHead.Dir; + if (ExclCheck(FileHead.FileName,Dir,false,true)) + return 0; +#ifndef SFX_MODULE + if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime)) + return 0; + if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir) + return 0; + if (InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0 && + (!FileHead.Dir || !InclDir)) + return 0; + if (!Dir && SizeCheck(FileHead.UnpSize)) + return 0; +#endif + wchar *ArgName; + FileArgs.Rewind(); + for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++) + if (CmpName(ArgName,FileHead.FileName,MatchType)) + { + if (ExactMatch!=NULL) + *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0; + if (MatchedArg!=NULL) + wcsncpyz(MatchedArg,ArgName,MatchedArgSize); + return StringCount; + } + return 0; +} + + +#if !defined(SFX_MODULE) +void CommandData::SetStoreTimeMode(const wchar *S) +{ + if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+') + { + // Apply -ts, -ts1, -ts-, -ts+ to all 3 times. + // Handle obsolete -ts[2,3,4] as ts+. + EXTTIME_MODE Mode=EXTTIME_MAX; + if (*S=='-') + Mode=EXTTIME_NONE; + if (*S=='1') + Mode=EXTTIME_1S; + xmtime=xctime=xatime=Mode; + S++; + } + + while (*S!=0) + { + EXTTIME_MODE Mode=EXTTIME_MAX; + if (S[1]=='-') + Mode=EXTTIME_NONE; + if (S[1]=='1') + Mode=EXTTIME_1S; + switch(toupperw(*S)) + { + case 'M': + xmtime=Mode; + break; + case 'C': + xctime=Mode; + break; + case 'A': + xatime=Mode; + break; + case 'P': + PreserveAtime=true; + break; + } + S++; + } +} +#endif diff --git a/unrar/unrar/cmdmix.cpp b/unrar/unrar/cmdmix.cpp new file mode 100644 index 0000000..6bd1e1a --- /dev/null +++ b/unrar/unrar/cmdmix.cpp @@ -0,0 +1,125 @@ +void CommandData::OutTitle() +{ + if (BareOutput || DisableCopyright) + return; +#if defined(__GNUC__) && defined(SFX_MODULE) + mprintf(St(MCopyrightS)); +#else +#ifndef SILENT + static bool TitleShown=false; + if (TitleShown) + return; + TitleShown=true; + + wchar Version[80]; + if (RARVER_BETA!=0) + swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); + else + swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR); +#if defined(_WIN_32) || defined(_WIN_64) + wcsncatz(Version,L" ",ASIZE(Version)); +#endif +#ifdef _WIN_32 + wcsncatz(Version,St(Mx86),ASIZE(Version)); +#endif +#ifdef _WIN_64 + wcsncatz(Version,St(Mx64),ASIZE(Version)); +#endif + if (PrintVersion) + { + mprintf(L"%s",Version); + exit(0); + } + mprintf(St(MUCopyright),Version,RARVER_YEAR); +#endif +#endif +} + + +inline bool CmpMSGID(MSGID i1,MSGID i2) +{ +#ifdef MSGID_INT + return i1==i2; +#else + // If MSGID is const char*, we cannot compare pointers only. + // Pointers to different instances of same string can differ, + // so we need to compare complete strings. + return wcscmp(i1,i2)==0; +#endif +} + +void CommandData::OutHelp(RAR_EXIT ExitCode) +{ +#if !defined(SILENT) + OutTitle(); + static MSGID Help[]={ +#ifdef SFX_MODULE + // Console SFX switches definition. + MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV +#else + // UnRAR switches definition. + MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, + MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm, + MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP, + MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDH,MCHelpSwEP, + MCHelpSwEP3,MCHelpSwEP4,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, + MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwME,MCHelpSwN,MCHelpSwNa, + MCHelpSwNal,MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOP,MCHelpSwOR, + MCHelpSwOW,MCHelpSwP,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSI, + MCHelpSwSL,MCHelpSwSM,MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO, + MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX, + MCHelpSwXa,MCHelpSwXal,MCHelpSwY +#endif + }; + + for (uint I=0;I<ASIZE(Help);I++) + { +#ifndef SFX_MODULE + if (CmpMSGID(Help[I],MCHelpSwV)) + continue; +#ifndef _WIN_ALL + static MSGID Win32Only[]={ + MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF, + MCHelpSwEP2,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI + }; + bool Found=false; + for (uint J=0;J<ASIZE(Win32Only);J++) + if (CmpMSGID(Help[I],Win32Only[J])) + { + Found=true; + break; + } + if (Found) + continue; +#endif +#ifdef _UNIX + if (CmpMSGID(Help[I],MRARTitle2)) + { + mprintf(St(MFwrSlTitle2)); + continue; + } +#endif +#if !defined(_UNIX) && !defined(_WIN_ALL) + if (CmpMSGID(Help[I],MCHelpSwOW)) + continue; +#endif +#if !defined(_WIN_ALL) && !defined(_EMX) + if (CmpMSGID(Help[I],MCHelpSwAC)) + continue; +#endif +#ifndef SAVE_LINKS + if (CmpMSGID(Help[I],MCHelpSwOL)) + continue; +#endif +#ifndef RAR_SMP + if (CmpMSGID(Help[I],MCHelpSwMT)) + continue; +#endif +#endif + mprintf(St(Help[I])); + } + mprintf(L"\n"); + ErrHandler.Exit(ExitCode); +#endif +} + diff --git a/unrar/unrar/coder.cpp b/unrar/unrar/coder.cpp index cb6a883..9d971a8 100644 --- a/unrar/unrar/coder.cpp +++ b/unrar/unrar/coder.cpp @@ -17,9 +17,10 @@ void RangeCoder::InitDecoder(Unpack *UnpackRead) } +// (int) cast before "low" added only to suppress compiler warnings. #define ARI_DEC_NORMALIZE(code,low,range,read) \ { \ - while ((low^(low+range))<TOP || range<BOT && ((range=-low&(BOT-1)),1)) \ + while ((low^(low+range))<TOP || range<BOT && ((range=-(int)low&(BOT-1)),1)) \ { \ code=(code << 8) | read->GetChar(); \ range <<= 8; \ diff --git a/unrar/unrar/coder.hpp b/unrar/unrar/coder.hpp index f09f911..7b36ff2 100644 --- a/unrar/unrar/coder.hpp +++ b/unrar/unrar/coder.hpp @@ -2,7 +2,6 @@ * Contents: 'Carryless rangecoder' by Dmitry Subbotin * ****************************************************************************/ -const uint TOP=1 << 24, BOT=1 << 15; class RangeCoder { diff --git a/unrar/unrar/compress.hpp b/unrar/unrar/compress.hpp index 3181e45..4ef8570 100644 --- a/unrar/unrar/compress.hpp +++ b/unrar/unrar/compress.hpp @@ -1,36 +1,60 @@ #ifndef _RAR_COMPRESS_ #define _RAR_COMPRESS_ -class ComprDataIO; -class PackingFileTable; - -#define CODEBUFSIZE 0x4000 -#define MAXWINSIZE 0x400000 -#define MAXWINMASK (MAXWINSIZE-1) - -#define LOW_DIST_REP_COUNT 16 - -#define NC 299 /* alphabet = {0, 1, 2, ..., NC - 1} */ -#define DC 60 -#define LDC 17 -#define RC 28 -#define HUFF_TABLE_SIZE (NC+DC+RC+LDC) -#define BC 20 - -#define NC20 298 /* alphabet = {0, 1, 2, ..., NC - 1} */ -#define DC20 48 -#define RC20 28 -#define BC20 19 -#define MC20 257 - -enum {CODE_HUFFMAN,CODE_LZ,CODE_LZ2,CODE_REPEATLZ,CODE_CACHELZ, - CODE_STARTFILE,CODE_ENDFILE,CODE_VM,CODE_VMDATA}; +// Combine pack and unpack constants to class to avoid polluting global +// namespace with numerous short names. +class PackDef +{ + public: + // Maximum LZ match length we can encode even for short distances. + static const uint MAX_LZ_MATCH = 0x1001; + + // We increment LZ match length for longer distances, because shortest + // matches are not allowed for them. Maximum length increment is 3 + // for distances larger than 256KB (0x40000). Here we define the maximum + // incremented LZ match. Normally packer does not use it, but we must be + // ready to process it in corrupt archives. + static const uint MAX_INC_LZ_MATCH = MAX_LZ_MATCH + 3; + + static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. + static const uint MAX3_INC_LZ_MATCH = MAX3_LZ_MATCH + 3; + static const uint LOW_DIST_REP_COUNT = 16; + + static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC = 64; + static const uint LDC = 16; + static const uint RC = 44; + static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC; + static const uint BC = 20; + + static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC30 = 60; + static const uint LDC30 = 17; + static const uint RC30 = 28; + static const uint BC30 = 20; + static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; + + static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC20 = 48; + static const uint RC20 = 28; + static const uint BC20 = 19; + static const uint MC20 = 257; + + // Largest alphabet size among all values listed above. + static const uint LARGEST_TABLE_SIZE = 306; + + enum { + CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, + CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA + }; +}; enum FilterType { - FILTER_NONE, FILTER_PPM /*dummy*/, FILTER_E8, FILTER_E8E9, - FILTER_UPCASETOLOW, FILTER_AUDIO, FILTER_RGB, FILTER_DELTA, - FILTER_ITANIUM, FILTER_E8E9V2 + // These values must not be changed, because we use them directly + // in RAR5 compression and decompression code. + FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, + FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE }; #endif diff --git a/unrar/unrar/consio.cpp b/unrar/unrar/consio.cpp index 6644dce..fa35d61 100644 --- a/unrar/unrar/consio.cpp +++ b/unrar/unrar/consio.cpp @@ -1,220 +1,340 @@ #include "rar.hpp" - -#ifndef GUI #include "log.cpp" + +static MESSAGE_TYPE MsgStream=MSG_STDOUT; +static RAR_CHARSET RedirectCharset=RCH_DEFAULT; +static bool ProhibitInput=false; + +const int MaxMsgSize=2*NM+2048; + +static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false; + +#ifdef _WIN_ALL +static bool IsRedirected(DWORD nStdHandle) +{ + HANDLE hStd=GetStdHandle(nStdHandle); + DWORD Mode; + return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0; +} #endif -static int KbdAnsi(char *Addr,int Size); -#if !defined(GUI) && !defined(SILENT) -static void RawPrint(char *Msg,MESSAGE_TYPE MessageType); -static uint GetKey(); +void InitConsole() +{ +#ifdef _WIN_ALL + // We want messages like file names or progress percent to be printed + // immediately. Use only in Windows, in Unix they can cause wprintf %ls + // to fail with non-English strings. + setbuf(stdout,NULL); + setbuf(stderr,NULL); + + // Detect if output is redirected and set output mode properly. + // We do not want to send Unicode output to files and especially to pipes + // like '|more', which cannot handle them correctly in Windows. + // In Unix console output is UTF-8 and it is handled correctly + // when redirecting, so no need to perform any adjustments. + StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE); + StderrRedirected=IsRedirected(STD_ERROR_HANDLE); + StdinRedirected=IsRedirected(STD_INPUT_HANDLE); +#ifdef _MSC_VER + if (!StdoutRedirected) + _setmode(_fileno(stdout), _O_U16TEXT); + if (!StderrRedirected) + _setmode(_fileno(stderr), _O_U16TEXT); #endif +#elif defined(_UNIX) + StdoutRedirected=!isatty(fileno(stdout)); + StderrRedirected=!isatty(fileno(stderr)); + StdinRedirected=!isatty(fileno(stdin)); +#endif +} -static MESSAGE_TYPE MsgStream=MSG_STDOUT; -static bool Sound=false; -const int MaxMsgSize=2*NM+2048; -void InitConsoleOptions(MESSAGE_TYPE MsgStream,bool Sound) +void SetConsoleMsgStream(MESSAGE_TYPE MsgStream) { ::MsgStream=MsgStream; - ::Sound=Sound; } -#if !defined(GUI) && !defined(SILENT) -void mprintf(const char *fmt,...) + +void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset) { - if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY) - return; - safebuf char Msg[MaxMsgSize]; - va_list argptr; - va_start(argptr,fmt); - vsprintf(Msg,fmt,argptr); - RawPrint(Msg,MsgStream); - va_end(argptr); + ::RedirectCharset=RedirectCharset; } -#endif -#if !defined(GUI) && !defined(SILENT) -void eprintf(const char *fmt,...) +void ProhibitConsoleInput() { - if (MsgStream==MSG_NULL) - return; - safebuf char Msg[MaxMsgSize]; - va_list argptr; - va_start(argptr,fmt); - vsprintf(Msg,fmt,argptr); - RawPrint(Msg,MSG_STDERR); - va_end(argptr); + ProhibitInput=true; } -#endif -#if !defined(GUI) && !defined(SILENT) -void RawPrint(char *Msg,MESSAGE_TYPE MessageType) +#ifndef SILENT +static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) { - File OutFile; - switch(MessageType) + // This buffer is for format string only, not for entire output, + // so it can be short enough. + wchar fmtw[1024]; + PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); +#ifdef _WIN_ALL + safebuf wchar Msg[MaxMsgSize]; + if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) { - case MSG_STDOUT: - OutFile.SetHandleType(FILE_HANDLESTD); - break; - case MSG_STDERR: - case MSG_ERRONLY: - OutFile.SetHandleType(FILE_HANDLEERR); - break; - default: - return; - } -#ifdef _WIN_32 - CharToOem(Msg,Msg); + HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE); + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + DWORD Written; + if (RedirectCharset==RCH_UNICODE) + WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL); + else + { + // Avoid Unicode for redirect in Windows, it does not work with pipes. + safebuf char MsgA[MaxMsgSize]; + if (RedirectCharset==RCH_UTF8) + WideToUtf(Msg,MsgA,ASIZE(MsgA)); + else + WideToChar(Msg,MsgA,ASIZE(MsgA)); + if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM) + CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding. - char OutMsg[MaxMsgSize],*OutPos=OutMsg; - for (int I=0;Msg[I]!=0;I++) - { - if (Msg[I]=='\n' && (I==0 || Msg[I-1]!='\r')) - *(OutPos++)='\r'; - *(OutPos++)=Msg[I]; + // We already converted \n to \r\n above, so we use WriteFile instead + // of C library to avoid unnecessary additional conversion. + WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL); + } + return; } - *OutPos=0; - strcpy(Msg,OutMsg); -#endif -#if defined(_UNIX) || defined(_EMX) - char OutMsg[MaxMsgSize],*OutPos=OutMsg; - for (int I=0;Msg[I]!=0;I++) - if (Msg[I]!='\r') - *(OutPos++)=Msg[I]; - *OutPos=0; - strcpy(Msg,OutMsg); + // MSVC2008 vfwprintf writes every character to console separately + // and it is too slow. We use direct WriteConsole call instead. + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE); + DWORD Written; + WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL); +#else + vfwprintf(dest,fmtw,arglist); + // We do not use setbuf(NULL) in Unix (see comments in InitConsole). + fflush(dest); #endif +} - OutFile.Write(Msg,strlen(Msg)); -// OutFile.Flush(); + +void mprintf(const wchar *fmt,...) +{ + if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY) + return; + + fflush(stderr); // Ensure proper message order. + + va_list arglist; + va_start(arglist,fmt); + FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout; + cvt_wprintf(dest,fmt,arglist); + va_end(arglist); } #endif #ifndef SILENT -void Alarm() +void eprintf(const wchar *fmt,...) { -#ifndef SFX_MODULE - if (Sound) - putchar('\007'); -#endif + if (MsgStream==MSG_NULL) + return; + + fflush(stdout); // Ensure proper message order. + + va_list arglist; + va_start(arglist,fmt); + cvt_wprintf(stderr,fmt,arglist); + va_end(arglist); } #endif #ifndef SILENT -#ifndef GUI -void GetPasswordText(char *Str,int MaxLength) +static void QuitIfInputProhibited() { -#ifdef _WIN_32 - HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); - HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE); - DWORD ConInMode,ConOutMode; - DWORD Read=0; - GetConsoleMode(hConIn,&ConInMode); - GetConsoleMode(hConOut,&ConOutMode); - SetConsoleMode(hConIn,ENABLE_LINE_INPUT); - SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT); - ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL); - Str[Read]=0; - OemToChar(Str,Str); - SetConsoleMode(hConIn,ConInMode); - SetConsoleMode(hConOut,ConOutMode); -#elif defined(_EMX) || defined(_BEOS) || defined(__sparc) || defined(sparc) || defined (__VMS) - fgets(Str,MaxLength-1,stdin); + // We cannot handle user prompts if -si is used to read file or archive data + // from stdin. + if (ProhibitInput) + { + mprintf(St(MStdinNoInput)); + ErrHandler.Exit(RARX_FATAL); + } +} + + +static void GetPasswordText(wchar *Str,uint MaxLength) +{ + if (MaxLength==0) + return; + QuitIfInputProhibited(); + if (StdinRedirected) + getwstr(Str,MaxLength); // Read from pipe or redirected file. + else + { +#ifdef _WIN_ALL + HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); + DWORD ConInMode; + GetConsoleMode(hConIn,&ConInMode); + SetConsoleMode(hConIn,ENABLE_LINE_INPUT); // Remove ENABLE_ECHO_INPUT. + + // We prefer ReadConsole to ReadFile, so we can read Unicode input. + DWORD Read=0; + ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL); + Str[Read]=0; + SetConsoleMode(hConIn,ConInMode); + + // If entered password is longer than MAXPASSWORD and truncated, + // read its unread part anyway, so it isn't read later as the second + // password for -p switch. Low level FlushConsoleInputBuffer doesn't help + // for high level ReadConsole, which in line input mode seems to store + // the rest of string in its own internal buffer. + if (wcschr(Str,'\r')==NULL) // If '\r' is missing, the password was truncated. + while (true) + { + wchar Trail[64]; + DWORD TrailRead=0; + // Use ASIZE(Trail)-1 to reserve the space for trailing 0. + ReadConsole(hConIn,Trail,ASIZE(Trail)-1,&TrailRead,NULL); + Trail[TrailRead]=0; + if (TrailRead==0 || wcschr(Trail,'\r')!=NULL) + break; + } + +#else + char StrA[MAXPASSWORD*4]; // "*4" for multibyte UTF-8 characters. +#if defined(_EMX) || defined (__VMS) + fgets(StrA,ASIZE(StrA)-1,stdin); +#elif defined(__sun) + strncpyz(StrA,getpassphrase(""),ASIZE(StrA)); #else - strncpyz(Str,getpass(""),MaxLength); + strncpyz(StrA,getpass(""),ASIZE(StrA)); #endif + CharToWide(StrA,Str,MaxLength); + cleandata(StrA,sizeof(StrA)); +#endif + } Str[MaxLength-1]=0; RemoveLF(Str); } #endif -#endif #ifndef SILENT -bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength) +bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) { - Alarm(); + if (!StdinRedirected) + uiAlarm(UIALARM_QUESTION); + while (true) { - char PromptStr[NM+256]; -#if defined(_EMX) || defined(_BEOS) - strcpy(PromptStr,St(MAskPswEcho)); -#else - strcpy(PromptStr,St(MAskPsw)); -#endif - if (Type!=PASSWORD_GLOBAL) +// if (!StdinRedirected) + if (Type==UIPASSWORD_GLOBAL) + eprintf(L"\n%s: ",St(MAskPsw)); + else + eprintf(St(MAskPswFor),FileName); + + wchar PlainPsw[MAXPASSWORD+1]; + GetPasswordText(PlainPsw,ASIZE(PlainPsw)); + if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL) + return false; + if (wcslen(PlainPsw)>=MAXPASSWORD) { - strcat(PromptStr,St(MFor)); - char *NameOnly=PointToName(FileName); - if (strlen(PromptStr)+strlen(NameOnly)<ASIZE(PromptStr)) - strcat(PromptStr,NameOnly); + PlainPsw[MAXPASSWORD-1]=0; + uiMsg(UIERROR_TRUNCPSW,MAXPASSWORD-1); } - eprintf("\n%s: ",PromptStr); - GetPasswordText(Password,MaxLength); - if (*Password==0 && Type==PASSWORD_GLOBAL) - return(false); - if (Type==PASSWORD_GLOBAL) + if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) { eprintf(St(MReAskPsw)); - char CmpStr[MAXPASSWORD]; + wchar CmpStr[MAXPASSWORD]; GetPasswordText(CmpStr,ASIZE(CmpStr)); - if (*CmpStr==0 || strcmp(Password,CmpStr)!=0) + if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0) { eprintf(St(MNotMatchPsw)); - memset(Password,0,MaxLength); - memset(CmpStr,0,sizeof(CmpStr)); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(CmpStr,sizeof(CmpStr)); continue; } - memset(CmpStr,0,sizeof(CmpStr)); + cleandata(CmpStr,sizeof(CmpStr)); } + Password->Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); break; } - return(true); + return true; } #endif -#if !defined(GUI) && !defined(SILENT) -uint GetKey() +#ifndef SILENT +bool getwstr(wchar *str,size_t n) { - char Str[80]; - bool EndOfFile; -#if defined(__GNUC__) || defined(sun) - EndOfFile=(fgets(Str,sizeof(Str),stdin)==NULL); -#else - File SrcFile; - SrcFile.SetHandleType(FILE_HANDLESTD); - EndOfFile=(SrcFile.Read(Str,sizeof(Str))==0); -#endif - if (EndOfFile) + // Print buffered prompt title function before waiting for input. + fflush(stderr); + + QuitIfInputProhibited(); + + *str=0; +#if defined(_WIN_ALL) + // fgetws does not work well with non-English text in Windows, + // so we do not use it. + if (StdinRedirected) // ReadConsole does not work if redirected. + { + // fgets does not work well with pipes in Windows in our test. + // Let's use files. + Array<char> StrA(n*4); // Up to 4 UTF-8 characters per wchar_t. + File SrcFile; + SrcFile.SetHandleType(FILE_HANDLESTD); + SrcFile.SetLineInputMode(true); + int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1); + if (ReadSize<=0) + { + // Looks like stdin is a null device. We can enter to infinite loop + // calling Ask(), so let's better exit. + ErrHandler.Exit(RARX_USERBREAK); + } + StrA[ReadSize]=0; + + // We expect ANSI encoding here, but "echo text|rar ..." to pipe to RAR, + // such as send passwords, we get OEM encoding by default, unless we + // use "chcp" in console. But we avoid OEM to ANSI conversion, + // because we also want to handle ANSI files redirection correctly, + // like "rar ... < ansifile.txt". + CharToWide(&StrA[0],str,n); + cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords. + } + else { - // Looks like stdin is a null device. We can enter to infinite loop - // calling Ask(), so let's better exit. - ErrHandler.Exit(USER_BREAK); + DWORD ReadSize=0; + if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0) + return false; + str[ReadSize]=0; } - return(Str[0]); +#else + if (fgetws(str,n,stdin)==NULL) + ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop. +#endif + RemoveLF(str); + return true; } #endif -#if !defined(GUI) && !defined(SILENT) -int Ask(const char *AskStr) +#ifndef SILENT +// We allow this function to return 0 in case of invalid input, +// because it might be convenient to press Enter to some not dangerous +// prompts like "insert disk with next volume". We should call this function +// again in case of 0 in dangerous prompt such as overwriting file. +int Ask(const wchar *AskStr) { + uiAlarm(UIALARM_QUESTION); + const int MaxItems=10; - char Item[MaxItems][40]; + wchar Item[MaxItems][40]; int ItemKeyPos[MaxItems],NumItems=0; - for (const char *NextItem=AskStr;NextItem!=NULL;NextItem=strchr(NextItem+1,'_')) + for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_')) { - char *CurItem=Item[NumItems]; - strncpyz(CurItem,NextItem+1,ASIZE(Item[0])); - char *EndItem=strchr(CurItem,'_'); + wchar *CurItem=Item[NumItems]; + wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0])); + wchar *EndItem=wcschr(CurItem,'_'); if (EndItem!=NULL) *EndItem=0; int KeyPos=0,CurKey; @@ -222,7 +342,7 @@ int Ask(const char *AskStr) { bool Found=false; for (int I=0;I<NumItems && !Found;I++) - if (loctoupper(Item[I][ItemKeyPos[I]])==loctoupper(CurKey)) + if (toupperw(Item[I][ItemKeyPos[I]])==toupperw(CurKey)) Found=true; if (!Found && CurKey!=' ') break; @@ -234,61 +354,53 @@ int Ask(const char *AskStr) for (int I=0;I<NumItems;I++) { - eprintf(I==0 ? (NumItems>4 ? "\n":" "):", "); + eprintf(I==0 ? (NumItems>3 ? L"\n":L" "):L", "); int KeyPos=ItemKeyPos[I]; for (int J=0;J<KeyPos;J++) - eprintf("%c",Item[I][J]); - eprintf("[%c]%s",Item[I][KeyPos],&Item[I][KeyPos+1]); + eprintf(L"%c",Item[I][J]); + eprintf(L"[%c]%ls",Item[I][KeyPos],&Item[I][KeyPos+1]); } - eprintf(" "); - int Ch=GetKey(); -#if defined(_WIN_32) - OemToCharBuff((LPCSTR)&Ch,(LPTSTR)&Ch,1); -#endif - Ch=loctoupper(Ch); + eprintf(L" "); + wchar Str[50]; + getwstr(Str,ASIZE(Str)); + wchar Ch=toupperw(Str[0]); for (int I=0;I<NumItems;I++) if (Ch==Item[I][ItemKeyPos[I]]) - return(I+1); - return(0); + return I+1; + return 0; } #endif -int KbdAnsi(char *Addr,int Size) +static bool IsCommentUnsafe(const wchar *Data,size_t Size) { - int RetCode=0; -#ifndef GUI - for (int I=0;I<Size;I++) - if (Addr[I]==27 && Addr[I+1]=='[') - { - for (int J=I+2;J<Size;J++) + for (size_t I=0;I<Size;I++) + if (Data[I]==27 && Data[I+1]=='[') + for (size_t J=I+2;J<Size;J++) { - if (Addr[J]=='\"') - return(2); - if (!isdigit(Addr[J]) && Addr[J]!=';') + // Return true for <ESC>[{key};"{string}"p used to redefine + // a keyboard key on some terminals. + if (Data[J]=='\"') + return true; + if (!IsDigit(Data[J]) && Data[J]!=';') break; } - RetCode=1; - } -#endif - return(RetCode); + return false; } -void OutComment(char *Comment,int Size) +void OutComment(const wchar *Comment,size_t Size) { -#ifndef GUI - if (KbdAnsi(Comment,Size)==2) + if (IsCommentUnsafe(Comment,Size)) return; - const int MaxOutSize=0x400; - for (int I=0;I<Size;I+=MaxOutSize) + const size_t MaxOutSize=0x400; + for (size_t I=0;I<Size;I+=MaxOutSize) { - char Msg[MaxOutSize+1]; - int CopySize=Min(MaxOutSize,Size-I); - strncpy(Msg,Comment+I,CopySize); + wchar Msg[MaxOutSize+1]; + size_t CopySize=Min(MaxOutSize,Size-I); + wcsncpy(Msg,Comment+I,CopySize); Msg[CopySize]=0; - mprintf("%s",Msg); + mprintf(L"%s",Msg); } - mprintf("\n"); -#endif + mprintf(L"\n"); } diff --git a/unrar/unrar/consio.hpp b/unrar/unrar/consio.hpp index b077cea..bf97289 100644 --- a/unrar/unrar/consio.hpp +++ b/unrar/unrar/consio.hpp @@ -1,44 +1,28 @@ #ifndef _RAR_CONSIO_ #define _RAR_CONSIO_ -enum {ALARM_SOUND,ERROR_SOUND,QUESTION_SOUND}; - -enum PASSWORD_TYPE {PASSWORD_GLOBAL,PASSWORD_FILE,PASSWORD_ARCHIVE}; - -void InitConsoleOptions(MESSAGE_TYPE MsgStream,bool Sound); +void InitConsole(); +void SetConsoleMsgStream(MESSAGE_TYPE MsgStream); +void SetConsoleRedirectCharset(RAR_CHARSET RedirectCharset); +void ProhibitConsoleInput(); +void OutComment(const wchar *Comment,size_t Size); #ifndef SILENT -void mprintf(const char *fmt,...); -void eprintf(const char *fmt,...); -void Alarm(); -void GetPasswordText(char *Str,int MaxLength); -bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength); -int Ask(const char *AskStr); +bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password); #endif -void OutComment(char *Comment,int Size); - #ifdef SILENT -#ifdef __GNUC__ - #define mprintf(args...) - #define eprintf(args...) + inline void mprintf(const wchar *fmt,...) {} + inline void eprintf(const wchar *fmt,...) {} + inline void Alarm() {} + inline int Ask(const wchar *AskStr) {return 0;} + inline bool getwstr(wchar *str,size_t n) {return false;} #else - #ifdef _MSC_VER - inline void mprintf(const char *fmt,...) {} - #else - inline void mprintf(const char *fmt,const char *a=NULL,const char *b=NULL) {} - #endif - inline void eprintf(const char *fmt,const char *a=NULL,const char *b=NULL) {} - inline void mprintf(const char *fmt,int b) {} - inline void eprintf(const char *fmt,int b) {} - inline void mprintf(const char *fmt,const char *a,int b) {} - inline void eprintf(const char *fmt,const char *a,int b) {} -#endif -inline void Alarm() {} -inline void GetPasswordText(char *Str,int MaxLength) {} -inline unsigned int GetKey() {return(0);} -inline bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength) {return(false);} -inline int Ask(const char *AskStr) {return(0);} + void mprintf(const wchar *fmt,...); + void eprintf(const wchar *fmt,...); + void Alarm(); + int Ask(const wchar *AskStr); + bool getwstr(wchar *str,size_t n); #endif #endif diff --git a/unrar/unrar/crc.cpp b/unrar/unrar/crc.cpp index 9758241..0c6aef1 100644 --- a/unrar/unrar/crc.cpp +++ b/unrar/unrar/crc.cpp @@ -1,60 +1,103 @@ +// This CRC function is based on Intel Slicing-by-8 algorithm. +// +// Original Intel Slicing-by-8 code is available here: +// +// http://sourceforge.net/projects/slicing-by-8/ +// +// Original Intel Slicing-by-8 code is licensed as: +// +// Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved +// +// This software program is licensed subject to the BSD License, +// available at http://www.opensource.org/licenses/bsd-license.html + + #include "rar.hpp" -uint CRCTab[256]; +#ifndef SFX_MODULE +// User suggested to avoid BSD license in SFX module, so they do not need +// to include the license to SFX archive. +#define USE_SLICING +#endif + +static uint crc_tables[8][256]; // Tables for Slicing-by-8. -void InitCRC() + +// Build the classic CRC32 lookup table. +// We also provide this function to legacy RAR and ZIP decryption code. +void InitCRC32(uint *CRCTab) { - for (int I=0;I<256;I++) + if (CRCTab[1]!=0) + return; + for (uint I=0;I<256;I++) { uint C=I; - for (int J=0;J<8;J++) - C=(C & 1) ? (C>>1)^0xEDB88320L : (C>>1); + for (uint J=0;J<8;J++) + C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1); CRCTab[I]=C; } } -uint CRC(uint StartCRC,const void *Addr,size_t Size) +static void InitTables() { - if (CRCTab[1]==0) - InitCRC(); - byte *Data=(byte *)Addr; -#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) + InitCRC32(crc_tables[0]); -#ifdef _MSC_VER - // avoid a warning about 'Data' pointer truncation in 64 bit mode - #pragma warning( disable : 4311 ) -#endif - - while (Size>0 && ((long)Data & 7)) +#ifdef USE_SLICING + for (uint I=0;I<256;I++) // Build additional lookup tables. { - StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8); - Size--; - Data++; + uint C=crc_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc_tables[0][(byte)C]^(C>>8); + crc_tables[J][I]=C; + } } - while (Size>=8) +#endif +} + + +struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32; + +uint CRC32(uint StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + +#ifdef USE_SLICING + // Align Data to 8 for better performance. + for (;Size>0 && ((size_t)Data & 7);Size--,Data++) + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (;Size>=8;Size-=8,Data+=8) { - StartCRC^=*(uint32 *)Data; - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC^=*(uint32 *)(Data+4); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8); - Data+=8; - Size-=8; +#ifdef BIG_ENDIAN + StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24); + uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24); +#else + StartCRC ^= *(uint32 *) Data; + uint NextData = *(uint32 *) (Data+4); +#endif + StartCRC = crc_tables[7][(byte) StartCRC ] ^ + crc_tables[6][(byte)(StartCRC >> 8) ] ^ + crc_tables[5][(byte)(StartCRC >> 16)] ^ + crc_tables[4][(byte)(StartCRC >> 24)] ^ + crc_tables[3][(byte) NextData ] ^ + crc_tables[2][(byte)(NextData >> 8) ] ^ + crc_tables[1][(byte)(NextData >> 16)] ^ + crc_tables[0][(byte)(NextData >> 24)]; } #endif - for (size_t I=0;I<Size;I++) - StartCRC=CRCTab[(byte)(StartCRC^Data[I])]^(StartCRC>>8); - return(StartCRC); + + for (;Size>0;Size--,Data++) // Process left data. + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return StartCRC; } + #ifndef SFX_MODULE -ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size) +// For RAR 1.4 archives in case somebody still has them. +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size) { byte *Data=(byte *)Addr; for (size_t I=0;I<Size;I++) @@ -62,6 +105,169 @@ ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size) StartCRC=(StartCRC+Data[I])&0xffff; StartCRC=((StartCRC<<1)|(StartCRC>>15))&0xffff; } - return(StartCRC); + return StartCRC; +} +#endif + + +#if 0 +static uint64 crc64_tables[8][256]; // Tables for Slicing-by-8 for CRC64. + +void InitCRC64(uint64 *CRCTab) +{ + const uint64 poly=INT32TO64(0xC96C5795, 0xD7870F42); // 0xC96C5795D7870F42; + for (uint I=0;I<256;I++) + { + uint64 C=I; + for (uint J=0;J<8;J++) + C=(C & 1) ? (C>>1)^poly: (C>>1); + CRCTab[I]=C; + } +} + + +static void InitTables64() +{ + InitCRC64(crc64_tables[0]); + + for (uint I=0;I<256;I++) // Build additional lookup tables. + { + uint64 C=crc64_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc64_tables[0][(byte)C]^(C>>8); + crc64_tables[J][I]=C; + } + } } + + +// We cannot place the intialization to CRC64(), because we use this function +// in multithreaded mode and it conflicts with multithreading. +struct CallInitCRC64 {CallInitCRC64() {InitTables64();}} static CallInit64; + +uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + + // Align Data to 8 for better performance. + for (;Size>0 && ((size_t)Data & 7)!=0;Size--,Data++) + StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (byte *DataEnd=Data+Size/8*8; Data<DataEnd; Data+=8 ) + { + uint64 Index=StartCRC; +#ifdef BIG_ENDIAN + Index ^= (uint64(Data[0])|(uint64(Data[1])<<8)|(uint64(Data[2])<<16)|(uint64(Data[3])<<24))| + (uint64(Data[4])<<32)|(uint64(Data[5])<<40)|(uint64(Data[6])<<48)|(uint64(Data[7])<<56); +#else + Index ^= *(uint64 *)Data; +#endif + StartCRC = crc64_tables[ 7 ] [ ( byte ) (Index ) ] ^ + crc64_tables[ 6 ] [ ( byte ) (Index >> 8 ) ] ^ + crc64_tables[ 5 ] [ ( byte ) (Index >> 16 ) ] ^ + crc64_tables[ 4 ] [ ( byte ) (Index >> 24 ) ] ^ + crc64_tables[ 3 ] [ ( byte ) (Index >> 32 ) ] ^ + crc64_tables[ 2 ] [ ( byte ) (Index >> 40 ) ] ^ + crc64_tables[ 1 ] [ ( byte ) (Index >> 48 ) ] ^ + crc64_tables[ 0 ] [ ( byte ) (Index >> 56 ) ] ; + } + + for (Size%=8;Size>0;Size--,Data++) // Process left data. + StartCRC=crc64_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return StartCRC; +} + + +#if 0 +static void TestCRC(); +struct TestCRCStruct {TestCRCStruct() {TestCRC();exit(0);}} GlobalTesCRC; + +void TestCRC() +{ + const uint FirstSize=300; + byte b[FirstSize]; + + if ((CRC32(0xffffffff,(byte*)"testtesttest",12)^0xffffffff)==0x44608e84) + mprintf(L"\nCRC32 test1 OK"); + else + mprintf(L"\nCRC32 test1 FAILED"); + + if (CRC32(0,(byte*)"te\x80st",5)==0xB2E5C5AE) + mprintf(L"\nCRC32 test2 OK"); + else + mprintf(L"\nCRC32 test2 FAILED"); + + for (uint I=0;I<14;I++) // Check for possible int sign extension. + b[I]=(byte)0x7f+I; + if ((CRC32(0xffffffff,b,14)^0xffffffff)==0x1DFA75DA) + mprintf(L"\nCRC32 test3 OK"); + else + mprintf(L"\nCRC32 test3 FAILED"); + + for (uint I=0;I<FirstSize;I++) + b[I]=(byte)I; + uint r32=CRC32(0xffffffff,b,FirstSize); + for (uint I=FirstSize;I<1024;I++) + { + b[0]=(byte)I; + r32=CRC32(r32,b,1); + } + if ((r32^0xffffffff)==0xB70B4C26) + mprintf(L"\nCRC32 test4 OK"); + else + mprintf(L"\nCRC32 test4 FAILED"); + + if ((CRC64(0xffffffffffffffff,(byte*)"testtesttest",12)^0xffffffffffffffff)==0x7B1C2D230EDEB436) + mprintf(L"\nCRC64 test1 OK"); + else + mprintf(L"\nCRC64 test1 FAILED"); + + if (CRC64(0,(byte*)"te\x80st",5)==0xB5DBF9583A6EED4A) + mprintf(L"\nCRC64 test2 OK"); + else + mprintf(L"\nCRC64 test2 FAILED"); + + for (uint I=0;I<14;I++) // Check for possible int sign extension. + b[I]=(byte)0x7f+I; + if ((CRC64(0xffffffffffffffff,b,14)^0xffffffffffffffff)==0xE019941C05B2820C) + mprintf(L"\nCRC64 test3 OK"); + else + mprintf(L"\nCRC64 test3 FAILED"); + + for (uint I=0;I<FirstSize;I++) + b[I]=(byte)I; + uint64 r64=CRC64(0xffffffffffffffff,b,FirstSize); + for (uint I=FirstSize;I<1024;I++) + { + b[0]=(byte)I; + r64=CRC64(r64,b,1); + } + if ((r64^0xffffffffffffffff)==0xD51FB58DC789C400) + mprintf(L"\nCRC64 test4 OK"); + else + mprintf(L"\nCRC64 test4 FAILED"); + + const size_t BufSize=0x100000; + byte *Buf=new byte[BufSize]; + memset(Buf,0,BufSize); + + clock_t StartTime=clock(); + r32=0xffffffff; + const uint BufCount=5000; + for (uint I=0;I<BufCount;I++) + r32=CRC32(r32,Buf,BufSize); + if (r32!=0) // Otherwise compiler optimizer removes CRC calculation. + mprintf(L"\nCRC32 speed: %d MB/s",BufCount*1000/(clock()-StartTime)); + + StartTime=clock(); + r64=0xffffffffffffffff; + for (uint I=0;I<BufCount;I++) + r64=CRC64(r64,Buf,BufSize); + if (r64!=0) // Otherwise compiler optimizer removes CRC calculation. + mprintf(L"\nCRC64 speed: %d MB/s",BufCount*1000/(clock()-StartTime)); +} +#endif + #endif diff --git a/unrar/unrar/crc.hpp b/unrar/unrar/crc.hpp index a632a54..f573454 100644 --- a/unrar/unrar/crc.hpp +++ b/unrar/unrar/crc.hpp @@ -1,10 +1,19 @@ #ifndef _RAR_CRC_ #define _RAR_CRC_ -extern uint CRCTab[256]; +// This function is only to intialize external CRC tables. We do not need to +// call it before calculating CRC32. +void InitCRC32(uint *CRCTab); -void InitCRC(); -uint CRC(uint StartCRC,const void *Addr,size_t Size); -ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size); +uint CRC32(uint StartCRC,const void *Addr,size_t Size); + +#ifndef SFX_MODULE +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size); +#endif + +#if 0 +void InitCRC64(uint64 *CRCTab); +uint64 CRC64(uint64 StartCRC,const void *Addr,size_t Size); +#endif #endif diff --git a/unrar/unrar/crypt.cpp b/unrar/unrar/crypt.cpp index 04a1d70..d14c78f 100644 --- a/unrar/unrar/crypt.cpp +++ b/unrar/unrar/crypt.cpp @@ -1,381 +1,128 @@ #include "rar.hpp" #ifndef SFX_MODULE -extern uint CRCTab[256]; +#include "crypt1.cpp" +#include "crypt2.cpp" #endif +#include "crypt3.cpp" +#include "crypt5.cpp" -#define NROUNDS 32 -#define rol(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) -#define ror(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) - -#define substLong(t) ( (uint)SubstTable[(uint)t&255] | \ - ((uint)SubstTable[(int)(t>> 8)&255]<< 8) | \ - ((uint)SubstTable[(int)(t>>16)&255]<<16) | \ - ((uint)SubstTable[(int)(t>>24)&255]<<24) ) - -CryptKeyCacheItem CryptData::Cache[4]; -int CryptData::CachePos=0; - - -#ifndef SFX_MODULE -static byte InitSubstTable[256]={ - 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, - 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, - 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, - 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, - 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, - 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, - 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, - 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, - 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, - 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, - 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, - 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, - 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, - 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, - 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, - 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 -}; -#endif - - - -void CryptData::DecryptBlock(byte *Buf,int Size) +CryptData::CryptData() { - rin.blockDecrypt(Buf,Size,Buf); + Method=CRYPT_NONE; + KDF3CachePos=0; + KDF5CachePos=0; + memset(CRCTab,0,sizeof(CRCTab)); } -#ifndef SFX_MODULE -void CryptData::EncryptBlock20(byte *Buf) -{ - uint A,B,C,D,T,TA,TB; -#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) - A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0]; - B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1]; - C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2]; - D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3]; -#else - uint32 *BufPtr=(uint32 *)Buf; - A=BufPtr[0]^Key[0]; - B=BufPtr[1]^Key[1]; - C=BufPtr[2]^Key[2]; - D=BufPtr[3]^Key[3]; -#endif - for(int I=0;I<NROUNDS;I++) - { - T=((C+rol(D,11,32))^Key[I&3]); - TA=A^substLong(T); - T=((D^rol(C,17,32))+Key[I&3]); - TB=B^substLong(T); - A=C; - B=D; - C=TA; - D=TB; - } -#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) - C^=Key[0]; - Buf[0]=(byte)C; - Buf[1]=(byte)(C>>8); - Buf[2]=(byte)(C>>16); - Buf[3]=(byte)(C>>24); - D^=Key[1]; - Buf[4]=(byte)D; - Buf[5]=(byte)(D>>8); - Buf[6]=(byte)(D>>16); - Buf[7]=(byte)(D>>24); - A^=Key[2]; - Buf[8]=(byte)A; - Buf[9]=(byte)(A>>8); - Buf[10]=(byte)(A>>16); - Buf[11]=(byte)(A>>24); - B^=Key[3]; - Buf[12]=(byte)B; - Buf[13]=(byte)(B>>8); - Buf[14]=(byte)(B>>16); - Buf[15]=(byte)(B>>24); -#else - BufPtr[0]=C^Key[0]; - BufPtr[1]=D^Key[1]; - BufPtr[2]=A^Key[2]; - BufPtr[3]=B^Key[3]; -#endif - UpdKeys(Buf); -} -void CryptData::DecryptBlock20(byte *Buf) +void CryptData::DecryptBlock(byte *Buf,size_t Size) { - byte InBuf[16]; - uint A,B,C,D,T,TA,TB; -#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) - A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0]; - B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1]; - C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2]; - D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3]; -#else - uint32 *BufPtr=(uint32 *)Buf; - A=BufPtr[0]^Key[0]; - B=BufPtr[1]^Key[1]; - C=BufPtr[2]^Key[2]; - D=BufPtr[3]^Key[3]; -#endif - memcpy(InBuf,Buf,sizeof(InBuf)); - for(int I=NROUNDS-1;I>=0;I--) + switch(Method) { - T=((C+rol(D,11,32))^Key[I&3]); - TA=A^substLong(T); - T=((D^rol(C,17,32))+Key[I&3]); - TB=B^substLong(T); - A=C; - B=D; - C=TA; - D=TB; - } -#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT) - C^=Key[0]; - Buf[0]=(byte)C; - Buf[1]=(byte)(C>>8); - Buf[2]=(byte)(C>>16); - Buf[3]=(byte)(C>>24); - D^=Key[1]; - Buf[4]=(byte)D; - Buf[5]=(byte)(D>>8); - Buf[6]=(byte)(D>>16); - Buf[7]=(byte)(D>>24); - A^=Key[2]; - Buf[8]=(byte)A; - Buf[9]=(byte)(A>>8); - Buf[10]=(byte)(A>>16); - Buf[11]=(byte)(A>>24); - B^=Key[3]; - Buf[12]=(byte)B; - Buf[13]=(byte)(B>>8); - Buf[14]=(byte)(B>>16); - Buf[15]=(byte)(B>>24); -#else - BufPtr[0]=C^Key[0]; - BufPtr[1]=D^Key[1]; - BufPtr[2]=A^Key[2]; - BufPtr[3]=B^Key[3]; +#ifndef SFX_MODULE + case CRYPT_RAR13: + Decrypt13(Buf,Size); + break; + case CRYPT_RAR15: + Crypt15(Buf,Size); + break; + case CRYPT_RAR20: + for (size_t I=0;I<Size;I+=CRYPT_BLOCK_SIZE) + DecryptBlock20(Buf+I); + break; #endif - UpdKeys(InBuf); + case CRYPT_RAR30: + case CRYPT_RAR50: + rin.blockDecrypt(Buf,Size,Buf); + break; + } } -void CryptData::UpdKeys(byte *Buf) +bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method, + SecPassword *Password,const byte *Salt, + const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck) { - for (int I=0;I<16;I+=4) - { - Key[0]^=CRCTab[Buf[I]]; - Key[1]^=CRCTab[Buf[I+1]]; - Key[2]^=CRCTab[Buf[I+2]]; - Key[3]^=CRCTab[Buf[I+3]]; - } -} + if (Method==CRYPT_NONE || !Password->IsSet()) + return false; + CryptData::Method=Method; -void CryptData::Swap(byte *Ch1,byte *Ch2) -{ - byte Ch=*Ch1; - *Ch1=*Ch2; - *Ch2=Ch; -} -#endif + wchar PwdW[MAXPASSWORD]; + Password->Get(PwdW,ASIZE(PwdW)); + PwdW[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. + char PwdA[MAXPASSWORD]; + WideToChar(PwdW,PwdA,ASIZE(PwdA)); + PwdA[Min(MAXPASSWORD_RAR,MAXPASSWORD)-1]=0; // For compatibility with existing archives. -void CryptData::SetCryptKeys(char *Password,byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash) -{ - if (*Password==0) - return; - if (OldOnly) + switch(Method) { #ifndef SFX_MODULE - if (CRCTab[1]==0) - InitCRC(); - byte Psw[MAXPASSWORD]; - SetOldKeys(Password); - Key[0]=0xD3A3B879L; - Key[1]=0x3F6D12F7L; - Key[2]=0x7515A235L; - Key[3]=0xA4E7F123L; - memset(Psw,0,sizeof(Psw)); -#if defined(_WIN_32) && !defined(GUI) - CharToOemBuff(Password,(char*)Psw,strlen(Password)); -#else - strncpyz((char *)Psw,Password,ASIZE(Psw)); -#endif - int PswLength=strlen(Password); - memcpy(SubstTable,InitSubstTable,sizeof(SubstTable)); - for (int J=0;J<256;J++) - for (int I=0;I<PswLength;I+=2) - { - uint N1=(byte)CRCTab[(Psw[I]-J)&0xff]; - uint N2=(byte)CRCTab[(Psw[I+1]+J)&0xff]; - for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++) - Swap(&SubstTable[N1],&SubstTable[(N1+I+K)&0xff]); - } - for (int I=0;I<PswLength;I+=16) - EncryptBlock20(&Psw[I]); + case CRYPT_RAR13: + SetKey13(PwdA); + break; + case CRYPT_RAR15: + SetKey15(PwdA); + break; + case CRYPT_RAR20: + SetKey20(PwdA); + break; #endif - return; - } - - bool Cached=false; - for (int I=0;I<sizeof(Cache)/sizeof(Cache[0]);I++) - if (strcmp(Cache[I].Password,Password)==0 && - (Salt==NULL && !Cache[I].SaltPresent || Salt!=NULL && - Cache[I].SaltPresent && memcmp(Cache[I].Salt,Salt,SALT_SIZE)==0) && - Cache[I].HandsOffHash==HandsOffHash) - { - memcpy(AESKey,Cache[I].AESKey,sizeof(AESKey)); - memcpy(AESInit,Cache[I].AESInit,sizeof(AESInit)); - Cached=true; + case CRYPT_RAR30: + SetKey30(Encrypt,Password,PwdW,Salt); + break; + case CRYPT_RAR50: + SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); break; - } - - if (!Cached) - { - wchar PswW[MAXPASSWORD]; - CharToWide(Password,PswW,MAXPASSWORD-1); - PswW[MAXPASSWORD-1]=0; - byte RawPsw[2*MAXPASSWORD+SALT_SIZE]; - WideToRaw(PswW,RawPsw); - int RawLength=2*strlenw(PswW); - if (Salt!=NULL) - { - memcpy(RawPsw+RawLength,Salt,SALT_SIZE); - RawLength+=SALT_SIZE; - } - hash_context c; - hash_initial(&c); - - const int HashRounds=0x40000; - for (int I=0;I<HashRounds;I++) - { - hash_process( &c, RawPsw, RawLength, HandsOffHash); - byte PswNum[3]; - PswNum[0]=(byte)I; - PswNum[1]=(byte)(I>>8); - PswNum[2]=(byte)(I>>16); - hash_process( &c, PswNum, 3, HandsOffHash); - if (I%(HashRounds/16)==0) - { - hash_context tempc=c; - uint32 digest[5]; - hash_final( &tempc, digest, HandsOffHash); - AESInit[I/(HashRounds/16)]=(byte)digest[4]; - } - } - uint32 digest[5]; - hash_final( &c, digest, HandsOffHash); - for (int I=0;I<4;I++) - for (int J=0;J<4;J++) - AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); - - strcpy(Cache[CachePos].Password,Password); - if ((Cache[CachePos].SaltPresent=(Salt!=NULL))==true) - memcpy(Cache[CachePos].Salt,Salt,SALT_SIZE); - Cache[CachePos].HandsOffHash=HandsOffHash; - memcpy(Cache[CachePos].AESKey,AESKey,sizeof(AESKey)); - memcpy(Cache[CachePos].AESInit,AESInit,sizeof(AESInit)); - CachePos=(CachePos+1)%(sizeof(Cache)/sizeof(Cache[0])); } - rin.init(Encrypt ? Rijndael::Encrypt : Rijndael::Decrypt,AESKey,AESInit); + cleandata(PwdA,sizeof(PwdA)); + cleandata(PwdW,sizeof(PwdW)); + return true; } -#ifndef SFX_MODULE -void CryptData::SetOldKeys(char *Password) +// Use the current system time to additionally randomize data. +static void TimeRandomize(byte *RndBuf,size_t BufSize) { - uint PswCRC=CRC(0xffffffff,Password,strlen(Password)); - OldKey[0]=PswCRC&0xffff; - OldKey[1]=(PswCRC>>16)&0xffff; - OldKey[2]=OldKey[3]=0; - PN1=PN2=PN3=0; - byte Ch; - while ((Ch=*Password)!=0) + static uint Count=0; + RarTime CurTime; + CurTime.SetCurrentTime(); + uint64 Random=CurTime.GetWin()+clock(); + for (size_t I=0;I<BufSize;I++) { - PN1+=Ch; - PN2^=Ch; - PN3+=Ch; - PN3=(byte)rol(PN3,1,8); - OldKey[2]^=Ch^CRCTab[Ch]; - OldKey[3]+=Ch+(CRCTab[Ch]>>16); - Password++; + byte RndByte = byte (Random >> ( (I & 7) * 8 )); + RndBuf[I]=byte( (RndByte ^ I) + Count++); } } -void CryptData::SetAV15Encryption() -{ - OldKey[0]=0x4765; - OldKey[1]=0x9021; - OldKey[2]=0x7382; - OldKey[3]=0x5215; -} - - -void CryptData::SetCmt13Encryption() -{ - PN1=0; - PN2=7; - PN3=77; -} -void CryptData::Crypt(byte *Data,uint Count,int Method) +// Fill buffer with random data. +void GetRnd(byte *RndBuf,size_t BufSize) { - if (Method==OLD_DECODE) - Decode13(Data,Count); - else - if (Method==OLD_ENCODE) - Encode13(Data,Count); - else - Crypt15(Data,Count); -} - - -void CryptData::Encode13(byte *Data,uint Count) -{ - while (Count--) - { - PN2+=PN3; - PN1+=PN2; - *Data+=PN1; - Data++; - } -} - - -void CryptData::Decode13(byte *Data,uint Count) -{ - while (Count--) + bool Success=false; +#if defined(_WIN_ALL) + HCRYPTPROV hProvider = 0; + if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - PN2+=PN3; - PN1+=PN2; - *Data-=PN1; - Data++; + Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE; + CryptReleaseContext(hProvider, 0); } -} - - -void CryptData::Crypt15(byte *Data,uint Count) -{ - while (Count--) +#elif defined(_UNIX) + FILE *rndf = fopen("/dev/urandom", "r"); + if (rndf!=NULL) { - OldKey[0]+=0x1234; - OldKey[1]^=CRCTab[(OldKey[0] & 0x1fe)>>1]; - OldKey[2]-=CRCTab[(OldKey[0] & 0x1fe)>>1]>>16; - OldKey[0]^=OldKey[2]; - OldKey[3]=ror(OldKey[3]&0xffff,1,16)^OldKey[1]; - OldKey[3]=ror(OldKey[3]&0xffff,1,16); - OldKey[0]^=OldKey[3]; - *Data^=(byte)(OldKey[0]>>8); - Data++; + Success=fread(RndBuf, BufSize, 1, rndf) == BufSize; + fclose(rndf); } -} #endif - - + // We use this code only as the last resort if code above failed. + if (!Success) + TimeRandomize(RndBuf,BufSize); +} diff --git a/unrar/unrar/crypt.hpp b/unrar/unrar/crypt.hpp index e70aaa3..9dc0215 100644 --- a/unrar/unrar/crypt.hpp +++ b/unrar/unrar/crypt.hpp @@ -1,62 +1,171 @@ #ifndef _RAR_CRYPT_ #define _RAR_CRYPT_ -enum { OLD_DECODE=0,OLD_ENCODE=1,NEW_CRYPT=2 }; +enum CRYPT_METHOD { + CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50 +}; + +#define SIZE_SALT50 16 +#define SIZE_SALT30 8 +#define SIZE_INITV 16 +#define SIZE_PSWCHECK 8 +#define SIZE_PSWCHECK_CSUM 4 + +#define CRYPT_BLOCK_SIZE 16 +#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf -struct CryptKeyCacheItem +#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count. +#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count. +#define CRYPT_VERSION 0 // Supported encryption version. + + +class CryptData { -#ifndef _SFX_RTL_ - CryptKeyCacheItem() + struct KDF5CacheItem { - *Password=0; - } + SecPassword Pwd; + byte Salt[SIZE_SALT50]; + byte Key[32]; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte PswCheckValue[SHA256_DIGEST_SIZE]; + byte HashKeyValue[SHA256_DIGEST_SIZE]; - ~CryptKeyCacheItem() + KDF5CacheItem() {Clean();} + ~KDF5CacheItem() {Clean();} + + void Clean() + { + cleandata(Salt,sizeof(Salt)); + cleandata(Key,sizeof(Key)); + cleandata(&Lg2Count,sizeof(Lg2Count)); + cleandata(PswCheckValue,sizeof(PswCheckValue)); + cleandata(HashKeyValue,sizeof(HashKeyValue)); + } + }; + + struct KDF3CacheItem { - memset(AESKey,0,sizeof(AESKey)); - memset(AESInit,0,sizeof(AESInit)); - memset(Password,0,sizeof(Password)); - } -#endif - byte AESKey[16],AESInit[16]; - char Password[MAXPASSWORD]; - bool SaltPresent; - byte Salt[SALT_SIZE]; - bool HandsOffHash; -}; + SecPassword Pwd; + byte Salt[SIZE_SALT30]; + byte Key[16]; + byte Init[16]; + bool SaltPresent; + + KDF3CacheItem() {Clean();} + ~KDF3CacheItem() {Clean();} + + void Clean() + { + cleandata(Salt,sizeof(Salt)); + cleandata(Key,sizeof(Key)); + cleandata(Init,sizeof(Init)); + cleandata(&SaltPresent,sizeof(SaltPresent)); + } + }; + -class CryptData -{ private: - void Encode13(byte *Data,uint Count); - void Decode13(byte *Data,uint Count); - void Crypt15(byte *Data,uint Count); - void UpdKeys(byte *Buf); - void Swap(byte *Ch1,byte *Ch2); - void SetOldKeys(char *Password); + void SetKey13(const char *Password); + void Decrypt13(byte *Data,size_t Count); - Rijndael rin; + void SetKey15(const char *Password); + void Crypt15(byte *Data,size_t Count); + + void SetKey20(const char *Password); + void Swap20(byte *Ch1,byte *Ch2); + void UpdKeys20(byte *Buf); + void EncryptBlock20(byte *Buf); + void DecryptBlock20(byte *Buf); + + void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt); + void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); + + KDF3CacheItem KDF3Cache[4]; + uint KDF3CachePos; - byte SubstTable[256]; - uint Key[4]; - ushort OldKey[4]; - byte PN1,PN2,PN3; + KDF5CacheItem KDF5Cache[4]; + uint KDF5CachePos; - byte AESKey[16],AESInit[16]; + CRYPT_METHOD Method; - static CryptKeyCacheItem Cache[4]; - static int CachePos; + Rijndael rin; + + uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption. + + byte SubstTable20[256]; + uint Key20[4]; + + byte Key13[3]; + ushort Key15[4]; public: - void SetCryptKeys(char *Password,byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash); + CryptData(); + bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, + const byte *Salt,const byte *InitV,uint Lg2Cnt, + byte *HashKey,byte *PswCheck); void SetAV15Encryption(); void SetCmt13Encryption(); - void EncryptBlock20(byte *Buf); - void DecryptBlock20(byte *Buf); - void EncryptBlock(byte *Buf,int Size); - void DecryptBlock(byte *Buf,int Size); - void Crypt(byte *Data,uint Count,int Method); - static void SetSalt(byte *Salt,int SaltSize); + void EncryptBlock(byte *Buf,size_t Size); + void DecryptBlock(byte *Buf,size_t Size); + static void SetSalt(byte *Salt,size_t SaltSize); +}; + + +class CheckPassword +{ + public: + enum CONFIDENCE {CONFIDENCE_HIGH,CONFIDENCE_MEDIUM,CONFIDENCE_LOW}; + virtual CONFIDENCE GetConfidence()=0; + virtual bool Check(SecPassword *Password)=0; }; +class RarCheckPassword:public CheckPassword +{ + private: + CryptData *Crypt; + uint Lg2Count; + byte Salt[SIZE_SALT50]; + byte InitV[SIZE_INITV]; + byte PswCheck[SIZE_PSWCHECK]; + public: + RarCheckPassword() + { + Crypt=NULL; + } + ~RarCheckPassword() + { + delete Crypt; + } + void Set(byte *Salt,byte *InitV,uint Lg2Count,byte *PswCheck) + { + if (Crypt==NULL) + Crypt=new CryptData; + memcpy(this->Salt,Salt,sizeof(this->Salt)); + memcpy(this->InitV,InitV,sizeof(this->InitV)); + this->Lg2Count=Lg2Count; + memcpy(this->PswCheck,PswCheck,sizeof(this->PswCheck)); + } + bool IsSet() {return Crypt!=NULL;} + + // RAR5 provides the higly reliable 64 bit password verification value. + CONFIDENCE GetConfidence() {return CONFIDENCE_HIGH;} + + bool Check(SecPassword *Password) + { + byte PswCheck[SIZE_PSWCHECK]; + Crypt->SetCryptKeys(false,CRYPT_RAR50,Password,Salt,InitV,Lg2Count,NULL,PswCheck); + return memcmp(PswCheck,this->PswCheck,sizeof(this->PswCheck))==0; + } +}; + +void GetRnd(byte *RndBuf,size_t BufSize); + +void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest); +void pbkdf2(const byte *pass, size_t pass_len, const byte *salt, + size_t salt_len,byte *key, byte *Value1, byte *Value2, + uint rounds); + +void ConvertHashToMAC(HashValue *Value,byte *Key); + #endif diff --git a/unrar/unrar/crypt1.cpp b/unrar/unrar/crypt1.cpp new file mode 100644 index 0000000..1426393 --- /dev/null +++ b/unrar/unrar/crypt1.cpp @@ -0,0 +1,79 @@ +extern uint CRCTab[256]; + +void CryptData::SetKey13(const char *Password) +{ + Key13[0]=Key13[1]=Key13[2]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key13[0]+=P; + Key13[1]^=P; + Key13[2]+=P; + Key13[2]=(byte)rotls(Key13[2],1,8); + } +} + + +void CryptData::SetKey15(const char *Password) +{ + InitCRC32(CRCTab); + uint PswCRC=CRC32(0xffffffff,Password,strlen(Password)); + Key15[0]=PswCRC&0xffff; + Key15[1]=(PswCRC>>16)&0xffff; + Key15[2]=Key15[3]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key15[2]^=P^CRCTab[P]; + Key15[3]+=P+(CRCTab[P]>>16); + } +} + + +void CryptData::SetAV15Encryption() +{ + InitCRC32(CRCTab); + Method=CRYPT_RAR15; + Key15[0]=0x4765; + Key15[1]=0x9021; + Key15[2]=0x7382; + Key15[3]=0x5215; +} + + +void CryptData::SetCmt13Encryption() +{ + Method=CRYPT_RAR13; + Key13[0]=0; + Key13[1]=7; + Key13[2]=77; +} + + +void CryptData::Decrypt13(byte *Data,size_t Count) +{ + while (Count--) + { + Key13[1]+=Key13[2]; + Key13[0]+=Key13[1]; + *Data-=Key13[0]; + Data++; + } +} + + +void CryptData::Crypt15(byte *Data,size_t Count) +{ + while (Count--) + { + Key15[0]+=0x1234; + Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1]; + Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16; + Key15[0]^=Key15[2]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16); + Key15[0]^=Key15[3]; + *Data^=(byte)(Key15[0]>>8); + Data++; + } +} diff --git a/unrar/unrar/crypt2.cpp b/unrar/unrar/crypt2.cpp new file mode 100644 index 0000000..5fa4a97 --- /dev/null +++ b/unrar/unrar/crypt2.cpp @@ -0,0 +1,133 @@ +#define NROUNDS 32 + +#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \ + ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \ + ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \ + ((uint)SubstTable20[(int)(t>>24)&255]<<24) ) + + +static byte InitSubstTable20[256]={ + 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, + 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, + 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, + 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, + 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, + 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, + 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, + 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, + 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, + 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, + 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, + 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, + 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, + 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, + 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, + 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 +}; + + +void CryptData::SetKey20(const char *Password) +{ + InitCRC32(CRCTab); + + char Psw[MAXPASSWORD]; + strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below. + size_t PswLength=strlen(Psw); + + Key20[0]=0xD3A3B879L; + Key20[1]=0x3F6D12F7L; + Key20[2]=0x7515A235L; + Key20[3]=0xA4E7F123L; + + memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20)); + for (uint J=0;J<256;J++) + for (size_t I=0;I<PswLength;I+=2) + { + uint N1=(byte)CRCTab [ (byte(Password[I]) - J) &0xff]; + uint N2=(byte)CRCTab [ (byte(Password[I+1]) + J) &0xff]; + for (int K=1;N1!=N2;N1=(N1+1)&0xff,K++) + Swap20(&SubstTable20[N1],&SubstTable20[(N1+I+K)&0xff]); + } + + // Incomplete last block of password must be zero padded. + if ((PswLength & CRYPT_BLOCK_MASK)!=0) + for (size_t I=PswLength;I<=(PswLength|CRYPT_BLOCK_MASK);I++) + Psw[I]=0; + + for (size_t I=0;I<PswLength;I+=CRYPT_BLOCK_SIZE) + EncryptBlock20((byte *)Psw+I); +} + + +void CryptData::EncryptBlock20(byte *Buf) +{ + uint A,B,C,D,T,TA,TB; + A=RawGet4(Buf+0)^Key20[0]; + B=RawGet4(Buf+4)^Key20[1]; + C=RawGet4(Buf+8)^Key20[2]; + D=RawGet4(Buf+12)^Key20[3]; + for(int I=0;I<NROUNDS;I++) + { + T=((C+rotls(D,11,32))^Key20[I&3]); + TA=A^substLong(T); + T=((D^rotls(C,17,32))+Key20[I&3]); + TB=B^substLong(T); + A=C; + B=D; + C=TA; + D=TB; + } + RawPut4(C^Key20[0],Buf+0); + RawPut4(D^Key20[1],Buf+4); + RawPut4(A^Key20[2],Buf+8); + RawPut4(B^Key20[3],Buf+12); + UpdKeys20(Buf); +} + + +void CryptData::DecryptBlock20(byte *Buf) +{ + byte InBuf[16]; + uint A,B,C,D,T,TA,TB; + A=RawGet4(Buf+0)^Key20[0]; + B=RawGet4(Buf+4)^Key20[1]; + C=RawGet4(Buf+8)^Key20[2]; + D=RawGet4(Buf+12)^Key20[3]; + memcpy(InBuf,Buf,sizeof(InBuf)); + for(int I=NROUNDS-1;I>=0;I--) + { + T=((C+rotls(D,11,32))^Key20[I&3]); + TA=A^substLong(T); + T=((D^rotls(C,17,32))+Key20[I&3]); + TB=B^substLong(T); + A=C; + B=D; + C=TA; + D=TB; + } + RawPut4(C^Key20[0],Buf+0); + RawPut4(D^Key20[1],Buf+4); + RawPut4(A^Key20[2],Buf+8); + RawPut4(B^Key20[3],Buf+12); + UpdKeys20(InBuf); +} + + +void CryptData::UpdKeys20(byte *Buf) +{ + for (int I=0;I<16;I+=4) + { + Key20[0]^=CRCTab[Buf[I]]; + Key20[1]^=CRCTab[Buf[I+1]]; + Key20[2]^=CRCTab[Buf[I+2]]; + Key20[3]^=CRCTab[Buf[I+3]]; + } +} + + +void CryptData::Swap20(byte *Ch1,byte *Ch2) +{ + byte Ch=*Ch1; + *Ch1=*Ch2; + *Ch2=Ch; +} diff --git a/unrar/unrar/crypt3.cpp b/unrar/unrar/crypt3.cpp new file mode 100644 index 0000000..e6e3a82 --- /dev/null +++ b/unrar/unrar/crypt3.cpp @@ -0,0 +1,69 @@ +void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt) +{ + byte AESKey[16],AESInit[16]; + + bool Cached=false; + for (uint I=0;I<ASIZE(KDF3Cache);I++) + if (KDF3Cache[I].Pwd==*Password && + (Salt==NULL && !KDF3Cache[I].SaltPresent || Salt!=NULL && + KDF3Cache[I].SaltPresent && memcmp(KDF3Cache[I].Salt,Salt,SIZE_SALT30)==0)) + { + memcpy(AESKey,KDF3Cache[I].Key,sizeof(AESKey)); + SecHideData(AESKey,sizeof(AESKey),false,false); + memcpy(AESInit,KDF3Cache[I].Init,sizeof(AESInit)); + Cached=true; + break; + } + + if (!Cached) + { + byte RawPsw[2*MAXPASSWORD+SIZE_SALT30]; + size_t PswLength=wcslen(PwdW); + size_t RawLength=2*PswLength; + WideToRaw(PwdW,PswLength,RawPsw,RawLength); + if (Salt!=NULL) + { + memcpy(RawPsw+RawLength,Salt,SIZE_SALT30); + RawLength+=SIZE_SALT30; + } + sha1_context c; + sha1_init(&c); + + const uint HashRounds=0x40000; + for (uint I=0;I<HashRounds;I++) + { + sha1_process_rar29( &c, RawPsw, RawLength ); + byte PswNum[3]; + PswNum[0]=(byte)I; + PswNum[1]=(byte)(I>>8); + PswNum[2]=(byte)(I>>16); + sha1_process(&c, PswNum, 3); + if (I%(HashRounds/16)==0) + { + sha1_context tempc=c; + uint32 digest[5]; + sha1_done( &tempc, digest ); + AESInit[I/(HashRounds/16)]=(byte)digest[4]; + } + } + uint32 digest[5]; + sha1_done( &c, digest ); + for (uint I=0;I<4;I++) + for (uint J=0;J<4;J++) + AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); + + KDF3Cache[KDF3CachePos].Pwd=*Password; + if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true) + memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30); + memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey)); + SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false); + memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit)); + KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache); + + cleandata(RawPsw,sizeof(RawPsw)); + } + rin.Init(Encrypt, AESKey, 128, AESInit); + cleandata(AESKey,sizeof(AESKey)); + cleandata(AESInit,sizeof(AESInit)); +} + diff --git a/unrar/unrar/crypt5.cpp b/unrar/unrar/crypt5.cpp new file mode 100644 index 0000000..bb9b2ba --- /dev/null +++ b/unrar/unrar/crypt5.cpp @@ -0,0 +1,241 @@ +static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest, + sha256_context *ICtxOpt,bool *SetIOpt, + sha256_context *RCtxOpt,bool *SetROpt) +{ + const size_t Sha256BlockSize=64; // As defined in RFC 4868. + + byte KeyHash[SHA256_DIGEST_SIZE]; + if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash. + { + sha256_context KCtx; + sha256_init(&KCtx); + sha256_process(&KCtx, Key, KeyLength); + sha256_done(&KCtx, KeyHash); + + Key = KeyHash; + KeyLength = SHA256_DIGEST_SIZE; + } + + byte KeyBuf[Sha256BlockSize]; // Store the padded key here. + sha256_context ICtx; + + if (ICtxOpt!=NULL && *SetIOpt) + ICtx=*ICtxOpt; // Use already calculated the first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest. + KeyBuf[I] = Key[I] ^ 0x36; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x36; + + sha256_init(&ICtx); + sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse. + { + *ICtxOpt=ICtx; + *SetIOpt=true; + } + + sha256_process(&ICtx, Data, DataLength); // Hash data. + + byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data. + sha256_done(&ICtx, IDig); + + sha256_context RCtx; + + if (RCtxOpt!=NULL && *SetROpt) + RCtx=*RCtxOpt; // Use already calculated first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding. + KeyBuf[I] = Key[I] ^ 0x5c; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x5c; + + sha256_init(&RCtx); + sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse. + { + *RCtxOpt=RCtx; + *SetROpt=true; + } + + sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest. + + sha256_done(&RCtx, ResDigest); +} + + +// PBKDF2 for 32 byte key length. We generate the key for specified number +// of iteration count also as two supplementary values (key for checksums +// and password verification) for iterations+16 and iterations+32. +void pbkdf2(const byte *Pwd, size_t PwdLength, + const byte *Salt, size_t SaltLength, + byte *Key, byte *V1, byte *V2, uint Count) +{ + const size_t MaxSalt=64; + byte SaltData[MaxSalt+4]; + memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); + + SaltData[SaltLength + 0] = 0; // Block index appened to salt. + SaltData[SaltLength + 1] = 0; // + SaltData[SaltLength + 2] = 0; // Since we do not request the key width + SaltData[SaltLength + 3] = 1; // exceeding HMAC width, it is always 1. + + // First iteration: HMAC of password, salt and block index (1). + byte U1[SHA256_DIGEST_SIZE]; + hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL); + byte Fn[SHA256_DIGEST_SIZE]; // Current function value. + memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration. + + uint CurCount[] = { Count-1, 16, 16 }; + byte *CurValue[] = { Key , V1, V2 }; + + sha256_context ICtxOpt,RCtxOpt; + bool SetIOpt=false,SetROpt=false; + + byte U2[SHA256_DIGEST_SIZE]; + for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values. + { + for (uint J = 0; J < CurCount[I]; J++) + { + // U2 = PRF (P, U1). + hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt); + memcpy(U1, U2, sizeof(U1)); + for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U. + Fn[K] ^= U1[K]; + } + memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE); + } + + cleandata(SaltData, sizeof(SaltData)); + cleandata(Fn, sizeof(Fn)); + cleandata(U1, sizeof(U1)); + cleandata(U2, sizeof(U2)); +} + + +void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, + const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey, + byte *PswCheck) +{ + if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX) + { + // Initialize these fields to prevent uninitialized data access warnings + // by analyzing tools when accessing returned data. + if (HashKey!=nullptr) + memset(HashKey,0,SHA256_DIGEST_SIZE); + if (PswCheck!=nullptr) + memset(PswCheck,0,SIZE_PSWCHECK); + return; + } + + byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; + bool Found=false; + for (uint I=0;I<ASIZE(KDF5Cache);I++) + { + KDF5CacheItem *Item=KDF5Cache+I; + if (Item->Pwd==*Password && Item->Lg2Count==Lg2Cnt && + memcmp(Item->Salt,Salt,SIZE_SALT50)==0) + { + memcpy(Key,Item->Key,sizeof(Key)); + SecHideData(Key,sizeof(Key),false,false); + + memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue)); + memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue)); + Found=true; + break; + } + } + + if (!Found) + { + char PwdUtf[MAXPASSWORD*4]; + WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf)); + + pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<<Lg2Cnt)); + cleandata(PwdUtf,sizeof(PwdUtf)); + + KDF5CacheItem *Item=KDF5Cache+(KDF5CachePos++ % ASIZE(KDF5Cache)); + Item->Lg2Count=Lg2Cnt; + Item->Pwd=*Password; + memcpy(Item->Salt,Salt,SIZE_SALT50); + memcpy(Item->Key,Key,sizeof(Item->Key)); + memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue)); + memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue)); + SecHideData(Item->Key,sizeof(Item->Key),true,false); + } + if (HashKey!=NULL) + memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE); + if (PswCheck!=NULL) + { + memset(PswCheck,0,SIZE_PSWCHECK); + for (uint I=0;I<SHA256_DIGEST_SIZE;I++) + PswCheck[I%SIZE_PSWCHECK]^=PswCheckValue[I]; + cleandata(PswCheckValue,sizeof(PswCheckValue)); + } + + // NULL initialization vector is possible if we only need the password + // check value for archive encryption header. + if (InitV!=NULL) + rin.Init(Encrypt, Key, 256, InitV); + + cleandata(Key,sizeof(Key)); +} + + +void ConvertHashToMAC(HashValue *Value,byte *Key) +{ + if (Value->Type==HASH_CRC32) + { + byte RawCRC[4]; + RawPut4(Value->CRC32,RawCRC); + byte Digest[SHA256_DIGEST_SIZE]; + hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL); + Value->CRC32=0; + for (uint I=0;I<ASIZE(Digest);I++) + Value->CRC32^=Digest[I] << ((I & 3) * 8); + } + if (Value->Type==HASH_BLAKE2) + { + byte Digest[BLAKE2_DIGEST_SIZE]; + hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL); + memcpy(Value->Digest,Digest,sizeof(Value->Digest)); + } +} + + +#if 0 +static void TestPBKDF2(); +struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF; + +void TestPBKDF2() // Test PBKDF2 HMAC-SHA256 +{ + byte Key[32],V1[32],V2[32]; + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1); + byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b }; + mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096); + byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a }; + mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536); + byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf}; + mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed"); +} +#endif diff --git a/unrar/unrar/dll.cpp b/unrar/unrar/dll.cpp index 18e62bd..178d559 100644 --- a/unrar/unrar/dll.cpp +++ b/unrar/unrar/dll.cpp @@ -1,17 +1,16 @@ #include "rar.hpp" -#include "dll.hpp" -static int RarErrorToDll(int ErrCode); +static int RarErrorToDll(RAR_EXIT ErrCode); struct DataSet { CommandData Cmd; - CmdExtract Extract; Archive Arc; + CmdExtract Extract; int OpenMode; int HeaderSize; - DataSet():Arc(&Cmd) {}; + DataSet():Arc(&Cmd),Extract(&Cmd) {}; }; @@ -27,124 +26,183 @@ HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r) r->OpenResult=rx.OpenResult; r->CmtSize=rx.CmtSize; r->CmtState=rx.CmtState; - return(hArc); + return hArc; } HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) { + DataSet *Data=NULL; try { + ErrHandler.Clean(); + r->OpenResult=0; - DataSet *Data=new DataSet; + Data=new DataSet; Data->Cmd.DllError=0; Data->OpenMode=r->OpenMode; - Data->Cmd.FileArgs->AddString("*"); + Data->Cmd.FileArgs.AddString(L"*"); + Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0; - char an[NM]; - if (r->ArcName==NULL && r->ArcNameW!=NULL) + char AnsiArcName[NM]; + *AnsiArcName=0; + if (r->ArcName!=NULL) { - WideToChar(r->ArcNameW,an,NM); - r->ArcName=an; + strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName)); +#ifdef _WIN_ALL + if (!AreFileApisANSI()) + { + OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName)); + AnsiArcName[ASIZE(AnsiArcName)-1]=0; + } +#endif } - Data->Cmd.AddArcName(r->ArcName,r->ArcNameW); + wchar ArcName[NM]; + GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName)); + + Data->Cmd.AddArcName(ArcName); Data->Cmd.Overwrite=OVERWRITE_ALL; Data->Cmd.VersionControl=1; - if (!Data->Arc.Open(r->ArcName,r->ArcNameW)) + + Data->Cmd.Callback=r->Callback; + Data->Cmd.UserData=r->UserData; + + // Open shared mode is added by request of dll users, who need to + // browse and unpack archives while downloading. + Data->Cmd.OpenShared = true; + if (!Data->Arc.Open(ArcName,FMF_OPENSHARED)) { r->OpenResult=ERAR_EOPEN; delete Data; - return(NULL); + return NULL; } - if (!Data->Arc.IsArchive(false)) + if (!Data->Arc.IsArchive(true)) { - r->OpenResult=Data->Cmd.DllError!=0 ? Data->Cmd.DllError:ERAR_BAD_ARCHIVE; + if (Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + { + RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); + if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING) + r->OpenResult=RarErrorToDll(ErrCode); + else + r->OpenResult=ERAR_BAD_ARCHIVE; + } delete Data; - return(NULL); + return NULL; } - r->Flags=Data->Arc.NewMhd.Flags; - Array<byte> CmtData; - if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtData,NULL)) + r->Flags=0; + + if (Data->Arc.Volume) + r->Flags|=ROADF_VOLUME; + if (Data->Arc.MainComment) + r->Flags|=ROADF_COMMENT; + if (Data->Arc.Locked) + r->Flags|=ROADF_LOCK; + if (Data->Arc.Solid) + r->Flags|=ROADF_SOLID; + if (Data->Arc.NewNumbering) + r->Flags|=ROADF_NEWNUMBERING; + if (Data->Arc.Signed) + r->Flags|=ROADF_SIGNED; + if (Data->Arc.Protected) + r->Flags|=ROADF_RECOVERY; + if (Data->Arc.Encrypted) + r->Flags|=ROADF_ENCHEADERS; + if (Data->Arc.FirstVolume) + r->Flags|=ROADF_FIRSTVOLUME; + + Array<wchar> CmtDataW; + if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) { - r->Flags|=2; - int Size=CmtData.Size()+1; - r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; - r->CmtSize=Min(Size,r->CmtBufSize); - memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); - if (Size<=r->CmtBufSize) - r->CmtBuf[r->CmtSize-1]=0; + if (r->CmtBufW!=NULL) + { + CmtDataW.Push(0); + size_t Size=wcslen(&CmtDataW[0])+1; + + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW)); + r->CmtBufW[r->CmtSize-1]=0; + } + else + if (r->CmtBuf!=NULL) + { + Array<char> CmtData(CmtDataW.Size()*4+1); + memset(&CmtData[0],0,CmtData.Size()); + WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1); + size_t Size=strlen(&CmtData[0])+1; + + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); + r->CmtBuf[r->CmtSize-1]=0; + } } else r->CmtState=r->CmtSize=0; - if (Data->Arc.Signed) - r->Flags|=0x20; - Data->Extract.ExtractArchiveInit(&Data->Cmd,Data->Arc); - return((HANDLE)Data); + Data->Extract.ExtractArchiveInit(Data->Arc); + return (HANDLE)Data; + } + catch (RAR_EXIT ErrCode) + { + if (Data!=NULL && Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + r->OpenResult=RarErrorToDll(ErrCode); + if (Data != NULL) + delete Data; + return NULL; } - catch (int ErrCode) + catch (std::bad_alloc&) // Catch 'new' exception. { - r->OpenResult=RarErrorToDll(ErrCode); - return(NULL); + r->OpenResult=ERAR_NO_MEMORY; + if (Data != NULL) + delete Data; } + return NULL; // To make compilers happy. } int PASCAL RARCloseArchive(HANDLE hArcData) { DataSet *Data=(DataSet *)hArcData; - bool Success=Data==NULL ? false:Data->Arc.Close(); - delete Data; - return(Success ? 0:ERAR_ECLOSE); -} - - -int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) -{ - DataSet *Data=(DataSet *)hArcData; try { - if ((Data->HeaderSize=Data->Arc.SearchBlock(FILE_HEAD))<=0) - { - if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD && - (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)) - if (MergeArchive(Data->Arc,NULL,false,'L')) - { - Data->Extract.SignatureFound=false; - Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); - return(RARReadHeader(hArcData,D)); - } - else - return(ERAR_EOPEN); - return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE); - } - if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)) - { - int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); - if (Code==0) - return(RARReadHeader(hArcData,D)); - else - return(Code); - } - strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName)); - strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName)); - D->Flags=Data->Arc.NewLhd.Flags; - D->PackSize=Data->Arc.NewLhd.PackSize; - D->UnpSize=Data->Arc.NewLhd.UnpSize; - D->HostOS=Data->Arc.NewLhd.HostOS; - D->FileCRC=Data->Arc.NewLhd.FileCRC; - D->FileTime=Data->Arc.NewLhd.FileTime; - D->UnpVer=Data->Arc.NewLhd.UnpVer; - D->Method=Data->Arc.NewLhd.Method; - D->FileAttr=Data->Arc.NewLhd.FileAttr; - D->CmtSize=0; - D->CmtState=0; + bool Success=Data==NULL ? false:Data->Arc.Close(); + delete Data; + return Success ? ERAR_SUCCESS : ERAR_ECLOSE; } - catch (int ErrCode) + catch (RAR_EXIT ErrCode) { - return(RarErrorToDll(ErrCode)); + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } - return(0); +} + + +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) +{ + struct RARHeaderDataEx X; + memset(&X,0,sizeof(X)); + + int Code=RARReadHeaderEx(hArcData,&X); + + strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName)); + strncpyz(D->FileName,X.FileName,ASIZE(D->FileName)); + D->Flags=X.Flags; + D->PackSize=X.PackSize; + D->UnpSize=X.UnpSize; + D->HostOS=X.HostOS; + D->FileCRC=X.FileCRC; + D->FileTime=X.FileTime; + D->UnpVer=X.UnpVer; + D->Method=X.Method; + D->FileAttr=X.FileAttr; + D->CmtSize=0; + D->CmtState=0; + + return Code; } @@ -153,65 +211,114 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) DataSet *Data=(DataSet *)hArcData; try { - if ((Data->HeaderSize=Data->Arc.SearchBlock(FILE_HEAD))<=0) + if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0) { - if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD && - (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)) + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC && + Data->Arc.EndArcHead.NextVolume) if (MergeArchive(Data->Arc,NULL,false,'L')) { - Data->Extract.SignatureFound=false; Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); - return(RARReadHeaderEx(hArcData,D)); + return RARReadHeaderEx(hArcData,D); } else - return(ERAR_EOPEN); - return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE); + return ERAR_EOPEN; + + if (Data->Arc.BrokenHeader) + return ERAR_BAD_DATA; + + // Might be necessary if RARSetPassword is still called instead of + // open callback for RAR5 archives and if password is invalid. + if (Data->Arc.FailedHeaderDecryption) + return ERAR_BAD_PASSWORD; + + return ERAR_END_ARCHIVE; } - if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)) + FileHeader *hd=&Data->Arc.FileHead; + if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore) { int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); if (Code==0) - return(RARReadHeaderEx(hArcData,D)); + return RARReadHeaderEx(hArcData,D); else - return(Code); + return Code; } - strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName)); - if (*Data->Arc.FileNameW) - strncpyw(D->ArcNameW,Data->Arc.FileNameW,sizeof(D->ArcNameW)); - else - CharToWide(Data->Arc.FileName,D->ArcNameW); - strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName)); - if (*Data->Arc.NewLhd.FileNameW) - strncpyw(D->FileNameW,Data->Arc.NewLhd.FileNameW,sizeof(D->FileNameW)); - else - { -#ifdef _WIN_32 - char AnsiName[NM]; - OemToChar(Data->Arc.NewLhd.FileName,AnsiName); - CharToWide(AnsiName,D->FileNameW); -#else - CharToWide(Data->Arc.NewLhd.FileName,D->FileNameW); + wcsncpyz(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW)); + WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); + + wcsncpyz(D->FileNameW,hd->FileName,ASIZE(D->FileNameW)); + WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); +#ifdef _WIN_ALL + CharToOemA(D->FileName,D->FileName); #endif - } - D->Flags=Data->Arc.NewLhd.Flags; - D->PackSize=Data->Arc.NewLhd.PackSize; - D->PackSizeHigh=Data->Arc.NewLhd.HighPackSize; - D->UnpSize=Data->Arc.NewLhd.UnpSize; - D->UnpSizeHigh=Data->Arc.NewLhd.HighUnpSize; - D->HostOS=Data->Arc.NewLhd.HostOS; - D->FileCRC=Data->Arc.NewLhd.FileCRC; - D->FileTime=Data->Arc.NewLhd.FileTime; - D->UnpVer=Data->Arc.NewLhd.UnpVer; - D->Method=Data->Arc.NewLhd.Method; - D->FileAttr=Data->Arc.NewLhd.FileAttr; + + D->Flags=0; + if (hd->SplitBefore) + D->Flags|=RHDF_SPLITBEFORE; + if (hd->SplitAfter) + D->Flags|=RHDF_SPLITAFTER; + if (hd->Encrypted) + D->Flags|=RHDF_ENCRYPTED; + if (hd->Solid) + D->Flags|=RHDF_SOLID; + if (hd->Dir) + D->Flags|=RHDF_DIRECTORY; + + D->PackSize=uint(hd->PackSize & 0xffffffff); + D->PackSizeHigh=uint(hd->PackSize>>32); + D->UnpSize=uint(hd->UnpSize & 0xffffffff); + D->UnpSizeHigh=uint(hd->UnpSize>>32); + D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX; + D->UnpVer=Data->Arc.FileHead.UnpVer; + D->FileCRC=hd->FileHash.CRC32; + D->FileTime=hd->mtime.GetDos(); + + uint64 MRaw=hd->mtime.GetWin(); + D->MtimeLow=(uint)MRaw; + D->MtimeHigh=(uint)(MRaw>>32); + uint64 CRaw=hd->ctime.GetWin(); + D->CtimeLow=(uint)CRaw; + D->CtimeHigh=(uint)(CRaw>>32); + uint64 ARaw=hd->atime.GetWin(); + D->AtimeLow=(uint)ARaw; + D->AtimeHigh=(uint)(ARaw>>32); + + D->Method=hd->Method+0x30; + D->FileAttr=hd->FileAttr; D->CmtSize=0; D->CmtState=0; + + D->DictSize=uint(hd->WinSize/1024); + + switch (hd->FileHash.Type) + { + case HASH_RAR14: + case HASH_CRC32: + D->HashType=RAR_HASH_CRC32; + break; + case HASH_BLAKE2: + D->HashType=RAR_HASH_BLAKE2; + memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + break; + default: + D->HashType=RAR_HASH_NONE; + break; + } + + D->RedirType=hd->RedirType; + // RedirNameSize sanity check is useful in case some developer + // did not initialize Reserved area with 0 as required in docs. + // We have taken 'Redir*' fields from Reserved area. We may remove + // this RedirNameSize check sometimes later. + if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL && + D->RedirNameSize>0 && D->RedirNameSize<100000) + wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize); + D->DirTarget=hd->DirTarget; } - catch (int ErrCode) + catch (RAR_EXIT ErrCode) { - return(RarErrorToDll(ErrCode)); + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); } - return(0); + return ERAR_SUCCESS; } @@ -224,88 +331,101 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT || Operation==RAR_SKIP && !Data->Arc.Solid) { - if (Data->Arc.Volume && - Data->Arc.GetHeaderType()==FILE_HEAD && - (Data->Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0) + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE && + Data->Arc.FileHead.SplitAfter) if (MergeArchive(Data->Arc,NULL,false,'L')) { - Data->Extract.SignatureFound=false; Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); - return(0); + return ERAR_SUCCESS; } else - return(ERAR_EOPEN); + return ERAR_EOPEN; Data->Arc.SeekToNext(); } else { Data->Cmd.DllOpMode=Operation; - if (DestPath!=NULL || DestName!=NULL) + *Data->Cmd.ExtrPath=0; + *Data->Cmd.DllDestName=0; + + if (DestPath!=NULL) { -#ifdef _WIN_32 - OemToChar(NullToEmpty(DestPath),Data->Cmd.ExtrPath); -#else - strcpy(Data->Cmd.ExtrPath,NullToEmpty(DestPath)); -#endif - AddEndSlash(Data->Cmd.ExtrPath); -#ifdef _WIN_32 - OemToChar(NullToEmpty(DestName),Data->Cmd.DllDestName); -#else - strcpy(Data->Cmd.DllDestName,NullToEmpty(DestName)); + char ExtrPathA[NM]; + strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestPath, + // because we do not know DestPath length and OemToCharBuffA + // does not stop at 0. + OemToCharA(ExtrPathA,ExtrPathA); #endif + CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); } - else + if (DestName!=NULL) { - *Data->Cmd.ExtrPath=0; - *Data->Cmd.DllDestName=0; + char DestNameA[NM]; + strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestName, + // because we do not know DestName length and OemToCharBuffA + // does not stop at 0. + OemToCharA(DestNameA,DestNameA); +#endif + CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName)); } - if (DestPathW!=NULL || DestNameW!=NULL) + if (DestPathW!=NULL) { - strncpyw(Data->Cmd.ExtrPathW,NullToEmpty(DestPathW),NM-2); - AddEndSlash(Data->Cmd.ExtrPathW); - strncpyw(Data->Cmd.DllDestNameW,NullToEmpty(DestNameW),NM-1); - - if (*Data->Cmd.DllDestNameW!=0 && *Data->Cmd.DllDestName==0) - WideToChar(Data->Cmd.DllDestNameW,Data->Cmd.DllDestName); - } - else - { - *Data->Cmd.ExtrPathW=0; - *Data->Cmd.DllDestNameW=0; + wcsncpyz(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); } - strcpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? "X":"T"); + if (DestNameW!=NULL) + wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName)); + + wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command)); Data->Cmd.Test=Operation!=RAR_EXTRACT; bool Repeat=false; - Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat); - - while (Data->Arc.ReadHeader()!=0 && Data->Arc.GetHeaderType()==NEWSUB_HEAD) + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); + + // Now we process extra file information if any. + // + // Archive can be closed if we process volumes, next volume is missing + // and current one is already removed or deleted. So we need to check + // if archive is still open to avoid calling file operations on + // the invalid file handle. Some of our file operations like Seek() + // process such invalid handle correctly, some not. + while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && + Data->Arc.GetHeaderType()==HEAD_SERVICE) { - Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat); + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); Data->Arc.SeekToNext(); } Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); } } - catch (int ErrCode) + catch (std::bad_alloc&) { - return(RarErrorToDll(ErrCode)); + return ERAR_NO_MEMORY; } - return(Data->Cmd.DllError); + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } + return Data->Cmd.DllError; } int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName) { - return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL)); + return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL); } int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName) { - return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName)); + return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName); } @@ -330,40 +450,47 @@ void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataPro Data->Cmd.ProcessDataProc=ProcessDataProc; } -#ifndef NOCRYPT + void PASCAL RARSetPassword(HANDLE hArcData,char *Password) { +#ifndef RAR_NOCRYPT DataSet *Data=(DataSet *)hArcData; - strncpyz(Data->Cmd.Password,Password,ASIZE(Data->Cmd.Password)); -} + wchar PasswordW[MAXPASSWORD]; + GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); + Data->Cmd.Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); #endif +} int PASCAL RARGetDllVersion() { - return(RAR_DLL_VERSION); + return RAR_DLL_VERSION; } -static int RarErrorToDll(int ErrCode) +static int RarErrorToDll(RAR_EXIT ErrCode) { switch(ErrCode) { - case FATAL_ERROR: - return(ERAR_EREAD); - case CRC_ERROR: - return(ERAR_BAD_DATA); - case WRITE_ERROR: - return(ERAR_EWRITE); - case OPEN_ERROR: - return(ERAR_EOPEN); - case CREATE_ERROR: - return(ERAR_ECREATE); - case MEMORY_ERROR: - return(ERAR_NO_MEMORY); - case SUCCESS: - return(0); + case RARX_FATAL: + case RARX_READ: + return ERAR_EREAD; + case RARX_CRC: + return ERAR_BAD_DATA; + case RARX_WRITE: + return ERAR_EWRITE; + case RARX_OPEN: + return ERAR_EOPEN; + case RARX_CREATE: + return ERAR_ECREATE; + case RARX_MEMORY: + return ERAR_NO_MEMORY; + case RARX_BADPWD: + return ERAR_BAD_PASSWORD; + case RARX_SUCCESS: + return ERAR_SUCCESS; // 0. default: - return(ERAR_UNKNOWN); + return ERAR_UNKNOWN; } } diff --git a/unrar/unrar/dll.def b/unrar/unrar/dll.def index 660f69b..3c9a2c8 100644 --- a/unrar/unrar/dll.def +++ b/unrar/unrar/dll.def @@ -5,6 +5,7 @@ EXPORTS RARReadHeader
RARReadHeaderEx
RARProcessFile
+ RARProcessFileW
RARSetCallback
RARSetChangeVolProc
RARSetProcessDataProc
diff --git a/unrar/unrar/dll.hpp b/unrar/unrar/dll.hpp index c7cf622..c785ff1 100644 --- a/unrar/unrar/dll.hpp +++ b/unrar/unrar/dll.hpp @@ -1,9 +1,9 @@ #ifndef _UNRAR_DLL_ #define _UNRAR_DLL_ -/* Added by Tomas Bzatek - GCC 4.3 compatibility */ -#define LPARAM LONG +#pragma pack(push, 1) +#define ERAR_SUCCESS 0 #define ERAR_END_ARCHIVE 10 #define ERAR_NO_MEMORY 11 #define ERAR_BAD_DATA 12 @@ -17,6 +17,8 @@ #define ERAR_SMALL_BUF 20 #define ERAR_UNKNOWN 21 #define ERAR_MISSING_PASSWORD 22 +#define ERAR_EREFERENCE 23 +#define ERAR_BAD_PASSWORD 24 #define RAR_OM_LIST 0 #define RAR_OM_EXTRACT 1 @@ -29,16 +31,29 @@ #define RAR_VOL_ASK 0 #define RAR_VOL_NOTIFY 1 -#define RAR_DLL_VERSION 4 +#define RAR_DLL_VERSION 8 + +#define RAR_HASH_NONE 0 +#define RAR_HASH_CRC32 1 +#define RAR_HASH_BLAKE2 2 + #ifdef _UNIX #define CALLBACK #define PASCAL #define LONG long #define HANDLE void * +#define LPARAM long #define UINT unsigned int #endif +#define RHDF_SPLITBEFORE 0x01 +#define RHDF_SPLITAFTER 0x02 +#define RHDF_ENCRYPTED 0x04 +#define RHDF_SOLID 0x10 +#define RHDF_DIRECTORY 0x20 + + struct RARHeaderData { char ArcName[260]; @@ -80,7 +95,20 @@ struct RARHeaderDataEx unsigned int CmtBufSize; unsigned int CmtSize; unsigned int CmtState; - unsigned int Reserved[1024]; + unsigned int DictSize; + unsigned int HashType; + char Hash[32]; + unsigned int RedirType; + wchar_t *RedirName; + unsigned int RedirNameSize; + unsigned int DirTarget; + unsigned int MtimeLow; + unsigned int MtimeHigh; + unsigned int CtimeLow; + unsigned int CtimeHigh; + unsigned int AtimeLow; + unsigned int AtimeHigh; + unsigned int Reserved[988]; }; @@ -95,26 +123,43 @@ struct RAROpenArchiveData unsigned int CmtState; }; +typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); + +#define ROADF_VOLUME 0x0001 +#define ROADF_COMMENT 0x0002 +#define ROADF_LOCK 0x0004 +#define ROADF_SOLID 0x0008 +#define ROADF_NEWNUMBERING 0x0010 +#define ROADF_SIGNED 0x0020 +#define ROADF_RECOVERY 0x0040 +#define ROADF_ENCHEADERS 0x0080 +#define ROADF_FIRSTVOLUME 0x0100 + +#define ROADOF_KEEPBROKEN 0x0001 + struct RAROpenArchiveDataEx { char *ArcName; wchar_t *ArcNameW; - unsigned int OpenMode; - unsigned int OpenResult; + unsigned int OpenMode; + unsigned int OpenResult; char *CmtBuf; - unsigned int CmtBufSize; - unsigned int CmtSize; - unsigned int CmtState; - unsigned int Flags; - unsigned int Reserved[32]; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Flags; + UNRARCALLBACK Callback; + LPARAM UserData; + unsigned int OpFlags; + wchar_t *CmtBufW; + unsigned int Reserved[25]; }; enum UNRARCALLBACK_MESSAGES { - UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD + UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, + UCM_NEEDPASSWORDW }; -typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); - typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); @@ -139,4 +184,6 @@ int PASCAL RARGetDllVersion(); } #endif +#pragma pack(pop) + #endif diff --git a/unrar/unrar/dll.rc b/unrar/unrar/dll.rc new file mode 100644 index 0000000..b7f0778 --- /dev/null +++ b/unrar/unrar/dll.rc @@ -0,0 +1,28 @@ +#include <windows.h>
+#include <commctrl.h>
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 6, 24, 100, 1007 +PRODUCTVERSION 6, 24, 100, 1007 +FILEOS VOS__WINDOWS32
+FILETYPE VFT_APP
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904E4"
+ {
+ VALUE "CompanyName", "Alexander Roshal\0"
+ VALUE "ProductName", "RAR decompression library\0"
+ VALUE "FileDescription", "RAR decompression library\0"
+ VALUE "FileVersion", "6.24.0\0" + VALUE "ProductVersion", "6.24.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2023\0" + VALUE "OriginalFilename", "Unrar.dll\0"
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04E4
+ }
+}
+
diff --git a/unrar/unrar/dll_nocrypt.def b/unrar/unrar/dll_nocrypt.def new file mode 100644 index 0000000..d473e97 --- /dev/null +++ b/unrar/unrar/dll_nocrypt.def @@ -0,0 +1,13 @@ +EXPORTS
+ RAROpenArchive
+ RAROpenArchiveEx
+ RARCloseArchive
+ RARReadHeader
+ RARReadHeaderEx
+ RARProcessFile
+ RARProcessFileW
+ RARSetCallback
+ RARSetChangeVolProc
+ RARSetProcessDataProc
+; RARSetPassword
+ RARGetDllVersion
diff --git a/unrar/unrar/encname.cpp b/unrar/unrar/encname.cpp index 6f57cd9..e1ba1ed 100644 --- a/unrar/unrar/encname.cpp +++ b/unrar/unrar/encname.cpp @@ -11,11 +11,11 @@ EncodeFileName::EncodeFileName() -void EncodeFileName::Decode(char *Name,byte *EncName,int EncSize,wchar *NameW, - int MaxDecSize) +void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize, + wchar *NameW,size_t MaxDecSize) { - int EncPos=0,DecPos=0; - byte HighByte=EncName[EncPos++]; + size_t EncPos=0,DecPos=0; + byte HighByte=EncPos<EncSize ? EncName[EncPos++] : 0; while (EncPos<EncSize && DecPos<MaxDecSize) { if (FlagBits==0) @@ -26,26 +26,36 @@ void EncodeFileName::Decode(char *Name,byte *EncName,int EncSize,wchar *NameW, switch(Flags>>6) { case 0: + if (EncPos>=EncSize) + break; NameW[DecPos++]=EncName[EncPos++]; break; case 1: + if (EncPos>=EncSize) + break; NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); break; case 2: + if (EncPos+1>=EncSize) + break; NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); EncPos+=2; break; case 3: { + if (EncPos>=EncSize) + break; int Length=EncName[EncPos++]; - if (Length & 0x80) + if ((Length & 0x80)!=0) { + if (EncPos>=EncSize) + break; byte Correction=EncName[EncPos++]; - for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++) + for (Length=(Length&0x7f)+2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++) NameW[DecPos]=((Name[DecPos]+Correction)&0xff)+(HighByte<<8); } else - for (Length+=2;Length>0 && DecPos<MaxDecSize;Length--,DecPos++) + for (Length+=2;Length>0 && DecPos<MaxDecSize && DecPos<NameSize;Length--,DecPos++) NameW[DecPos]=Name[DecPos]; } break; diff --git a/unrar/unrar/encname.hpp b/unrar/unrar/encname.hpp index 586f421..c6b4ed4 100644 --- a/unrar/unrar/encname.hpp +++ b/unrar/unrar/encname.hpp @@ -4,17 +4,16 @@ class EncodeFileName { private: - void AddFlags(int Value); + void AddFlags(byte Value,byte *EncName); - byte *EncName; byte Flags; - int FlagBits; - int FlagsPos; - int DestSize; + uint FlagBits; + size_t FlagsPos; + size_t DestSize; public: EncodeFileName(); - int Encode(char *Name,wchar *NameW,byte *EncName); - void Decode(char *Name,byte *EncName,int EncSize,wchar *NameW,int MaxDecSize); + size_t Encode(char *Name,wchar *NameW,byte *EncName); + void Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,wchar *NameW,size_t MaxDecSize); }; #endif diff --git a/unrar/unrar/errhnd.cpp b/unrar/unrar/errhnd.cpp index 2797f63..97193e5 100644 --- a/unrar/unrar/errhnd.cpp +++ b/unrar/unrar/errhnd.cpp @@ -1,8 +1,5 @@ #include "rar.hpp" - -static bool UserBreak; - ErrorHandler::ErrorHandler() { Clean(); @@ -11,250 +8,257 @@ ErrorHandler::ErrorHandler() void ErrorHandler::Clean() { - ExitCode=SUCCESS; + ExitCode=RARX_SUCCESS; ErrCount=0; EnableBreak=true; Silent=false; - DoShutdown=false; + UserBreak=false; + MainExit=false; + DisableShutdown=false; + ReadErrIgnoreAll=false; } void ErrorHandler::MemoryError() { MemoryErrorMsg(); - Throw(MEMORY_ERROR); + Exit(RARX_MEMORY); } -void ErrorHandler::OpenError(const char *FileName) +void ErrorHandler::OpenError(const wchar *FileName) { #ifndef SILENT OpenErrorMsg(FileName); - Throw(OPEN_ERROR); + Exit(RARX_OPEN); #endif } -void ErrorHandler::CloseError(const char *FileName) +void ErrorHandler::CloseError(const wchar *FileName) { -#ifndef SILENT if (!UserBreak) { - ErrMsg(NULL,St(MErrFClose),FileName); + uiMsg(UIERROR_FILECLOSE,FileName); SysErrMsg(); } -#endif -#if !defined(SILENT) || defined(RARDLL) - Throw(FATAL_ERROR); -#endif + // We must not call Exit and throw an exception here, because this function + // is called from File object destructor and can be invoked when stack + // unwinding while handling another exception. Throwing a new exception + // when stack unwinding is prohibited and terminates a program. + // If necessary, we can check std::uncaught_exception() before throw. + SetErrorCode(RARX_FATAL); } -void ErrorHandler::ReadError(const char *FileName) +void ErrorHandler::ReadError(const wchar *FileName) { #ifndef SILENT - ReadErrorMsg(NULL,FileName); + ReadErrorMsg(FileName); #endif #if !defined(SILENT) || defined(RARDLL) - Throw(FATAL_ERROR); + Exit(RARX_READ); #endif } -bool ErrorHandler::AskRepeatRead(const char *FileName) +void ErrorHandler::AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit) { -#if !defined(SILENT) && !defined(SFX_MODULE) && !defined(_WIN_CE) + SetErrorCode(RARX_READ); +#if !defined(SILENT) && !defined(SFX_MODULE) if (!Silent) { - mprintf("\n"); - Log(NULL,St(MErrRead),FileName); - return(Ask(St(MRetryAbort))==1); + uiMsg(UIERROR_FILEREAD,UINULL,FileName); + SysErrMsg(); + if (ReadErrIgnoreAll) + Ignore=true; + else + { + bool All=false; + uiAskRepeatRead(FileName,Ignore,All,Retry,Quit); + if (All) + ReadErrIgnoreAll=Ignore=true; + if (Quit) // Disable shutdown if user select Quit in read error prompt. + DisableShutdown=true; + } + return; } #endif - return(false); + Ignore=true; // Saving the file part for -y or -inul or "Ignore all" choice. } -void ErrorHandler::WriteError(const char *ArcName,const char *FileName) +void ErrorHandler::WriteError(const wchar *ArcName,const wchar *FileName) { #ifndef SILENT WriteErrorMsg(ArcName,FileName); #endif #if !defined(SILENT) || defined(RARDLL) - Throw(WRITE_ERROR); + Exit(RARX_WRITE); #endif } -#ifdef _WIN_32 -void ErrorHandler::WriteErrorFAT(const char *FileName) +#ifdef _WIN_ALL +void ErrorHandler::WriteErrorFAT(const wchar *FileName) { -#if !defined(SILENT) && !defined(SFX_MODULE) SysErrMsg(); - ErrMsg(NULL,St(MNTFSRequired),FileName); -#endif + uiMsg(UIERROR_NTFSREQUIRED,FileName); #if !defined(SILENT) && !defined(SFX_MODULE) || defined(RARDLL) - Throw(WRITE_ERROR); + Exit(RARX_WRITE); #endif } #endif -bool ErrorHandler::AskRepeatWrite(const char *FileName) +bool ErrorHandler::AskRepeatWrite(const wchar *FileName,bool DiskFull) { -#if !defined(SILENT) && !defined(_WIN_CE) +#ifndef SILENT if (!Silent) { - mprintf("\n"); - Log(NULL,St(MErrWrite),FileName); - return(Ask(St(MRetryAbort))==1); + // We do not display "repeat write" prompt in Android, so we do not + // need the matching system error message. + SysErrMsg(); + bool Repeat=uiAskRepeatWrite(FileName,DiskFull); + if (!Repeat) // Disable shutdown if user pressed Cancel in error dialog. + DisableShutdown=true; + return Repeat; } #endif - return(false); + return false; } -void ErrorHandler::SeekError(const char *FileName) +void ErrorHandler::SeekError(const wchar *FileName) { -#ifndef SILENT if (!UserBreak) { - ErrMsg(NULL,St(MErrSeek),FileName); + uiMsg(UIERROR_FILESEEK,FileName); SysErrMsg(); } -#endif #if !defined(SILENT) || defined(RARDLL) - Throw(FATAL_ERROR); + Exit(RARX_FATAL); #endif } -void ErrorHandler::GeneralErrMsg(const char *Msg) +void ErrorHandler::GeneralErrMsg(const wchar *fmt,...) { -#ifndef SILENT - Log(NULL,"%s",Msg); + va_list arglist; + va_start(arglist,fmt); + wchar Msg[1024]; + vswprintf(Msg,ASIZE(Msg),fmt,arglist); + uiMsg(UIERROR_GENERALERRMSG,Msg); SysErrMsg(); -#endif + va_end(arglist); } void ErrorHandler::MemoryErrorMsg() { -#ifndef SILENT - ErrMsg(NULL,St(MErrOutMem)); -#endif + uiMsg(UIERROR_MEMORY); + SetErrorCode(RARX_MEMORY); } -void ErrorHandler::OpenErrorMsg(const char *FileName) +void ErrorHandler::OpenErrorMsg(const wchar *FileName) { OpenErrorMsg(NULL,FileName); } -void ErrorHandler::OpenErrorMsg(const char *ArcName,const char *FileName) +void ErrorHandler::OpenErrorMsg(const wchar *ArcName,const wchar *FileName) { -#ifndef SILENT - Log(ArcName && *ArcName ? ArcName:NULL,St(MCannotOpen),FileName); - Alarm(); + uiMsg(UIERROR_FILEOPEN,ArcName,FileName); SysErrMsg(); -#endif + SetErrorCode(RARX_OPEN); + + // Keep GUI responsive if many files cannot be opened when archiving. + // Call after SysErrMsg to avoid modifying the error code and SysErrMsg text. + Wait(); } -void ErrorHandler::CreateErrorMsg(const char *FileName) +void ErrorHandler::CreateErrorMsg(const wchar *FileName) { CreateErrorMsg(NULL,FileName); } -void ErrorHandler::CreateErrorMsg(const char *ArcName,const char *FileName) +void ErrorHandler::CreateErrorMsg(const wchar *ArcName,const wchar *FileName) { -#ifndef SILENT - Log(ArcName && *ArcName ? ArcName:NULL,St(MCannotCreate),FileName); - Alarm(); -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) && defined(MAXPATH) - if (GetLastError()==ERROR_PATH_NOT_FOUND) - { - int NameLength=strlen(FileName); - if (!IsFullPath(FileName)) - { - char CurDir[NM]; - GetCurrentDirectory(sizeof(CurDir),CurDir); - NameLength+=strlen(CurDir)+1; - } - if (NameLength>MAXPATH) - { - Log(ArcName && *ArcName ? ArcName:NULL,St(MMaxPathLimit),MAXPATH); - } - } -#endif + uiMsg(UIERROR_FILECREATE,ArcName,FileName); SysErrMsg(); -#endif + SetErrorCode(RARX_CREATE); } -void ErrorHandler::ReadErrorMsg(const char *ArcName,const char *FileName) +void ErrorHandler::ReadErrorMsg(const wchar *FileName) { -#ifndef SILENT - ErrMsg(ArcName,St(MErrRead),FileName); + ReadErrorMsg(NULL,FileName); +} + + +void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_FILEREAD,ArcName,FileName); SysErrMsg(); -#endif + SetErrorCode(RARX_READ); } -void ErrorHandler::WriteErrorMsg(const char *ArcName,const char *FileName) +void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName) { -#ifndef SILENT - ErrMsg(ArcName,St(MErrWrite),FileName); + uiMsg(UIERROR_FILEWRITE,ArcName,FileName); SysErrMsg(); -#endif + SetErrorCode(RARX_WRITE); } -void ErrorHandler::Exit(int ExitCode) +void ErrorHandler::ArcBrokenMsg(const wchar *ArcName) { -#ifndef SFX_MODULE - Alarm(); -#endif - Throw(ExitCode); + uiMsg(UIERROR_ARCBROKEN,ArcName); + SetErrorCode(RARX_CRC); } -#ifndef GUI -void ErrorHandler::ErrMsg(const char *ArcName,const char *fmt,...) +void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName) { - safebuf char Msg[NM+1024]; - va_list argptr; - va_start(argptr,fmt); - vsprintf(Msg,fmt,argptr); - va_end(argptr); -#ifdef _WIN_32 - if (UserBreak) - Sleep(5000); -#endif - Alarm(); - if (*Msg) - { - Log(ArcName,"\n%s",Msg); - mprintf("\n%s\n",St(MProgAborted)); - } + uiMsg(UIERROR_CHECKSUM,ArcName,FileName); + SetErrorCode(RARX_CRC); } -#endif -void ErrorHandler::SetErrorCode(int Code) +void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName) +{ + uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName); + ErrHandler.SetErrorCode(RARX_FATAL); +} + + +void ErrorHandler::Exit(RAR_EXIT ExitCode) +{ + uiAlarm(UIALARM_ERROR); + Throw(ExitCode); +} + + +void ErrorHandler::SetErrorCode(RAR_EXIT Code) { switch(Code) { - case WARNING: - case USER_BREAK: - if (ExitCode==SUCCESS) + case RARX_WARNING: + case RARX_USERBREAK: + if (ExitCode==RARX_SUCCESS) + ExitCode=Code; + break; + case RARX_CRC: + if (ExitCode!=RARX_BADPWD) ExitCode=Code; break; - case FATAL_ERROR: - if (ExitCode==SUCCESS || ExitCode==WARNING) - ExitCode=FATAL_ERROR; + case RARX_FATAL: + if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING) + ExitCode=RARX_FATAL; break; default: ExitCode=Code; @@ -264,8 +268,7 @@ void ErrorHandler::SetErrorCode(int Code) } -#if !defined(GUI) && !defined(_SFX_RTL_) -#ifdef _WIN_32 +#ifdef _WIN_ALL BOOL __stdcall ProcessSignal(DWORD SigType) #else #if defined(__sun) @@ -274,102 +277,147 @@ extern "C" void _stdfunction ProcessSignal(int SigType) #endif { -#ifdef _WIN_32 +#ifdef _WIN_ALL + // When a console application is run as a service, this allows the service + // to continue running after the user logs off. if (SigType==CTRL_LOGOFF_EVENT) - return(TRUE); + return TRUE; #endif - UserBreak=true; + + ErrHandler.UserBreak=true; + ErrHandler.SetDisableShutdown(); mprintf(St(MBreak)); - for (int I=0;!File::RemoveCreated() && I<3;I++) - { -#ifdef _WIN_32 + +#ifdef _WIN_ALL + // Let the main thread to handle 'throw' and destroy file objects. + for (uint I=0;!ErrHandler.MainExit && I<50;I++) Sleep(100); -#endif - } -#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(_WIN_CE) +#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL) ExtRes.UnloadDLL(); #endif - exit(USER_BREAK); -#if defined(_WIN_32) && !defined(_MSC_VER) - // never reached, just to avoid a compiler warning - return(TRUE); + exit(RARX_USERBREAK); #endif -} + +#ifdef _UNIX + static uint BreakCount=0; + // User continues to press Ctrl+C, exit immediately without cleanup. + if (++BreakCount>1) + exit(RARX_USERBREAK); + // Otherwise return from signal handler and let Wait() function to close + // files and quit. We cannot use the same approach as in Windows, + // because Unix signal handler can block execution of our main code. +#endif + +#if defined(_WIN_ALL) && !defined(_MSC_VER) + // Never reached, just to avoid a compiler warning + return TRUE; #endif +} void ErrorHandler::SetSignalHandlers(bool Enable) { EnableBreak=Enable; -#if !defined(GUI) && !defined(_SFX_RTL_) -#ifdef _WIN_32 +#ifdef _WIN_ALL SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE); -// signal(SIGBREAK,Enable ? ProcessSignal:SIG_IGN); #else signal(SIGINT,Enable ? ProcessSignal:SIG_IGN); signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN); #endif -#endif } -void ErrorHandler::Throw(int Code) +void ErrorHandler::Throw(RAR_EXIT Code) { - if (Code==USER_BREAK && !EnableBreak) + if (Code==RARX_USERBREAK && !EnableBreak) return; - ErrHandler.SetErrorCode(Code); -#ifdef ALLOW_EXCEPTIONS +#if !defined(SILENT) + // Do not write "aborted" when just displaying online help. + if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR) + mprintf(L"\n%s\n",St(MProgAborted)); +#endif + SetErrorCode(Code); throw Code; -#else - File::RemoveCreated(); - exit(Code); +} + + +bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size) +{ +#ifndef SILENT +#ifdef _WIN_ALL + int ErrType=GetLastError(); + if (ErrType!=0) + return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), + Msg,(DWORD)Size,NULL)!=0; +#endif + +#if defined(_UNIX) || defined(_EMX) + if (errno!=0) + { + char *err=strerror(errno); + if (err!=NULL) + { + CharToWide(err,Msg,Size); + return true; + } + } +#endif #endif + return false; } void ErrorHandler::SysErrMsg() { -#if !defined(SFX_MODULE) && !defined(SILENT) -#ifdef _WIN_32 - #define STRCHR strchr - #define ERRCHAR char - ERRCHAR *lpMsgBuf=NULL; - int ErrType=GetLastError(); - if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, - NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&lpMsgBuf,0,NULL)) +#ifndef SILENT + wchar Msg[1024]; + if (!GetSysErrMsg(Msg,ASIZE(Msg))) + return; +#ifdef _WIN_ALL + wchar *CurMsg=Msg; + while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines. { - ERRCHAR *CurMsg=lpMsgBuf; - while (CurMsg!=NULL) + while (*CurMsg=='\r' || *CurMsg=='\n') + CurMsg++; + if (*CurMsg==0) + break; + wchar *EndMsg=wcschr(CurMsg,'\r'); + if (EndMsg==NULL) + EndMsg=wcschr(CurMsg,'\n'); + if (EndMsg!=NULL) { - while (*CurMsg=='\r' || *CurMsg=='\n') - CurMsg++; - if (*CurMsg==0) - break; - ERRCHAR *EndMsg=STRCHR(CurMsg,'\r'); - if (EndMsg==NULL) - EndMsg=STRCHR(CurMsg,'\n'); - if (EndMsg!=NULL) - { - *EndMsg=0; - EndMsg++; - } - Log(NULL,"\n%s",CurMsg); - CurMsg=EndMsg; + *EndMsg=0; + EndMsg++; } + uiMsg(UIERROR_SYSERRMSG,CurMsg); + CurMsg=EndMsg; } - LocalFree( lpMsgBuf ); #endif #if defined(_UNIX) || defined(_EMX) - char *err=strerror(errno); - if (err!=NULL) - Log(NULL,"\n%s",err); + uiMsg(UIERROR_SYSERRMSG,Msg); #endif #endif } +int ErrorHandler::GetSystemErrorCode() +{ +#ifdef _WIN_ALL + return GetLastError(); +#else + return errno; +#endif +} +void ErrorHandler::SetSystemErrorCode(int Code) +{ +#ifdef _WIN_ALL + SetLastError(Code); +#else + errno=Code; +#endif +} diff --git a/unrar/unrar/errhnd.hpp b/unrar/unrar/errhnd.hpp index 950a62c..06f4f61 100644 --- a/unrar/unrar/errhnd.hpp +++ b/unrar/unrar/errhnd.hpp @@ -1,63 +1,75 @@ #ifndef _RAR_ERRHANDLER_ #define _RAR_ERRHANDLER_ -#if (defined(GUI) || !defined(_WIN_32)) && !defined(SFX_MODULE) && !defined(_WIN_CE) || defined(RARDLL) -#define ALLOW_EXCEPTIONS -#endif - - - -#define rarmalloc malloc -#define rarcalloc calloc -#define rarrealloc realloc -#define rarfree free -#define rarstrdup strdup -#define rarstrdupw strdupw - - +enum RAR_EXIT // RAR exit code. +{ + RARX_SUCCESS = 0, + RARX_WARNING = 1, + RARX_FATAL = 2, + RARX_CRC = 3, + RARX_LOCK = 4, + RARX_WRITE = 5, + RARX_OPEN = 6, + RARX_USERERROR = 7, + RARX_MEMORY = 8, + RARX_CREATE = 9, + RARX_NOFILES = 10, + RARX_BADPWD = 11, + RARX_READ = 12, + RARX_USERBREAK = 255 +}; -enum { SUCCESS,WARNING,FATAL_ERROR,CRC_ERROR,LOCK_ERROR,WRITE_ERROR, - OPEN_ERROR,USER_ERROR,MEMORY_ERROR,CREATE_ERROR,USER_BREAK=255}; class ErrorHandler { private: - void ErrMsg(const char *ArcName,const char *fmt,...); - - int ExitCode; - int ErrCount; + RAR_EXIT ExitCode; + uint ErrCount; bool EnableBreak; bool Silent; - bool DoShutdown; + bool DisableShutdown; // Shutdown is not suitable after last error. + bool ReadErrIgnoreAll; public: ErrorHandler(); void Clean(); void MemoryError(); - void OpenError(const char *FileName); - void CloseError(const char *FileName); - void ReadError(const char *FileName); - bool AskRepeatRead(const char *FileName); - void WriteError(const char *ArcName,const char *FileName); - void WriteErrorFAT(const char *FileName); - bool AskRepeatWrite(const char *FileName); - void SeekError(const char *FileName); - void GeneralErrMsg(const char *Msg); + void OpenError(const wchar *FileName); + void CloseError(const wchar *FileName); + void ReadError(const wchar *FileName); + void AskRepeatRead(const wchar *FileName,bool &Ignore,bool &Retry,bool &Quit); + void WriteError(const wchar *ArcName,const wchar *FileName); + void WriteErrorFAT(const wchar *FileName); + bool AskRepeatWrite(const wchar *FileName,bool DiskFull); + void SeekError(const wchar *FileName); + void GeneralErrMsg(const wchar *fmt,...); void MemoryErrorMsg(); - void OpenErrorMsg(const char *FileName); - void OpenErrorMsg(const char *ArcName,const char *FileName); - void CreateErrorMsg(const char *FileName); - void CreateErrorMsg(const char *ArcName,const char *FileName); - void ReadErrorMsg(const char *ArcName,const char *FileName); - void WriteErrorMsg(const char *ArcName,const char *FileName); - void Exit(int ExitCode); - void SetErrorCode(int Code); - int GetErrorCode() {return(ExitCode);} - int GetErrorCount() {return(ErrCount);} + void OpenErrorMsg(const wchar *FileName); + void OpenErrorMsg(const wchar *ArcName,const wchar *FileName); + void CreateErrorMsg(const wchar *FileName); + void CreateErrorMsg(const wchar *ArcName,const wchar *FileName); + void ReadErrorMsg(const wchar *FileName); + void ReadErrorMsg(const wchar *ArcName,const wchar *FileName); + void WriteErrorMsg(const wchar *ArcName,const wchar *FileName); + void ArcBrokenMsg(const wchar *ArcName); + void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName); + void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName); + void Exit(RAR_EXIT ExitCode); + void SetErrorCode(RAR_EXIT Code); + RAR_EXIT GetErrorCode() {return ExitCode;} + uint GetErrorCount() {return ErrCount;} void SetSignalHandlers(bool Enable); - void Throw(int Code); - void SetSilent(bool Mode) {Silent=Mode;}; - void SetShutdown(bool Mode) {DoShutdown=Mode;}; + void Throw(RAR_EXIT Code); + void SetSilent(bool Mode) {Silent=Mode;} + bool GetSysErrMsg(wchar *Msg,size_t Size); void SysErrMsg(); + int GetSystemErrorCode(); + void SetSystemErrorCode(int Code); + void SetDisableShutdown() {DisableShutdown=true;} + bool IsShutdownEnabled() {return !DisableShutdown;} + + bool UserBreak; // Ctrl+Break is pressed. + bool MainExit; // main() is completed. }; + #endif diff --git a/unrar/unrar/extinfo.cpp b/unrar/unrar/extinfo.cpp index 13239fe..d61747e 100644 --- a/unrar/unrar/extinfo.cpp +++ b/unrar/unrar/extinfo.cpp @@ -1,76 +1,190 @@ #include "rar.hpp" -#ifdef _WIN_32 -#include "win32acl.cpp" +#include "hardlinks.cpp" #include "win32stm.cpp" + +#ifdef _WIN_ALL +#include "win32acl.cpp" +#include "win32lnk.cpp" #endif -#ifdef _BEOS -#include "beosea.cpp" -#endif -#if defined(_EMX) && !defined(_DJGPP) -#include "os2ea.cpp" -#endif + #ifdef _UNIX #include "uowners.cpp" +#ifdef SAVE_LINKS +#include "ulinks.cpp" +#endif #endif +// RAR2 service header extra records. #ifndef SFX_MODULE -void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW) +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) { +#ifdef _WIN_ALL + if (Cmd->Test) + return; switch(Arc.SubBlockHead.SubType) { -#if defined(_EMX) && !defined(_DJGPP) - case EA_HEAD: - if (Cmd->ProcessEA) - ExtractOS2EA(Arc,Name); - break; -#endif -#ifdef _UNIX - case UO_HEAD: - if (Cmd->ProcessOwners) - ExtractUnixOwner(Arc,Name); - break; -#endif -#ifdef _BEOS - case BEEA_HEAD: - if (Cmd->ProcessEA) - ExtractBeEA(Arc,Name); - break; -#endif -#ifdef _WIN_32 case NTACL_HEAD: if (Cmd->ProcessOwners) - ExtractACL(Arc,Name,NameW); + ExtractACL20(Arc,Name); break; case STREAM_HEAD: - ExtractStreams(Arc,Name,NameW); + ExtractStreams20(Arc,Name); break; -#endif } +#endif } #endif -void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW) +// RAR3 and RAR5 service header extra records. +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) { -#if defined(_EMX) && !defined(_DJGPP) - if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_OS2EA)) - ExtractOS2EANew(Arc,Name); +#ifdef _UNIX + if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && + Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) + ExtractUnixOwner30(Arc,Name); #endif +#ifdef _WIN_ALL + if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) + ExtractACL(Arc,Name); + if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + ExtractStreams(Arc,Name,Cmd->Test); +#endif +} + + +// Extra data stored directly in file header. +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) +{ #ifdef _UNIX - if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) - ExtractUnixOwnerNew(Arc,Name); + if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) + SetUnixOwner(Arc,Name); #endif -#ifdef _BEOS - if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) - ExtractUnixOwnerNew(Arc,Name); +} + + + + +// Calculate a number of path components except \. and \.. +static int CalcAllowedDepth(const wchar *Name) +{ + int AllowedDepth=0; + while (*Name!=0) + { + if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1])) + { + bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0); + bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0); + if (!Dot && !Dot2) + AllowedDepth++; + else + if (Dot2) + AllowedDepth--; + } + Name++; + } + return AllowedDepth < 0 ? 0 : AllowedDepth; +} + + +// Check if all existing path components are directories and not links. +static bool LinkInPath(const wchar *Name) +{ + wchar Path[NM]; + if (wcslen(Name)>=ASIZE(Path)) + return true; // It should not be that long, skip. + wcsncpyz(Path,Name,ASIZE(Path)); + for (wchar *s=Path+wcslen(Path)-1;s>Path;s--) + if (IsPathDiv(*s)) + { + *s=0; + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) + return true; + } + return false; +} + + +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) +{ + // Catch root dir based /path/file paths also as stuff like \\?\. + // Do not check PrepSrcName here, it can be root based if destination path + // is a root based. + if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName)) + return false; + + // Number of ".." in link target. + int UpLevels=0; + for (int Pos=0;*TargetName!=0;Pos++) + { + bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && + (IsPathDiv(TargetName[2]) || TargetName[2]==0) && + (Pos==0 || IsPathDiv(*(TargetName-1))); + if (Dot2) + UpLevels++; + TargetName++; + } + // If link target includes "..", it must not have another links in its + // source path, because they can bypass our safety check. For example, + // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next + // or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and + // file "dir/lnk1/lnk2/poc.txt" last. + // Do not confuse with link chains in target, this is in link source path. + // It is important for Windows too, though this check can be omitted + // if LinksToDirs is invoked in Windows as well. + if (UpLevels>0 && LinkInPath(PrepSrcName)) + return false; + + // We could check just prepared src name, but for extra safety + // we check both original (as from archive header) and prepared + // (after applying the destination path and -ep switches) names. + + int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth. + + // Remove the destination path from prepared name if any. We should not + // count the destination path depth, because the link target must point + // inside of this path, not outside of it. + size_t ExtrPathLength=wcslen(Cmd->ExtrPath); + if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0) + { + PrepSrcName+=ExtrPathLength; + while (IsPathDiv(*PrepSrcName)) + PrepSrcName++; + } + int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); + + return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels; +} + + +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink) +{ + // Returning true in Uplink indicates that link target might include ".." + // and enables additional checks. It is ok to falsely return true here, + // as it implies only the minor performance penalty. But we shall always + // return true for links with ".." in target for security reason. + + UpLink=true; // Assume the target might include potentially unsafe "..". +#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL) + if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows. + UpLink=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL; #endif -#ifdef _WIN_32 - if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) - ExtractACLNew(Arc,Name,NameW); - if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) - ExtractStreamsNew(Arc,Name,NameW); + +#if defined(SAVE_LINKS) && defined(_UNIX) + // For RAR 3.x archives we process links even in test mode to skip link data. + if (Arc.Format==RARFMT15) + return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink); + if (Arc.Format==RARFMT50) + return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); +#elif defined(_WIN_ALL) + // RAR 5.0 archives store link information in file header, so there is + // no need to additionally test it if we do not create a file. + if (Arc.Format==RARFMT50) + return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead); #endif + return false; } diff --git a/unrar/unrar/extinfo.hpp b/unrar/unrar/extinfo.hpp index db7cea5..da82ec3 100644 --- a/unrar/unrar/extinfo.hpp +++ b/unrar/unrar/extinfo.hpp @@ -1,8 +1,23 @@ #ifndef _RAR_EXTINFO_ #define _RAR_EXTINFO_ +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink); +#ifdef _UNIX +void SetUnixOwner(Archive &Arc,const wchar *FileName); +#endif + +bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + +void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize); + +#ifdef _WIN_ALL +bool SetPrivilege(LPCTSTR PrivName); +#endif + +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name); -void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW); -void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW); #endif diff --git a/unrar/unrar/extract.cpp b/unrar/unrar/extract.cpp index 712dc90..f2eb166 100644 --- a/unrar/unrar/extract.cpp +++ b/unrar/unrar/extract.cpp @@ -1,77 +1,125 @@ #include "rar.hpp" -CmdExtract::CmdExtract() +CmdExtract::CmdExtract(CommandData *Cmd) { + CmdExtract::Cmd=Cmd; + + *ArcName=0; + *DestFileName=0; + + ArcAnalyzed=false; + Analyze=new AnalyzeData; + memset(Analyze,0,sizeof(*Analyze)); + TotalFileCount=0; - *Password=0; + + // Common for all archives involved. Set here instead of DoExtract() + // to use in unrar.dll too. + // We enable it by default in Unix to care about the case when several + // archives are unpacked to same directory with several independent RAR runs. + // Worst case performance penalty for a lot of small files seems to be ~3%. + // 2023.09.15: Windows performance impact seems to be negligible, + // less than 0.5% when extracting mix of small files and folders. + // So for extra security we enabled it for Windows too, even though + // unlike Unix, Windows doesn't expand lnk1 in symlink targets like + // "lnk1/../dir", but converts such path to "dir". + ConvertSymlinkPaths=true; + Unp=new Unpack(&DataIO); - Unp->Init(NULL); +#ifdef RAR_SMP + Unp->SetThreads(Cmd->Threads); +#endif } CmdExtract::~CmdExtract() { + FreeAnalyzeData(); delete Unp; - memset(Password,0,sizeof(Password)); + delete Analyze; +} + + +void CmdExtract::FreeAnalyzeData() +{ + for (size_t I=0;I<RefList.Size();I++) + { + // We can have undeleted temporary reference source here if extraction + // was interrupted early or if user refused to overwrite prompt. + if (RefList[I].TmpName!=NULL) + DelFile(RefList[I].TmpName); + free(RefList[I].RefName); + free(RefList[I].TmpName); + } + RefList.Reset(); + + memset(Analyze,0,sizeof(*Analyze)); } -void CmdExtract::DoExtract(CommandData *Cmd) +void CmdExtract::DoExtract() { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + Fat32=NotFat32=false; +#endif PasswordCancelled=false; - DataIO.SetCurrentCommand(*Cmd->Command); + DataIO.SetCurrentCommand(Cmd->Command[0]); - struct FindData FD; - while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName))) - if (FindFile::FastFind(ArcName,ArcNameW,&FD)) - DataIO.TotalArcSize+=FD.Size; + if (*Cmd->UseStdin==0) + { + FindData FD; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + if (FindFile::FastFind(ArcName,&FD)) + DataIO.TotalArcSize+=FD.Size; + } - Cmd->ArcNames->Rewind(); - while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName))) + Cmd->ArcNames.Rewind(); + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + + ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit(). + UseExactVolName=false; // Must be reset here, not in ExtractArchiveInit(). while (true) { - char PrevCmdPassword[MAXPASSWORD]; - strcpy(PrevCmdPassword,Cmd->Password); - - EXTRACT_ARC_CODE Code=ExtractArchive(Cmd); - - // restore Cmd->Password which could be changed in IsArchive() call - // for next header encrypted archive - strcpy(Cmd->Password,PrevCmdPassword); - + EXTRACT_ARC_CODE Code=ExtractArchive(); if (Code!=EXTRACT_ARC_REPEAT) break; } - if (FindFile::FastFind(ArcName,ArcNameW,&FD)) - DataIO.ProcessedArcSize+=FD.Size; + DataIO.ProcessedArcSize+=DataIO.LastArcSize; } - if (TotalFileCount==0 && *Cmd->Command!='I') + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (TotalFileCount==0 && Cmd->Command[0]!='I' && + ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password. { if (!PasswordCancelled) - { - mprintf(St(MExtrNoFiles)); - } - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName); + + // Other error codes may explain a reason of "no files extracted" clearer, + // so set it only if no other errors found (wrong mask set by user). + if (ErrHandler.GetErrorCode()==RARX_SUCCESS) + ErrHandler.SetErrorCode(RARX_NOFILES); } -#ifndef GUI else if (!Cmd->DisableDone) - if (*Cmd->Command=='I') + if (Cmd->Command[0]=='I') mprintf(St(MDone)); else if (ErrHandler.GetErrorCount()==0) mprintf(St(MExtrAllOk)); else mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount()); -#endif } -void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc) +void CmdExtract::ExtractArchiveInit(Archive &Arc) { - DataIO.UnpArcSize=Arc.FileLength(); + DataIO.AdjustTotalArcSize(&Arc); FileCount=0; MatchedArgs=0; @@ -79,476 +127,415 @@ void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc) FirstFile=true; #endif - if (*Cmd->Password!=0) - strcpy(Password,Cmd->Password); - PasswordAll=(*Cmd->Password!=0); + GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet(); DataIO.UnpVolume=false; - PrevExtracted=false; - SignatureFound=false; + PrevProcessed=false; AllMatchesExact=true; - ReconstructDone=false; + AnySolidDataUnpackedWell=false; + + ArcAnalyzed=false; StartTime.SetCurrentTime(); + + LastCheckedSymlink.clear(); } -EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd) +EXTRACT_ARC_CODE CmdExtract::ExtractArchive() { Archive Arc(Cmd); - if (!Arc.WOpen(ArcName,ArcNameW)) + if (*Cmd->UseStdin!=0) { - ErrHandler.SetErrorCode(OPEN_ERROR); - return(EXTRACT_ARC_NEXT); + Arc.SetHandleType(FILE_HANDLESTD); +#ifdef USE_QOPEN + Arc.SetProhibitQOpen(true); +#endif + } + else + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) // WinRAR GUI code also resets the cache. + if (*Cmd->Command=='T' || Cmd->Test) + ResetFileCache(ArcName); // Reset the file cache when testing an archive. +#endif + if (!Arc.WOpen(ArcName)) + return EXTRACT_ARC_NEXT; } if (!Arc.IsArchive(true)) { -#ifndef GUI +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (CmpExt(ArcName,L"rev")) + { + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true); + + // If several volume names from same volume set are specified + // and current volume is not first in set and first volume is present + // and specified too, let's skip the current volume. + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; + RecVolumesTest(Cmd,NULL,ArcName); + TotalFileCount++; // Suppress "No files to extract" message. + return EXTRACT_ARC_NEXT; + } +#endif + mprintf(St(MNotRAR),ArcName); + +#ifndef SFX_MODULE + if (CmpExt(ArcName,L"rar")) #endif - if (CmpExt(ArcName,"rar")) - ErrHandler.SetErrorCode(WARNING); - return(EXTRACT_ARC_NEXT); + ErrHandler.SetErrorCode(RARX_WARNING); + return EXTRACT_ARC_NEXT; } - // archive with corrupt encrypted header can be closed in IsArchive() call - if (!Arc.IsOpened()) - return(EXTRACT_ARC_NEXT); + if (Arc.FailedHeaderDecryption) // Bad archive password. + return EXTRACT_ARC_NEXT; #ifndef SFX_MODULE - if (Arc.Volume && Arc.NotFirstVolume) + if (Arc.Volume && !Arc.FirstVolume && !UseExactVolName) { - char FirstVolName[NM]; - VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)); + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); // If several volume names from same volume set are specified // and current volume is not first in set and first volume is present // and specified too, let's skip the current volume. - if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && - Cmd->ArcNames->Search(FirstVolName,NULL,false)) - return(EXTRACT_ARC_NEXT); + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; + } +#endif + + Arc.ViewComment(); // Must be before possible EXTRACT_ARC_REPEAT. + + int64 VolumeSetSize=0; // Total size of volumes after the current volume. + +#ifndef SFX_MODULE + if (!ArcAnalyzed && *Cmd->UseStdin==0) + { + AnalyzeArchive(Arc.FileName,Arc.Volume,Arc.NewNumbering); + ArcAnalyzed=true; // Avoid repeated analysis on EXTRACT_ARC_REPEAT. } #endif if (Arc.Volume) { +#ifndef SFX_MODULE + // Try to speed up extraction for independent solid volumes by starting + // extraction from non-first volume if we can. + if (*Analyze->StartName!=0) + { + wcsncpyz(ArcName,Analyze->StartName,ASIZE(ArcName)); + *Analyze->StartName=0; + + UseExactVolName=true; + return EXTRACT_ARC_REPEAT; + } +#endif + // Calculate the total size of all accessible volumes. // This size is necessary to display the correct total progress indicator. - char NextName[NM]; - wchar NextNameW[NM]; - - strcpy(NextName,Arc.FileName); - strcpyw(NextNameW,Arc.FileNameW); + wchar NextName[NM]; + wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); while (true) { // First volume is already added to DataIO.TotalArcSize // in initial TotalArcSize calculation in DoExtract. // So we skip it and start from second volume. - NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); - struct FindData FD; - if (FindFile::FastFind(NextName,NextNameW,&FD)) - DataIO.TotalArcSize+=FD.Size; + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + FindData FD; + if (FindFile::FastFind(NextName,&FD)) + VolumeSetSize+=FD.Size; else break; } + DataIO.TotalArcSize+=VolumeSetSize; } - ExtractArchiveInit(Cmd,Arc); + ExtractArchiveInit(Arc); if (*Cmd->Command=='T' || *Cmd->Command=='I') Cmd->Test=true; -#ifndef GUI + if (*Cmd->Command=='I') + { Cmd->DisablePercentage=true; + } else - if (Cmd->Test) - mprintf(St(MExtrTest),ArcName); - else - mprintf(St(MExtracting),ArcName); -#endif + uiStartArchiveExtract(!Cmd->Test,ArcName); - Arc.ViewComment(); - - // RAR can close a corrupt encrypted archive - if (!Arc.IsOpened()) - return(EXTRACT_ARC_NEXT); +#ifndef SFX_MODULE + if (Analyze->StartPos!=0) + { + Arc.Seek(Analyze->StartPos,SEEK_SET); + Analyze->StartPos=0; + } +#endif while (1) { - int Size=Arc.ReadHeader(); + size_t Size=Arc.ReadHeader(); + + bool Repeat=false; - if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat)) + if (!ExtractCurrentFile(Arc,Size,Repeat)) if (Repeat) { - return(EXTRACT_ARC_REPEAT); + // If we started extraction from not first volume and need to + // restart it from first, we must set DataIO.TotalArcSize to size + // of new first volume to display the total progress correctly. + FindData NewArc; + if (FindFile::FastFind(ArcName,&NewArc)) + DataIO.TotalArcSize=NewArc.Size; + return EXTRACT_ARC_REPEAT; } else break; } - return(EXTRACT_ARC_NEXT); + +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Cmd->Test && Arc.Volume) + RecVolumesTest(Cmd,&Arc,ArcName); +#endif + + return EXTRACT_ARC_NEXT; } -bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize,bool &Repeat) +bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) { - char Command=*Cmd->Command; - if (HeaderSize<=0) + wchar Command=Cmd->Command[0]; + if (HeaderSize==0) if (DataIO.UnpVolume) { #ifdef NOVOLUME - return(false); + return false; #else + // Supposing we unpack an old RAR volume without the end of archive + // record and last file is not split between volumes. if (!MergeArchive(Arc,&DataIO,false,Command)) { - ErrHandler.SetErrorCode(WARNING); - return(false); + ErrHandler.SetErrorCode(RARX_WARNING); + return false; } - SignatureFound=false; #endif } else - return(false); - int HeadType=Arc.GetHeaderType(); - if (HeadType!=FILE_HEAD) + return false; + + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_FILE) { - if (HeadType==AV_HEAD || HeadType==SIGN_HEAD) - SignatureFound=true; -#if !defined(SFX_MODULE) && !defined(_WIN_CE) - if (HeadType==SUB_HEAD && PrevExtracted) - SetExtraInfo(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL); -#endif - if (HeadType==NEWSUB_HEAD) - { - if (Arc.SubHead.CmpName(SUBHEAD_TYPE_AV)) - SignatureFound=true; -#if !defined(NOSUBBLOCKS) && !defined(_WIN_CE) - if (PrevExtracted) - SetExtraInfoNew(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL); + // Unlike Arc.FileName, ArcName might store an old volume name here. + if (Analyze->EndPos!=0 && Analyze->EndPos==Arc.CurBlockPos && + (*Analyze->EndName==0 || wcscmp(Analyze->EndName,Arc.FileName)==0)) + return false; + } + else + { +#ifndef SFX_MODULE + if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) + SetExtraInfo20(Cmd,Arc,DestFileName); #endif - } - if (HeadType==ENDARC_HEAD) - if (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME) + if (HeaderType==HEAD_SERVICE && PrevProcessed) + SetExtraInfo(Cmd,Arc,DestFileName); + if (HeaderType==HEAD_ENDARC) + if (Arc.EndArcHead.NextVolume) { -#ifndef NOVOLUME +#ifdef NOVOLUME + return false; +#else if (!MergeArchive(Arc,&DataIO,false,Command)) { - ErrHandler.SetErrorCode(WARNING); - return(false); + ErrHandler.SetErrorCode(RARX_WARNING); + return false; } - SignatureFound=false; -#endif Arc.Seek(Arc.CurBlockPos,SEEK_SET); - return(true); + return true; +#endif } else - return(false); + return false; Arc.SeekToNext(); - return(true); + return true; } - PrevExtracted=false; - - if (SignatureFound || - !Cmd->Recurse && MatchedArgs>=Cmd->FileArgs->ItemsCount() && - AllMatchesExact) - return(false); - - char ArcFileName[NM]; - IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName); - strcpy(ArcFileName,Arc.NewLhd.FileName); - - wchar ArcFileNameW[NM]; - *ArcFileNameW=0; + PrevProcessed=false; + + // We can get negative sizes in corrupt archive and it is unacceptable + // for size comparisons in ComprDataIO::UnpRead, where we cast sizes + // to size_t and can exceed another read or available size. We could fix it + // when reading an archive. But we prefer to do it here, because this + // function is called directly in unrar.dll, so we fix bad parameters + // passed to dll. Also we want to see real negative sizes in the listing + // of corrupt archive. To prevent uninitialized data access perform + // these checks after rejecting zero length and non-file headers above. + if (Arc.FileHead.PackSize<0) + Arc.FileHead.PackSize=0; + if (Arc.FileHead.UnpSize<0) + Arc.FileHead.UnpSize=0; + + // 2022.03.20: We might remove this check in the future. + // It duplicates Analyze->EndPos and Analyze->EndName in all cases except + // volumes on removable media. + if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) + return false; int MatchType=MATCH_WILDSUBPATH; bool EqualNames=false; - int MatchNumber=Cmd->IsProcessFile(Arc.NewLhd,&EqualNames,MatchType); - bool ExactMatch=MatchNumber!=0; -#if !defined(SFX_MODULE) && !defined(_WIN_CE) + wchar MatchedArg[NM]; + int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg)); + bool MatchFound=MatchNumber!=0; +#ifndef SFX_MODULE if (Cmd->ExclPath==EXCL_BASEPATH) { - *Cmd->ArcPath=0; - if (ExactMatch) - { - Cmd->FileArgs->Rewind(); - if (Cmd->FileArgs->GetString(Cmd->ArcPath,NULL,sizeof(Cmd->ArcPath),MatchNumber-1)) - *PointToName(Cmd->ArcPath)=0; - } + wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath)); + *PointToName(Cmd->ArcPath)=0; + if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here. + *Cmd->ArcPath=0; } #endif - if (ExactMatch && !EqualNames) + if (MatchFound && !EqualNames) AllMatchesExact=false; -#ifdef UNICODE_SUPPORTED - bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled(); -#else - bool WideName=false; -#endif + Arc.ConvertAttributes(); -#ifdef _APPLE - if (WideName) +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Arc.FileHead.SplitBefore && FirstFile && !UseExactVolName) { - WideToUtf(Arc.NewLhd.FileNameW,ArcFileName,sizeof(ArcFileName)); - WideName=false; - } -#endif - - wchar *DestNameW=WideName ? DestFileNameW:NULL; + wchar CurVolName[NM]; + wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); + GetFirstVolIfFullSet(ArcName,Arc.NewNumbering,ArcName,ASIZE(ArcName)); -#ifdef UNICODE_SUPPORTED - if (WideName) - { - ConvertPath(Arc.NewLhd.FileNameW,ArcFileNameW); - char Name[NM]; - if (WideToChar(ArcFileNameW,Name) && IsNameUsable(Name)) - strcpy(ArcFileName,Name); + if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) + { + wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction". + // If first volume name does not match the current name and if such + // volume name really exists, let's unpack from this first volume. + Repeat=true; + return false; + } +#ifndef RARDLL + if (!ReconstructDone) + { + ReconstructDone=true; + if (RecVolumesRestore(Cmd,Arc.FileName,true)) + { + Repeat=true; + return false; + } + } +#endif + wcsncpyz(ArcName,CurVolName,ASIZE(ArcName)); } #endif - ConvertPath(ArcFileName,ArcFileName); - - if (Arc.IsArcLabel()) - return(true); + wchar ArcFileName[NM]; + ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName)); - if (Arc.NewLhd.Flags & LHD_VERSION) + if (Arc.FileHead.Version) { if (Cmd->VersionControl!=1 && !EqualNames) { if (Cmd->VersionControl==0) - ExactMatch=false; - int Version=ParseVersionFileName(ArcFileName,ArcFileNameW,false); + MatchFound=false; + int Version=ParseVersionFileName(ArcFileName,false); if (Cmd->VersionControl-1==Version) - ParseVersionFileName(ArcFileName,ArcFileNameW,true); + ParseVersionFileName(ArcFileName,true); else - ExactMatch=false; + MatchFound=false; } } else if (!Arc.IsArcDir() && Cmd->VersionControl>1) - ExactMatch=false; - - Arc.ConvertAttributes(); + MatchFound=false; -#ifndef SFX_MODULE - if ((Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/)) && FirstFile) - { - char CurVolName[NM]; - strcpy(CurVolName,ArcName); - - VolNameToFirstName(ArcName,ArcName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)); - if (stricomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) - { - *ArcNameW=0; - Repeat=true; - return(false); - } -#if !defined(RARDLL) && !defined(_WIN_CE) - if (!ReconstructDone) - { - ReconstructDone=true; - - RecVolumes RecVol; - if (RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true)) - { - Repeat=true; - return(false); - } - } -#endif - strcpy(ArcName,CurVolName); - } -#endif - DataIO.UnpVolume=(Arc.NewLhd.Flags & LHD_SPLIT_AFTER); + DataIO.UnpVolume=Arc.FileHead.SplitAfter; DataIO.NextVolumeMissing=false; - Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET); + Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); - bool TestMode=false; bool ExtrFile=false; bool SkipSolid=false; #ifndef SFX_MODULE - if (FirstFile && (ExactMatch || Arc.Solid) && (Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/))!=0) + if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore) { - if (ExactMatch) + if (MatchFound) { - Log(Arc.FileName,St(MUnpCannotMerge),ArcFileName); + uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName); #ifdef RARDLL Cmd->DllError=ERAR_BAD_DATA; #endif - ErrHandler.SetErrorCode(OPEN_ERROR); + ErrHandler.SetErrorCode(RARX_OPEN); } - ExactMatch=false; + MatchFound=false; } FirstFile=false; #endif - if (ExactMatch || (SkipSolid=Arc.Solid)!=0) - { - if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0) -#ifndef RARDLL - if (*Password==0) -#endif + bool RefTarget=false; + if (!MatchFound) + for (size_t I=0;I<RefList.Size();I++) + if (wcscmp(ArcFileName,RefList[I].RefName)==0) { -#ifdef RARDLL - if (*Cmd->Password==0) - if (Cmd->Callback==NULL || - Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1) - return(false); - strcpy(Password,Cmd->Password); - -#else - if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password))) + ExtractRef *MatchedRef=&RefList[I]; + + if (!Cmd->Test) // While harmless, it is useless for 't'. { - PasswordCancelled=true; - return(false); + // If reference source isn't selected, but target is selected, + // we unpack the source under the temporary name and then rename + // or copy it to target name. We do not unpack it under the target + // name immediately, because the same source can be used by multiple + // targets and it is possible that first target isn't unpacked + // for some reason. Also targets might have associated service blocks + // like ACLs. All this would complicate processing a lot. + wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName)); + AddEndSlash(DestFileName,ASIZE(DestFileName)); + wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName)); + MkTemp(DestFileName,ASIZE(DestFileName)); + MatchedRef->TmpName=wcsdup(DestFileName); } -#endif + RefTarget=true; // Need it even for 't' to test the reference source. + break; } -#if !defined(GUI) && !defined(SILENT) - else - if (!PasswordAll && (!Arc.Solid || Arc.NewLhd.UnpVer>=20 && (Arc.NewLhd.Flags & LHD_SOLID)==0)) - { - eprintf(St(MUseCurPsw),ArcFileName); - switch(Cmd->AllYes ? 1:Ask(St(MYesNoAll))) - { - case -1: - ErrHandler.Exit(USER_BREAK); - case 2: - if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password))) - { - return(false); - } - break; - case 3: - PasswordAll=true; - break; - } - } -#endif - -#ifndef SFX_MODULE - if (*Cmd->ExtrPath==0 && *Cmd->ExtrPathW!=0) - WideToChar(Cmd->ExtrPathW,DestFileName); - else -#endif - strcpy(DestFileName,Cmd->ExtrPath); - - -#ifndef SFX_MODULE - if (Cmd->AppendArcNameToPath) - { - strcat(DestFileName,PointToName(Arc.FirstVolumeName)); - SetExt(DestFileName,NULL); - AddEndSlash(DestFileName); - } -#endif - - char *ExtrName=ArcFileName; - - bool EmptyName=false; -#ifndef SFX_MODULE - int Length=strlen(Cmd->ArcPath); - if (Length>1 && IsPathDiv(Cmd->ArcPath[Length-1]) && - strlen(ArcFileName)==Length-1) - Length--; - if (Length>0 && strnicomp(Cmd->ArcPath,ArcFileName,Length)==0) - { - ExtrName+=Length; - while (*ExtrName==CPATHDIVIDER) - ExtrName++; - if (*ExtrName==0) - EmptyName=true; - } -#endif - - bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); - if (AbsPaths) - *DestFileName=0; - - if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) - strcat(DestFileName,PointToName(ExtrName)); + + if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted) + if (Arc.Solid) + return false; // Abort the entire extraction for solid archive. else - strcat(DestFileName,ExtrName); - - char DiskLetter=etoupper(DestFileName[0]); - - if (AbsPaths && DestFileName[1]=='_' && IsPathDiv(DestFileName[2]) && - DiskLetter>='A' && DiskLetter<='Z') - DestFileName[1]=':'; - -#ifndef SFX_MODULE - if (!WideName && *Cmd->ExtrPathW!=0) - { - DestNameW=DestFileNameW; - WideName=true; - CharToWide(ArcFileName,ArcFileNameW); - } -#endif - - if (WideName) - { - if (*Cmd->ExtrPathW!=0) - strcpyw(DestFileNameW,Cmd->ExtrPathW); - else - CharToWide(Cmd->ExtrPath,DestFileNameW); - -#ifndef SFX_MODULE - if (Cmd->AppendArcNameToPath) - { - wchar FileNameW[NM]; - if (*Arc.FirstVolumeNameW!=0) - strcpyw(FileNameW,Arc.FirstVolumeNameW); - else - CharToWide(Arc.FirstVolumeName,FileNameW); - strcatw(DestFileNameW,PointToName(FileNameW)); - SetExt(DestFileNameW,NULL); - AddEndSlash(DestFileNameW); - } -#endif - wchar *ExtrNameW=ArcFileNameW; -#ifndef SFX_MODULE - if (Length>0) - { - wchar ArcPathW[NM]; - GetWideName(Cmd->ArcPath,Cmd->ArcPathW,ArcPathW); - Length=strlenw(ArcPathW); - } - ExtrNameW+=Length; - while (*ExtrNameW==CPATHDIVIDER) - ExtrNameW++; -#endif - - if (AbsPaths) - *DestFileNameW=0; - - if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) - strcatw(DestFileNameW,PointToName(ExtrNameW)); - else - strcatw(DestFileNameW,ExtrNameW); + MatchFound=false; // Skip only the current file for non-solid archive. + + if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=0) + { + // First common call of uiStartFileExtract. It is done before overwrite + // prompts, so if SkipSolid state is changed below, we'll need to make + // additional uiStartFileExtract calls with updated parameters. + if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) + return false; - if (AbsPaths && DestFileNameW[1]=='_' && IsPathDiv(DestFileNameW[2])) - DestFileNameW[1]=':'; - } - else - *DestFileNameW=0; + if (!RefTarget) + ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); - ExtrFile=!SkipSolid && !EmptyName && (Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0; + // DestFileName can be set empty in case of excessive -ap switch. + ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X')) { - struct FindData FD; - if (FindFile::FastFind(DestFileName,DestNameW,&FD)) + FindData FD; + if (FindFile::FastFind(DestFileName,&FD)) { - if (FD.mtime >= Arc.NewLhd.mtime) + if (FD.mtime >= Arc.FileHead.mtime) { // If directory already exists and its modification time is newer // than start of extraction, it is likely it was created @@ -565,195 +552,169 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize ExtrFile=false; } - // skip encrypted file if no password is specified - if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0 && *Password==0) + if (!CheckUnpVer(Arc,ArcFileName)) { - ErrHandler.SetErrorCode(WARNING); -#ifdef RARDLL - Cmd->DllError=ERAR_MISSING_PASSWORD; -#endif - ExtrFile=false; - } - -#ifdef RARDLL - if (*Cmd->DllDestName) - { - strncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); - *DestFileNameW=0; - if (Cmd->DllOpMode!=RAR_EXTRACT) - ExtrFile=false; - } - if (*Cmd->DllDestNameW) - { - strncpyzw(DestFileNameW,Cmd->DllDestNameW,ASIZE(DestFileNameW)); - DestNameW=DestFileNameW; - if (Cmd->DllOpMode!=RAR_EXTRACT) - ExtrFile=false; - } -#endif - -#ifdef SFX_MODULE - if ((Arc.NewLhd.UnpVer!=UNP_VER && Arc.NewLhd.UnpVer!=29) && - Arc.NewLhd.Method!=0x30) -#else - if (Arc.NewLhd.UnpVer<13 || Arc.NewLhd.UnpVer>UNP_VER) -#endif - { -#ifndef SILENT - Log(Arc.FileName,St(MUnknownMeth),ArcFileName); -#ifndef SFX_MODULE - Log(Arc.FileName,St(MVerRequired),Arc.NewLhd.UnpVer/10,Arc.NewLhd.UnpVer%10); -#endif -#endif - ExtrFile=false; - ErrHandler.SetErrorCode(WARNING); + ErrHandler.SetErrorCode(RARX_FATAL); #ifdef RARDLL Cmd->DllError=ERAR_UNKNOWN_FORMAT; #endif + Arc.SeekToNext(); + return !Arc.Solid; // Can try extracting next file only in non-solid archive. } - File CurFile; + if (Arc.FileHead.Encrypted) + { + RarCheckPassword CheckPwd; + if (Arc.Format==RARFMT50 && Arc.FileHead.UsePswCheck && !Arc.BrokenHeader) + CheckPwd.Set(Arc.FileHead.Salt,Arc.FileHead.InitV,Arc.FileHead.Lg2Count,Arc.FileHead.PswCheck); - if (!IsLink(Arc.NewLhd.FileAttr)) - if (Arc.IsArcDir()) + while (true) // Repeat the password prompt for wrong and empty passwords. { - if (!ExtrFile || Command=='P' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) - return(true); - if (SkipSolid) - { -#ifndef GUI - mprintf(St(MExtrSkipFile),ArcFileName); -#endif - return(true); - } - TotalFileCount++; - if (Cmd->Test) + // Stop archive extracting if user cancelled a password prompt. +#ifdef RARDLL + if (!ExtrDllGetPassword()) { -#ifndef GUI - mprintf(St(MExtrTestFile),ArcFileName); - mprintf(" %s",St(MOk)); -#endif - return(true); + Cmd->DllError=ERAR_MISSING_PASSWORD; + return false; } - MKDIR_CODE MDCode=MakeDir(DestFileName,DestNameW,Arc.NewLhd.FileAttr); - bool DirExist=false; - if (MDCode!=MKDIR_SUCCESS) +#else + if (!ExtrGetPassword(Arc,ArcFileName,CheckPwd.IsSet() ? &CheckPwd:NULL)) { - DirExist=FileExist(DestFileName,DestNameW); - if (DirExist && !IsDir(GetFileAttr(DestFileName,DestNameW))) - { - bool UserReject; - FileCreate(Cmd,NULL,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime); - DirExist=false; - } - CreatePath(DestFileName,DestNameW,true); - MDCode=MakeDir(DestFileName,DestNameW,Arc.NewLhd.FileAttr); + PasswordCancelled=true; + return false; } - if (MDCode==MKDIR_SUCCESS) - { -#ifndef GUI - mprintf(St(MCreatDir),DestFileName); - mprintf(" %s",St(MOk)); #endif - PrevExtracted=true; - } - else - if (DirExist) + + // Set a password before creating the file, so we can skip creating + // in case of wrong password. + SecPassword FilePassword=Cmd->Password; + #if defined(_WIN_ALL) && !defined(SFX_MODULE) + ConvertDosPassword(Arc,FilePassword); + #endif + + byte PswCheck[SIZE_PSWCHECK]; + DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, + Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, + Arc.FileHead.InitV,Arc.FileHead.Lg2Count, + Arc.FileHead.HashKey,PswCheck); + + // If header is damaged, we cannot rely on password check value, + // because it can be damaged too. + if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader && + memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0) + { + if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop. { - SetFileAttr(DestFileName,DestNameW,Arc.NewLhd.FileAttr); - PrevExtracted=true; + // This message is used by Android GUI to reset cached passwords. + // Update appropriate code if changed. + uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); } - else + else // For passwords entered manually. { - Log(Arc.FileName,St(MExtrErrMkDir),DestFileName); - ErrHandler.SysErrMsg(); -#ifdef RARDLL - Cmd->DllError=ERAR_ECREATE; -#endif - ErrHandler.SetErrorCode(CREATE_ERROR); + // This message is used by Android GUI and Windows GUI and SFX to + // reset cached passwords. Update appropriate code if changed. + uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); + Cmd->Password.Clean(); + + // Avoid new requests for unrar.dll to prevent the infinite loop + // if app always returns the same password. + #ifndef RARDLL + continue; // Request a password again. + #endif } - if (PrevExtracted) - { -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) - if (Cmd->SetCompressedAttr && - (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) - SetFileCompression(DestFileName,DestNameW,true); -#endif - SetDirTime(DestFileName,DestNameW, - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, - Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); + #ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume, + // we should not replace it with less precise ERAR_BAD_PASSWORD. + if (Cmd->DllError!=ERAR_EOPEN) + Cmd->DllError=ERAR_BAD_PASSWORD; + #endif + ErrHandler.SetErrorCode(RARX_BADPWD); + ExtrFile=false; } - return(true); + break; } - else - { - if (Cmd->Test && ExtrFile) - TestMode=true; -#if !defined(GUI) && !defined(SFX_MODULE) - if (Command=='P' && ExtrFile) - CurFile.SetHandleType(FILE_HANDLESTD); -#endif - if ((Command=='E' || Command=='X') && ExtrFile && !Cmd->Test) - { - bool UserReject; - if (!FileCreate(Cmd,&CurFile,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime)) - { - ExtrFile=false; - if (!UserReject) - { - ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); - ErrHandler.SetErrorCode(CREATE_ERROR); + } + else + DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL); + #ifdef RARDLL - Cmd->DllError=ERAR_ECREATE; + if (*Cmd->DllDestName!=0) + wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); #endif - if (!IsNameUsable(DestFileName)) - { - Log(Arc.FileName,St(MCorrectingName)); - char OrigName[sizeof(DestFileName)]; - strncpyz(OrigName,DestFileName,ASIZE(OrigName)); - MakeNameUsable(DestFileName,true); - CreatePath(DestFileName,NULL,true); - if (FileCreate(Cmd,&CurFile,DestFileName,NULL,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime)) - { -#ifndef SFX_MODULE - Log(Arc.FileName,St(MRenaming),OrigName,DestFileName); -#endif - ExtrFile=true; - } - else - ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); - } - } - } - } + if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks && + ConvertSymlinkPaths) + ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); + + File CurFile; + + bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; + if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY)) + { + if (ExtrFile && Command!='P' && !Cmd->Test) + { + // Overwrite prompt for symbolic and hard links and when we move + // a temporary file to the file reference instead of copying it. + bool UserReject=false; + if (FileExist(DestFileName) && !UserReject) + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + if (UserReject) + ExtrFile=false; + } + } + else + if (Arc.IsArcDir()) + { + if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + return true; + TotalFileCount++; + ExtrCreateDir(Arc,ArcFileName); + // It is important to not increment MatchedArgs here, so we extract + // dir with its entire contents and not dir record only even if + // dir record precedes files. + return true; } + else + if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY). + ExtrFile=ExtrCreateFile(Arc,CurFile); if (!ExtrFile && Arc.Solid) { SkipSolid=true; - TestMode=true; ExtrFile=true; + + // We changed SkipSolid, so we need to call uiStartFileExtract + // with "Skip" parameter to change the operation status + // from "extracting" to "skipping". For example, it can be necessary + // if user answered "No" to overwrite prompt when unpacking + // a solid archive. + if (!uiStartFileExtract(ArcFileName,false,false,true)) + return false; } if (ExtrFile) { + // Set it in test mode, so we also test subheaders such as NTFS streams + // after tested file. + if (Cmd->Test) + PrevProcessed=true; + + bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk. + if (!SkipSolid) { if (!TestMode && Command!='P' && CurFile.IsDevice()) { - Log(Arc.FileName,St(MInvalidName),DestFileName); + uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName); ErrHandler.WriteError(Arc.FileName,DestFileName); } TotalFileCount++; } FileCount++; -#ifndef GUI - if (Command!='I') + if (Command!='I' && !Cmd->DisableNames) if (SkipSolid) mprintf(St(MExtrSkipFile),ArcFileName); else - switch(Cmd->Test ? 'T':Command) + switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch. { case 'T': mprintf(St(MExtrTestFile),ArcFileName); @@ -768,146 +729,937 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize mprintf(St(MExtrFile),DestFileName); break; } - if (!Cmd->DisablePercentage) - mprintf(" "); -#endif + if (!Cmd->DisablePercentage && !Cmd->DisableNames) + mprintf(L" "); + if (Cmd->DisableNames) + uiEolAfterMsg(); // Avoid erasing preceding messages by percentage indicator in -idn mode. + DataIO.CurUnpRead=0; DataIO.CurUnpWrite=0; - DataIO.UnpFileCRC=Arc.OldFormat ? 0 : 0xffffffff; - DataIO.PackedCRC=0xffffffff; - DataIO.SetEncryption( - (Arc.NewLhd.Flags & LHD_PASSWORD) ? Arc.NewLhd.UnpVer:0,Password, - (Arc.NewLhd.Flags & LHD_SALT) ? Arc.NewLhd.Salt:NULL,false, - Arc.NewLhd.UnpVer>=36); - DataIO.SetPackedSizeToRead(Arc.NewLhd.FullPackSize); + DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize); DataIO.SetFiles(&Arc,&CurFile); DataIO.SetTestMode(TestMode); DataIO.SetSkipUnpCRC(SkipSolid); -#ifndef _WIN_CE - if (!TestMode && !Arc.BrokenFileHeader && - (Arc.NewLhd.FullPackSize<<11)>Arc.NewLhd.FullUnpSize && - (Arc.NewLhd.FullUnpSize<100000000 || Arc.FileLength()>Arc.NewLhd.FullPackSize)) - CurFile.Prealloc(Arc.NewLhd.FullUnpSize); + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + if (!TestMode && !Arc.BrokenHeader && + Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32)) + { + if (!Fat32) // Not detected yet. + NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath)); + if (Fat32) + uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit. + } #endif + uint64 Preallocated=0; + if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 && + Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize && Arc.IsSeekable() && + (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) + { + CurFile.Prealloc(Arc.FileHead.UnpSize); + Preallocated=Arc.FileHead.UnpSize; + } CurFile.SetAllowDelete(!Cmd->KeepBroken); - bool LinkCreateMode=!Cmd->Test && !SkipSolid; - if (ExtractLink(DataIO,Arc,DestFileName,DataIO.UnpFileCRC,LinkCreateMode)) - PrevExtracted=LinkCreateMode; + bool FileCreateMode=!TestMode && !SkipSolid && Command!='P'; + bool ShowChecksum=true; // Display checksum verification result. + + bool LinkSuccess=true; // Assume success for test mode. + if (LinkEntry) + { + FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType; + + if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) + { + wchar RedirName[NM]; + + // 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with + // Unix RAR. WinRAR 5.0 used \ path separators here, when beginning + // from 5.10 even Windows version uses / internally and converts + // them to \ when reading FHEXTRA_REDIR. + // We must perform this conversion before ConvertPath call, + // so paths mixing different slashes like \dir1/dir2\file are + // processed correctly. + SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); + + ConvertPath(RedirName,RedirName,ASIZE(RedirName)); + + wchar NameExisting[NM]; + ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); + if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. + if (Type==FSREDIR_HARDLINK) + LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); + else + LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize); + } + else + if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) + { + if (FileCreateMode) + { + bool UpLink; + LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink); + + // Unix symlink can have its own owner data. + if (LinkSuccess) + SetFileHeaderExtra(Cmd,Arc,DestFileName); + + ConvertSymlinkPaths|=LinkSuccess && UpLink; + + // We do not actually need to reset the cache here if we cache + // only the single last checked path, because at this point + // it will always contain the link own path and link can't + // overwrite its parent folder. But if we ever decide to cache + // several already checked paths, we'll need to reset them here. + // Otherwise if no files were created in one of such paths, + // let's say because of file create error, it might be possible + // to overwrite the path with link and avoid checks. We keep this + // code here as a reminder in case of possible modifications. + LastCheckedSymlink.clear(); // Reset cache for safety reason. + } + } + else + { + uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName); + LinkSuccess=false; + } + + if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode) + { + // RAR 5.x links have a valid data checksum even in case of + // failure, because they do not store any data. + // We do not want to display "OK" in this case. + // For 4.x symlinks we verify the checksum only when extracting, + // but not when testing an archive. + ShowChecksum=false; + } + PrevProcessed=FileCreateMode && LinkSuccess; + } else - if ((Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0) - if (Arc.NewLhd.Method==0x30) - UnstoreFile(DataIO,Arc.NewLhd.FullUnpSize); + if (!Arc.FileHead.SplitBefore) + if (Arc.FileHead.Method==0) + UnstoreFile(DataIO,Arc.FileHead.UnpSize); else { - Unp->SetDestSize(Arc.NewLhd.FullUnpSize); + Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); + Unp->SetDestSize(Arc.FileHead.UnpSize); #ifndef SFX_MODULE - if (Arc.NewLhd.UnpVer<=15) + // RAR 1.3 - 1.5 archives do not set per file solid flag. + if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) Unp->DoUnpack(15,FileCount>1 && Arc.Solid); else #endif - Unp->DoUnpack(Arc.NewLhd.UnpVer,Arc.NewLhd.Flags & LHD_SOLID); + Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid); } - if (Arc.IsOpened()) - Arc.SeekToNext(); + Arc.SeekToNext(); + // We check for "split after" flag to detect partially extracted files + // from incomplete volume sets. For them file header contains packed + // data hash, which must not be compared against unpacked data hash + // to prevent accidental match. Moreover, for -m0 volumes packed data + // hash would match truncated unpacked data hash and lead to fake "OK" + // in incomplete volume set. + bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL); + + // We set AnySolidDataUnpackedWell to true if we found at least one + // valid non-zero solid file in preceding solid stream. If it is true + // and if current encrypted file is broken, we do not need to hint + // about a wrong password and can report CRC error only. + if (!Arc.FileHead.Solid) + AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found. + else + if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC) + AnySolidDataUnpackedWell=true; + bool BrokenFile=false; - if (!SkipSolid) + + // Checksum is not calculated in skip solid mode for performance reason. + if (!SkipSolid && ShowChecksum) { - if (Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC) || - !Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC^0xffffffff)) + if (ValidCRC) { -#ifndef GUI - if (Command!='P' && Command!='I') - mprintf("%s%s ",Cmd->DisablePercentage ? " ":"\b\b\b\b\b ",St(MOk)); -#endif + if (Command!='P' && Command!='I' && !Cmd->DisableNames) + mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ", + Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk)); } else { - char *BadArcName=/*(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE) ? NULL:*/Arc.FileName; - if (Arc.NewLhd.Flags & LHD_PASSWORD) - { - Log(BadArcName,St(MEncrBadCRC),ArcFileName); - } + if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck || + Arc.BrokenHeader) && !AnySolidDataUnpackedWell) + uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName); else - { - Log(BadArcName,St(MCRCFailed),ArcFileName); - } + uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName); BrokenFile=true; - ErrHandler.SetErrorCode(CRC_ERROR); + ErrHandler.SetErrorCode(RARX_CRC); #ifdef RARDLL - Cmd->DllError=ERAR_BAD_DATA; + // If we already have ERAR_EOPEN as result of missing volume + // or ERAR_BAD_PASSWORD for RAR5 wrong password, + // we should not replace it with less precise ERAR_BAD_DATA. + if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD) + Cmd->DllError=ERAR_BAD_DATA; #endif - Alarm(); } } -#ifndef GUI else - mprintf("\b\b\b\b\b "); -#endif + { + // We check SkipSolid to remove percent for skipped solid files only. + // We must not apply these \b to links with ShowChecksum==false + // and their possible error messages. + if (SkipSolid) + mprintf(L"\b\b\b\b\b "); + } + + // If we successfully unpacked a hard link, we wish to set its file + // attributes. Hard link shares file metadata with link target, + // so we do not need to set link time or owner. But when we overwrite + // an existing link, we can call PrepareToDelete(), which affects + // link target attributes as well. So we set link attributes to restore + // both target and link attributes if PrepareToDelete() changed them. + bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess; if (!TestMode && (Command=='X' || Command=='E') && - !IsLink(Arc.NewLhd.FileAttr)) + (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && + (!BrokenFile || Cmd->KeepBroken)) { -#if defined(_WIN_32) || defined(_EMX) - if (Cmd->ClearArc) - Arc.NewLhd.FileAttr&=~FA_ARCH; -/* - else - Arc.NewLhd.FileAttr|=FA_ARCH; //set archive bit for unpacked files (file is not backed up) -*/ -#endif - if (!BrokenFile || Cmd->KeepBroken) + // Below we use DestFileName instead of CurFile.FileName, + // so we can set file attributes also for hard links, which do not + // have the open CurFile. These strings are the same for other items. + + if (!SetAttrOnly) { - if (BrokenFile) + // We could preallocate more space that really written to broken file + // or file with crafted header. + if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated)) CurFile.Truncate(); - CurFile.SetOpenFileStat( - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, - Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime); + + + CurFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); CurFile.Close(); -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) - if (Cmd->SetCompressedAttr && - (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) - SetFileCompression(CurFile.FileName,CurFile.FileNameW,true); -#endif - CurFile.SetCloseFileStat( - Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime, - Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime, - Arc.NewLhd.FileAttr); - PrevExtracted=true; + + SetFileHeaderExtra(Cmd,Arc,DestFileName); + + CurFile.SetCloseFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); } + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0) + SetFileCompression(DestFileName,true); + if (Cmd->ClearArc) + Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; +#endif + if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr)) + { + uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName); + // Android cannot set file attributes and while UIERROR_FILEATTR + // above is handled by Android RAR silently, this call would cause + // "Operation not permitted" message for every unpacked file. + ErrHandler.SysErrMsg(); + } + + PrevProcessed=true; } } } - if (ExactMatch) + // It is important to increment it for files, but not dirs. So we extract + // dir with its entire contents, not just dir record only even if dir + // record precedes files. + if (MatchFound) MatchedArgs++; - if (DataIO.NextVolumeMissing || !Arc.IsOpened()) - return(false); + if (DataIO.NextVolumeMissing) + return false; if (!ExtrFile) if (!Arc.Solid) Arc.SeekToNext(); else if (!SkipSolid) - return(false); - return(true); + return false; + return true; } -void CmdExtract::UnstoreFile(ComprDataIO &DataIO,Int64 DestUnpSize) +void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) { - Array<byte> Buffer(0x10000); - while (1) + Array<byte> Buffer(File::CopyBufferSize()); + while (true) + { + int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size()); + if (ReadSize<=0) + break; + int WriteSize=ReadSize<DestUnpSize ? ReadSize:(int)DestUnpSize; + if (WriteSize>0) + { + DataIO.UnpWrite(&Buffer[0],WriteSize); + DestUnpSize-=WriteSize; + } + } +} + + +bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize) +{ + File Existing; + if (!Existing.Open(NameExisting)) + { + bool OpenFailed=true; + // If we couldn't find the existing file, check if match is present + // in temporary reference sources list. + for (size_t I=0;I<RefList.Size();I++) + if (wcscmp(RedirName,RefList[I].RefName)==0 && RefList[I].TmpName!=NULL) + { + // If only one reference left targeting to this temporary file, + // it is faster to move the file instead of copying and deleting it. + bool RefMove=RefList[I].RefCount-- == 1; + NameExisting=RefList[I].TmpName; + if (RefMove) // Only one reference left for this temporary file. + { + New.Delete(); // Delete the previously opened destination file. + // Try moving the file first. + bool MoveFailed=!RenameFile(NameExisting,NameNew); + if (MoveFailed) + { + // If move failed, re-create the destination and try coping. + if (!New.WCreate(NameNew,FMF_WRITE|FMF_SHAREREAD)) + return false; + RefMove=false; // Try copying below. + } + else + { + // If moved successfully, reopen the destination file and seek to + // end for SetOpenFileTime() and possible Truncate() calls later. + if (New.Open(NameNew)) + New.Seek(0,SEEK_END); + // We already moved the file, so clean the name to not try + // deleting non-existent temporary file later. + free(RefList[I].TmpName); + RefList[I].TmpName=NULL; + return true; + } + } + if (!RefMove) + OpenFailed=!Existing.Open(NameExisting); + break; + } + + if (OpenFailed) + { + ErrHandler.OpenErrorMsg(NameExisting); + uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); + uiMsg(UIERROR_FILECOPYHINT,ArcName); +#ifdef RARDLL + Cmd->DllError=ERAR_EREFERENCE; +#endif + return false; + } + } + + Array<byte> Buffer(0x100000); + int64 CopySize=0; + + while (true) + { + Wait(); + int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); + if (ReadSize==0) + break; + // Update only the current file progress in WinRAR, set the total to 0 + // to keep it as is. It looks better for WinRAR. + uiExtractProgress(CopySize,UnpSize,0,0); + + New.Write(&Buffer[0],ReadSize); + CopySize+=ReadSize; + } + + return true; +} + + +void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) +{ + if (Cmd->Test) + { + // Destination name conversion isn't needed for simple archive test. + // This check also allows to avoid issuing "Attempting to correct... + // Renaming..." messages in MakeNameCompatible() below for problematic + // names like aux.txt when testing an archive. + wcsncpyz(DestName,ArcFileName,DestSize); + return; + } + + wcsncpyz(DestName,Cmd->ExtrPath,DestSize); + + if (*Cmd->ExtrPath!=0) + { + wchar LastChar=*PointToLastChar(Cmd->ExtrPath); + // We need IsPathDiv check here to correctly handle Unix forward slash + // in the end of destination path in Windows: rar x arc dest/ + // so we call IsPathDiv first instead of just calling AddEndSlash, + // which checks for only one type of path separator. + // IsDriveDiv is needed for current drive dir: rar x arc d: + if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar)) + { + // Destination path can be without trailing slash if it come from GUI shell. + AddEndSlash(DestName,DestSize); + } + } + +#ifndef SFX_MODULE + if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE) + { + switch(Cmd->AppendArcNameToPath) + { + case APPENDARCNAME_DESTPATH: // To subdir of destination path. + wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); + SetExt(DestName,NULL,DestSize); + break; + case APPENDARCNAME_OWNSUBDIR: // To subdir of archive own dir. + wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); + SetExt(DestName,NULL,DestSize); + break; + case APPENDARCNAME_OWNDIR: // To archive own dir. + wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); + RemoveNameFromPath(DestName); + break; + } + AddEndSlash(DestName,DestSize); + } +#endif + +#ifndef SFX_MODULE + wchar *ArcPath=*Cmd->ExclArcPath!=0 ? Cmd->ExclArcPath:Cmd->ArcPath; + size_t ArcPathLength=wcslen(ArcPath); + if (ArcPathLength>0) + { + size_t NameLength=wcslen(ArcFileName); + if (NameLength>=ArcPathLength && wcsnicompc(ArcPath,ArcFileName,ArcPathLength)==0 && + (IsPathDiv(ArcPath[ArcPathLength-1]) || + IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0)) + { + ArcFileName+=Min(ArcPathLength,NameLength); + while (IsPathDiv(*ArcFileName)) + ArcFileName++; + if (*ArcFileName==0) // Excessive -ap switch. + { + *DestName=0; + return; + } + } + } +#endif + + wchar Command=Cmd->Command[0]; + // Use -ep3 only in systems, where disk letters are exist, not in Unix. + bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); + + // We do not use any user specified destination paths when extracting + // absolute paths in -ep3 mode. + if (AbsPaths) + *DestName=0; + + if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + wcsncatz(DestName,PointToName(ArcFileName),DestSize); + else + wcsncatz(DestName,ArcFileName,DestSize); + +#ifdef _WIN_ALL + // Must do after Cmd->ArcPath processing above, so file name and arc path + // trailing spaces are in sync. + if (!Cmd->AllowIncompatNames) + MakeNameCompatible(DestName,DestSize); +#endif + + wchar DiskLetter=toupperw(DestName[0]); + + if (AbsPaths) + { + if (DestName[1]=='_' && IsPathDiv(DestName[2]) && + DiskLetter>='A' && DiskLetter<='Z') + DestName[1]=':'; + else + if (DestName[0]=='_' && DestName[1]=='_') + { + // Convert __server\share to \\server\share. + DestName[0]=CPATHDIVIDER; + DestName[1]=CPATHDIVIDER; + } + } +} + + +#ifdef RARDLL +bool CmdExtract::ExtrDllGetPassword() +{ + if (!Cmd->Password.IsSet()) + { + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + Cmd->ManualPassword=true; + } + if (!Cmd->Password.IsSet()) + return false; + } + return true; +} +#endif + + +#ifndef RARDLL +bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd) +{ + if (!Cmd->Password.IsSet()) + { + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)/* || !Cmd->Password.IsSet()*/) + { + // Suppress "test is ok" message if user cancelled the password prompt. + uiMsg(UIERROR_INCERRCOUNT); + return false; + } + Cmd->ManualPassword=true; + } +#if !defined(SILENT) + else + if (!GlobalPassword && !Arc.FileHead.Solid) + { + eprintf(St(MUseCurPsw),ArcFileName); + switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll))) + { + case -1: + ErrHandler.Exit(RARX_USERBREAK); + case 2: + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)) + return false; + break; + case 3: + GlobalPassword=true; + break; + } + } +#endif + return true; +} +#endif + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd) +{ + if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS) { - unsigned int Code=DataIO.UnpRead(&Buffer[0],Buffer.Size()); - if (Code==0 || (int)Code==-1) + // We need the password in OEM encoding if file was encrypted by + // native RAR/DOS (not extender based). Let's make the conversion. + wchar PlainPsw[MAXPASSWORD]; + Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw)); + char PswA[MAXPASSWORD]; + CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA)); + PswA[ASIZE(PswA)-1]=0; + CharToWide(PswA,PlainPsw,ASIZE(PlainPsw)); + DestPwd.Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(PswA,sizeof(PswA)); + } +} +#endif + + +void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) +{ + if (Cmd->Test) + { + if (!Cmd->DisableNames) + { + mprintf(St(MExtrTestFile),ArcFileName); + mprintf(L" %s",St(MOk)); + } + return; + } + + MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + bool DirExist=false; + if (MDCode!=MKDIR_SUCCESS) + { + DirExist=FileExist(DestFileName); + if (DirExist && !IsDir(GetFileAttr(DestFileName))) + { + // File with name same as this directory exists. Propose user + // to overwrite it. + bool UserReject; + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + DirExist=false; + } + if (!DirExist) + { + CreatePath(DestFileName,true,Cmd->DisableNames); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + if (MDCode!=MKDIR_SUCCESS && !IsNameUsable(DestFileName)) + { + uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + MakeNameUsable(DestFileName,true); +#ifndef SFX_MODULE + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); + if (!DirExist) + { + if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); + CreatePath(DestFileName,true,Cmd->DisableNames); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + } + } + } + } + if (MDCode==MKDIR_SUCCESS) + { + if (!Cmd->DisableNames) + { + mprintf(St(MCreatDir),DestFileName); + mprintf(L" %s",St(MOk)); + } + PrevProcessed=true; + } + else + if (DirExist) + { + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(DestFileName,Arc.FileHead.FileAttr); + PrevProcessed=true; + } + else + { + uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName); + ErrHandler.SysErrMsg(); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + ErrHandler.SetErrorCode(RARX_CREATE); + } + if (PrevProcessed) + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE) + SetFileCompression(DestFileName,true); +#endif + SetFileHeaderExtra(Cmd,Arc,DestFileName); + SetDirTime(DestFileName, + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + } +} + + +bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) +{ + bool Success=true; + wchar Command=Cmd->Command[0]; +#if !defined(SFX_MODULE) + if (Command=='P') + CurFile.SetHandleType(FILE_HANDLESTD); +#endif + if ((Command=='E' || Command=='X') && !Cmd->Test) + { + bool UserReject; + // Specify "write only" mode to avoid OpenIndiana NAS problems + // with SetFileTime and read+write files. + if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { + Success=false; + if (!UserReject) + { + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); + if (FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName))) + uiMsg(UIERROR_DIRNAMEEXISTS); + +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + if (!IsNameUsable(DestFileName)) + { + uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); + + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + + MakeNameUsable(DestFileName,true); + + if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) + LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); + CreatePath(DestFileName,true,Cmd->DisableNames); + if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { +#ifndef SFX_MODULE + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + Success=true; + } + else + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); + } + } + } + } + return Success; +} + + +bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) +{ + bool WrongVer; + if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives. + WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5; + else + { +#ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives. + WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK; +#else // All formats since 1.3 for RAR. + WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK; +#endif + } + + // We can unpack stored files regardless of compression version field. + if (Arc.FileHead.Method==0) + WrongVer=false; + + if (WrongVer) + { + ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName); + uiMsg(UIERROR_NEWERRAR,Arc.FileName); + } + return !WrongVer; +} + + +#ifndef SFX_MODULE +// Find non-matched reference sources in solid and non-solid archives. +// Detect the optimal start position for semi-solid archives +// and optimal start volume for independent solid volumes. +// +// Alternatively we could collect references while extracting an archive +// and perform the second extraction pass for references only. +// But it would be slower for solid archives than scaning headers +// in first pass and extracting everything in second, as implemented now. +// +void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering) +{ + FreeAnalyzeData(); // If processing non-first archive in multiple archives set. + + wchar *ArgName=Cmd->FileArgs.GetString(); + Cmd->FileArgs.Rewind(); + if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0)) + return; // No need to check further for * and *.* masks. + + // Start search from first volume if all volumes preceding current are available. + wchar NextName[NM]; + if (Volume) + GetFirstVolIfFullSet(ArcName,NewNumbering,NextName,ASIZE(NextName)); + else + wcsncpyz(NextName,ArcName,ASIZE(NextName)); + + bool MatchFound=false; + bool PrevMatched=false; + bool OpenNext=false; + + bool FirstVolume=true; + + // We shall set FirstFile once for all volumes and not for each volume. + // So we do not reuse the outdated Analyze->StartPos from previous volume + // if extracted file resides completely in the beginning of current one. + bool FirstFile=true; + + while (true) + { + Archive Arc(Cmd); + if (!Arc.Open(NextName) || !Arc.IsArchive(false)) + { + if (OpenNext) + { + // If we couldn't open trailing volumes, we can't set early exit + // parameters. It is possible that some volume are on removable media + // and will be provided by user when extracting. + *Analyze->EndName=0; + Analyze->EndPos=0; + } + break; + } + + OpenNext=false; + while (Arc.ReadHeader()>0) + { + Wait(); + + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_ENDARC) + { + OpenNext|=Arc.EndArcHead.NextVolume; // Allow open next volume. + break; + } + if (HeaderType==HEAD_FILE) + { + if ((Arc.Format==RARFMT14 || Arc.Format==RARFMT15) && Arc.FileHead.UnpVer<=15) + { + // RAR versions earlier than 2.0 do not set per file solid flag. + // They have only the global archive solid flag, so we can't + // reliably analyze them here. + OpenNext=false; + break; + } + + if (!Arc.FileHead.SplitBefore) + { + if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here. + { + // We would gain nothing and unnecessarily complicate extraction + // if we set StartName for first volume or StartPos for first + // archived file. + if (!FirstVolume) + wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName)); + + // We shall set FirstFile once for all volumes for this code + // to work properly. Alternatively we could append + // "|| Analyze->StartPos!=0" to the condition, so we do not reuse + // the outdated Analyze->StartPos value from previous volume. + if (!FirstFile) + Analyze->StartPos=Arc.CurBlockPos; + } + + if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) + { + MatchFound = true; + PrevMatched = true; + + // Reset the previously set early exit position, if any, because + // we found a new matched file. + Analyze->EndPos=0; + + // Matched file reference pointing at maybe non-matched source file. + // Even though we know RedirName, we can't check if source file + // is certainly non-matched, because it can be filtered out by + // date or attributes, which we do not know here. + if (Arc.FileHead.RedirType==FSREDIR_FILECOPY) + { + bool AlreadyAdded=false; + for (size_t I=0;I<RefList.Size();I++) + if (wcscmp(Arc.FileHead.RedirName,RefList[I].RefName)==0) + { + // Increment the reference count if we added such reference + // source earlier. + RefList[I].RefCount++; + AlreadyAdded=true; + break; + } + + // Limit the maximum size of reference sources list to some + // sensible value to prevent the excessive memory allocation. + size_t MaxListSize=1000000; + + if (!AlreadyAdded && RefList.Size()<MaxListSize) + { + ExtractRef Ref={0}; + Ref.RefName=wcsdup(Arc.FileHead.RedirName); + Ref.RefCount=1; + RefList.Push(Ref); + } + } + } + else + { + if (PrevMatched) // First non-matched item after matched. + { + // We would perform the unnecessarily string comparison + // when extracting if we set this value for first volume + // or non-volume archive. + if (!FirstVolume) + wcsncpyz(Analyze->EndName,NextName,ASIZE(Analyze->EndName)); + Analyze->EndPos=Arc.CurBlockPos; + } + PrevMatched=false; + } + } + + FirstFile=false; + if (Arc.FileHead.SplitAfter) + { + OpenNext=true; // Allow open next volume. + break; + } + } + Arc.SeekToNext(); + } + Arc.Close(); + + if (Volume && OpenNext) + { + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + FirstVolume=false; + + // Needed for multivolume archives. Added in case some 'break' + // will quit early from loop above, so we do not set it in the loop. + // Now it can happen for hypothetical archive without file records + // and with HEAD_ENDARC record. + FirstFile=false; + } + else + break; + } + + // If file references are present, we can't reliably skip in semi-solid + // archives, because reference source can be present in skipped data. + if (RefList.Size()!=0) + memset(Analyze,0,sizeof(*Analyze)); +} +#endif + + +#ifndef SFX_MODULE +// Return the first volume name if all volumes preceding the specified +// are available. Otherwise return the specified volume name. +void CmdExtract::GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize) +{ + wchar FirstVolName[NM]; + VolNameToFirstName(SrcName,FirstVolName,ASIZE(FirstVolName),NewNumbering); + wchar NextName[NM]; + wcsncpyz(NextName,FirstVolName,ASIZE(NextName)); + wchar ResultName[NM]; + wcsncpyz(ResultName,SrcName,ASIZE(ResultName)); + while (true) + { + if (wcscmp(SrcName,NextName)==0) + { + wcsncpyz(ResultName,FirstVolName,DestSize); + break; + } + if (!FileExist(NextName)) break; - Code=Code<DestUnpSize ? Code:int64to32(DestUnpSize); - DataIO.UnpWrite(&Buffer[0],Code); - if (DestUnpSize>=0) - DestUnpSize-=Code; + NextVolumeName(NextName,ASIZE(NextName),!NewNumbering); } + wcsncpyz(DestName,ResultName,DestSize); } +#endif
\ No newline at end of file diff --git a/unrar/unrar/extract.hpp b/unrar/unrar/extract.hpp index dcf4534..18396c5 100644 --- a/unrar/unrar/extract.hpp +++ b/unrar/unrar/extract.hpp @@ -6,38 +6,90 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; class CmdExtract { private: - EXTRACT_ARC_CODE ExtractArchive(CommandData *Cmd); - RarTime StartTime; // time when extraction started + struct ExtractRef + { + wchar *RefName; + wchar *TmpName; + uint64 RefCount; + }; + Array<ExtractRef> RefList; + + struct AnalyzeData + { + wchar StartName[NM]; + uint64 StartPos; + wchar EndName[NM]; + uint64 EndPos; + } *Analyze; + + bool ArcAnalyzed; + + void FreeAnalyzeData(); + EXTRACT_ARC_CODE ExtractArchive(); + bool ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize); + void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); +#ifdef RARDLL + bool ExtrDllGetPassword(); +#else + bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd); +#endif +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); +#endif + void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName); + bool ExtrCreateFile(Archive &Arc,File &CurFile); + bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); +#ifndef SFX_MODULE + void AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering); + void GetFirstVolIfFullSet(const wchar *SrcName,bool NewNumbering,wchar *DestName,size_t DestSize); +#endif + + RarTime StartTime; // Time when extraction started. + + CommandData *Cmd; ComprDataIO DataIO; Unpack *Unp; - long TotalFileCount; + unsigned long TotalFileCount; - long FileCount; - long MatchedArgs; + unsigned long FileCount; + unsigned long MatchedArgs; bool FirstFile; bool AllMatchesExact; bool ReconstructDone; + bool UseExactVolName; + + // If any non-zero solid file was successfully unpacked before current. + // If true and if current encrypted file is broken, obviously + // the password is correct and we can report broken CRC without + // any wrong password hints. + bool AnySolidDataUnpackedWell; - char ArcName[NM]; - wchar ArcNameW[NM]; + wchar ArcName[NM]; - char Password[MAXPASSWORD]; - bool PasswordAll; - bool PrevExtracted; - char DestFileName[NM]; - wchar DestFileNameW[NM]; + bool GlobalPassword; + bool PrevProcessed; // If previous file was successfully extracted or tested. + wchar DestFileName[NM]; bool PasswordCancelled; + + // In Windows it is set to true if at least one symlink with ".." + // in target was extracted. + bool ConvertSymlinkPaths; + + // Last path checked for symlinks. We use it to improve the performance, + // so we do not check recently checked folders again. + std::wstring LastCheckedSymlink; + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + bool Fat32,NotFat32; +#endif public: - CmdExtract(); + CmdExtract(CommandData *Cmd); ~CmdExtract(); - void DoExtract(CommandData *Cmd); - void ExtractArchiveInit(CommandData *Cmd,Archive &Arc); - bool ExtractCurrentFile(CommandData *Cmd,Archive &Arc,int HeaderSize, - bool &Repeat); - static void UnstoreFile(ComprDataIO &DataIO,Int64 DestUnpSize); - - bool SignatureFound; + void DoExtract(); + void ExtractArchiveInit(Archive &Arc); + bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat); + static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize); }; #endif diff --git a/unrar/unrar/filcreat.cpp b/unrar/unrar/filcreat.cpp index c377dd6..d58e4f6 100644 --- a/unrar/unrar/filcreat.cpp +++ b/unrar/unrar/filcreat.cpp @@ -1,239 +1,165 @@ #include "rar.hpp" -bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW, - OVERWRITE_MODE Mode,bool *UserReject,Int64 FileSize, - uint FileTime) +// If NewFile==NULL, we delete created file after user confirmation. +// It is useful if we need to overwrite an existing folder or file, +// but need user confirmation for that. +bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) { if (UserReject!=NULL) *UserReject=false; -#if defined(_WIN_32) && !defined(_WIN_CE) +#ifdef _WIN_ALL bool ShortNameChanged=false; #endif - while (FileExist(Name,NameW)) + while (FileExist(Name)) { -#if defined(_WIN_32) && !defined(_WIN_CE) +#if defined(_WIN_ALL) if (!ShortNameChanged) { + // Avoid the infinite loop if UpdateExistingShortName returns + // the same name. ShortNameChanged=true; - if (UpdateExistingShortName(Name,NameW)) + + // Maybe our long name matches the short name of existing file. + // Let's check if we can change the short name. + if (UpdateExistingShortName(Name)) continue; } + // Allow short name check again. It is necessary, because rename and + // autorename below can change the name, so we need to check it again. + ShortNameChanged=false; #endif - if (Mode==OVERWRITE_NONE) + UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); + + if (Choice==UIASKREP_R_REPLACE) + break; + if (Choice==UIASKREP_R_SKIP) { if (UserReject!=NULL) *UserReject=true; - return(false); - } -#ifdef SILENT - Mode=OVERWRITE_ALL; -#endif - if (Cmd->AllYes || Mode==OVERWRITE_ALL) - break; - if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK) - { - eprintf(St(MFileExists),Name); - int Choice=Ask(St(MYesNoAllRenQ)); - if (Choice==1) - break; - if (Choice==2) - { - if (UserReject!=NULL) - *UserReject=true; - return(false); - } - if (Choice==3) - { - Cmd->Overwrite=OVERWRITE_ALL; - break; - } - if (Choice==4) - { - if (UserReject!=NULL) - *UserReject=true; - Cmd->Overwrite=OVERWRITE_NONE; - return(false); - } - if (Choice==5) - { - mprintf(St(MAskNewName)); - - char NewName[NM]; -#ifdef _WIN_32 - File SrcFile; - SrcFile.SetHandleType(FILE_HANDLESTD); - int Size=SrcFile.Read(NewName,sizeof(NewName)-1); - NewName[Size]=0; - OemToChar(NewName,NewName); -#else - fgets(NewName,sizeof(NewName),stdin); -#endif - RemoveLF(NewName); - if (PointToName(NewName)==NewName) - strcpy(PointToName(Name),NewName); - else - strcpy(Name,NewName); - if (NameW!=NULL) - *NameW=0; - continue; - } - if (Choice==6) - ErrHandler.Exit(USER_BREAK); - } - if (Mode==OVERWRITE_AUTORENAME) - { - if (GetAutoRenamedName(Name)) - { - if (NameW!=NULL) - *NameW=0; - } - else - Mode=OVERWRITE_DEFAULT; - continue; + return false; } + if (Choice==UIASKREP_R_CANCEL) + ErrHandler.Exit(RARX_USERBREAK); } - if (NewFile!=NULL && NewFile->Create(Name,NameW)) - return(true); - PrepareToDelete(Name,NameW); - CreatePath(Name,NameW,true); - return(NewFile!=NULL ? NewFile->Create(Name,NameW):DelFile(Name,NameW)); + + // Try to truncate the existing file first instead of delete, + // so we preserve existing file permissions, such as NTFS permissions, + // also as "Compressed" attribute and hard links. In GUI version we avoid + // deleting an existing file for non-.rar archive formats as well. + uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; + if (NewFile!=NULL && NewFile->Create(Name,FileMode)) + return true; + + CreatePath(Name,true,Cmd->DisableNames); + return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); } -bool GetAutoRenamedName(char *Name) +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) { - char NewName[NM]; - - if (strlen(Name)>sizeof(NewName)-10) - return(false); - char *Ext=GetExt(Name); + wchar NewName[NM]; + size_t NameLength=wcslen(Name); + wchar *Ext=GetExt(Name); if (Ext==NULL) - Ext=Name+strlen(Name); - for (int FileVer=1;;FileVer++) + Ext=Name+NameLength; + for (uint FileVer=1;;FileVer++) { - sprintf(NewName,"%.*s(%d)%s",Ext-Name,Name,FileVer,Ext); + swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); if (!FileExist(NewName)) { - strcpy(Name,NewName); + wcsncpyz(Name,NewName,MaxNameSize); break; } if (FileVer>=1000000) - return(false); + return false; } - return(true); + return true; } -#if defined(_WIN_32) && !defined(_WIN_CE) -bool UpdateExistingShortName(char *Name,wchar *NameW) +#if defined(_WIN_ALL) +// If we find a file, which short name is equal to 'Name', we try to change +// its short name, while preserving the long name. It helps when unpacking +// an archived file, which long name is equal to short name of already +// existing file. Otherwise we would overwrite the already existing file, +// even though its long name does not match the name of unpacking file. +bool UpdateExistingShortName(const wchar *Name) { - FindData fd; - if (!FindFile::FastFind(Name,NameW,&fd)) - return(false); - if (*fd.Name==0 || *fd.ShortName==0) - return(false); - if (stricomp(PointToName(fd.Name),fd.ShortName)==0 || - stricomp(PointToName(Name),fd.ShortName)!=0) - return(false); - - char NewName[NM]; - for (int I=0;I<10000;I+=123) + wchar LongPathName[NM]; + DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName)); + if (Res==0 || Res>=ASIZE(LongPathName)) + return false; + wchar ShortPathName[NM]; + Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName)); + if (Res==0 || Res>=ASIZE(ShortPathName)) + return false; + wchar *LongName=PointToName(LongPathName); + wchar *ShortName=PointToName(ShortPathName); + + // We continue only if file has a short name, which does not match its + // long name, and this short name is equal to name of file which we need + // to create. + if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 || + wcsicomp(PointToName(Name),ShortName)!=0) + return false; + + // Generate the temporary new name for existing file. + wchar NewName[NM]; + *NewName=0; + for (int I=0;I<10000 && *NewName==0;I+=123) { - strncpyz(NewName,Name,ASIZE(NewName)); - sprintf(PointToName(NewName),"rtmp%d",I); - if (!FileExist(NewName)) - break; + // Here we copy the path part of file to create. We'll make the temporary + // file in the same folder. + wcsncpyz(NewName,Name,ASIZE(NewName)); + + // Here we set the random name part. + swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); + + // If such file is already exist, try next random name. + if (FileExist(NewName)) + *NewName=0; } - if (FileExist(NewName)) - return(false); - char FullName[NM]; - strncpyz(FullName,Name,ASIZE(FullName)); - strcpy(PointToName(FullName),PointToName(fd.Name)); + + // If we could not generate the name not used by any other file, we return. + if (*NewName==0) + return false; + + // FastFind returns the name without path, but we need the fully qualified + // name for renaming, so we use the path from file to create and long name + // from existing file. + wchar FullName[NM]; + wcsncpyz(FullName,Name,ASIZE(FullName)); + SetName(FullName,LongName,ASIZE(FullName)); + + // Rename the existing file to randomly generated name. Normally it changes + // the short name too. if (!MoveFile(FullName,NewName)) - return(false); + return false; + + // Now we need to create the temporary empty file with same name as + // short name of our already existing file. We do it to occupy its previous + // short name and not allow to use it again when renaming the file back to + // its original long name. File KeepShortFile; bool Created=false; if (!FileExist(Name)) - Created=KeepShortFile.Create(Name); + Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD); + + // Now we rename the existing file from temporary name to original long name. + // Since its previous short name is occupied by another file, it should + // get another short name. MoveFile(NewName,FullName); + if (Created) { + // Delete the temporary zero length file occupying the short name, KeepShortFile.Close(); KeepShortFile.Delete(); } - return(true); -} - -/* -bool UpdateExistingShortName(char *Name,wchar *NameW) -{ - if (WinNT()<5) - return(false); - FindData fd; - if (!FindFile::FastFind(Name,NameW,&fd)) - return(false); - if (*fd.Name==0 || *fd.ShortName==0) - return(false); - if (stricomp(PointToName(fd.Name),fd.ShortName)==0 || - stricomp(PointToName(Name),fd.ShortName)!=0) - return(false); - - typedef BOOL (WINAPI *SETFILESHORTNAME)(HANDLE,LPCSTR); - static SETFILESHORTNAME pSetFileShortName=NULL; - if (pSetFileShortName==NULL) - { - HMODULE hKernel=GetModuleHandle("kernel32.dll"); - if (hKernel!=NULL) - pSetFileShortName=(SETFILESHORTNAME)GetProcAddress(hKernel,"SetFileShortNameA"); - if (pSetFileShortName==NULL) - return(false); - } - static bool RestoreEnabled=false; - if (!RestoreEnabled) - { - HANDLE hToken; - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) - return(false); - - TOKEN_PRIVILEGES tp; - tp.PrivilegeCount = 1; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - if (LookupPrivilegeValue(NULL,SE_RESTORE_NAME,&tp.Privileges[0].Luid)) - AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL); - - CloseHandle(hToken); - RestoreEnabled=true; - } - - wchar FileNameW[NM]; - GetWideName(Name,NameW,FileNameW); - HANDLE hFile=CreateFileW(FileNameW,GENERIC_WRITE|DELETE,FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); - if (hFile==INVALID_HANDLE_VALUE) - return(false); - - bool RetCode=false; - - char FullName[NM]; - wchar FullNameW[NM]; - strcpy(FullName,Name); - strcpyw(FullNameW,NullToEmpty(NameW)); - for (int I=1;I<1000000;I++) - { - char NewName[NM]; - sprintf(NewName,"NAME~%d.%d",I%1000,I/1000+1); - strcpy(PointToName(FullName),NewName); - if (*FullNameW) - CharToWide(NewName,PointToName(FullNameW)); - if (!FileExist(FullName,FullNameW)) - { - RetCode=pSetFileShortName(hFile,NewName); - break; - } - } - CloseHandle(hFile); - return(RetCode); + // We successfully changed the short name. Maybe sometimes we'll simplify + // this function by use of SetFileShortName Windows API call. + // But SetFileShortName is not available in older Windows. + return true; } -*/ #endif diff --git a/unrar/unrar/filcreat.hpp b/unrar/unrar/filcreat.hpp index d803582..456a4a4 100644 --- a/unrar/unrar/filcreat.hpp +++ b/unrar/unrar/filcreat.hpp @@ -1,13 +1,14 @@ #ifndef _RAR_FILECREATE_ #define _RAR_FILECREATE_ -bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW, - OVERWRITE_MODE Mode,bool *UserReject,Int64 FileSize=INT64ERR, - uint FileTime=0); -bool GetAutoRenamedName(char *Name); +bool FileCreate(CommandData *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize=INT64NDF, + RarTime *FileTime=NULL,bool WriteOnly=false); -#if defined(_WIN_32) && !defined(_WIN_CE) -bool UpdateExistingShortName(char *Name,wchar *NameW); +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize); + +#if defined(_WIN_ALL) +bool UpdateExistingShortName(const wchar *Name); #endif #endif diff --git a/unrar/unrar/file.cpp b/unrar/unrar/file.cpp index 8fe71ec..7bf60fd 100644 --- a/unrar/unrar/file.cpp +++ b/unrar/unrar/file.cpp @@ -1,32 +1,31 @@ #include "rar.hpp" -static File *CreatedFiles[256]; -static int RemoveCreatedActive=0; - File::File() { - hFile=BAD_HANDLE; + hFile=FILE_BAD_HANDLE; *FileName=0; - *FileNameW=0; NewFile=false; LastWrite=false; HandleType=FILE_HANDLENORMAL; + LineInput=false; SkipClose=false; - IgnoreReadErrors=false; ErrorType=FILE_SUCCESS; OpenShared=false; AllowDelete=true; - CloseCount=0; AllowExceptions=true; -#ifdef _WIN_32 - NoSequentialRead=false; + PreserveAtime=false; +#ifdef _WIN_ALL + CreateMode=FMF_UNDEFINED; #endif + ReadErrorMode=FREM_ASK; + TruncatedAfterReadError=false; + CurFilePos=0; } File::~File() { - if (hFile!=BAD_HANDLE && !SkipClose) + if (hFile!=FILE_BAD_HANDLE && !SkipClose) if (NewFile) Delete(); else @@ -37,294 +36,326 @@ File::~File() void File::operator = (File &SrcFile) { hFile=SrcFile.hFile; - strcpy(FileName,SrcFile.FileName); NewFile=SrcFile.NewFile; LastWrite=SrcFile.LastWrite; HandleType=SrcFile.HandleType; + TruncatedAfterReadError=SrcFile.TruncatedAfterReadError; + wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName)); SrcFile.SkipClose=true; } -bool File::Open(const char *Name,const wchar *NameW,bool OpenShared,bool Update) +bool File::Open(const wchar *Name,uint Mode) { ErrorType=FILE_SUCCESS; FileHandle hNewFile; - if (File::OpenShared) - OpenShared=true; -#ifdef _WIN_32 - uint Access=GENERIC_READ; - if (Update) + bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0; + bool UpdateMode=(Mode & FMF_UPDATE)!=0; + bool WriteMode=(Mode & FMF_WRITE)!=0; +#ifdef _WIN_ALL + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ; + if (UpdateMode) Access|=GENERIC_WRITE; - uint ShareMode=FILE_SHARE_READ; + uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ; if (OpenShared) ShareMode|=FILE_SHARE_WRITE; - uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN; - if (WinNT() && NameW!=NULL && *NameW!=0) - hNewFile=CreateFileW(NameW,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); - else - hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + uint Flags=FILE_FLAG_SEQUENTIAL_SCAN; + FindData FD; + if (PreserveAtime) + Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime. + hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + DWORD LastError; + if (hNewFile==FILE_BAD_HANDLE) + { + LastError=GetLastError(); - if (hNewFile==BAD_HANDLE && GetLastError()==ERROR_FILE_NOT_FOUND) + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + { + hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + // For archive names longer than 260 characters first CreateFile + // (without \\?\) fails and sets LastError to 3 (access denied). + // We need the correct "file not found" error code to decide + // if we create a new archive or quit with "cannot create" error. + // So we need to check the error code after \\?\ CreateFile again, + // otherwise we'll fail to create new archives with long names. + // But we cannot simply assign the new code to LastError, + // because it would break "..\arcname.rar" relative names processing. + // First CreateFile returns the correct "file not found" code for such + // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating + // dots as a directory name. So we check only for "file not found" + // error here and for other errors use the first CreateFile result. + if (GetLastError()==ERROR_FILE_NOT_FOUND) + LastError=ERROR_FILE_NOT_FOUND; + } + } + if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND) ErrorType=FILE_NOTFOUND; + if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE) + { + FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification. + SetFileTime(hNewFile,NULL,&ft,NULL); + } + #else - int flags=Update ? O_RDWR:O_RDONLY; + int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); #ifdef O_BINARY flags|=O_BINARY; #if defined(_AIX) && defined(_LARGE_FILE_API) flags|=O_LARGEFILE; #endif #endif -#if defined(_EMX) && !defined(_DJGPP) - int sflags=OpenShared ? SH_DENYNO:SH_DENYWR; - int handle=sopen(Name,flags,sflags); -#else - int handle=open(Name,flags); + // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+. +#if defined(O_NOATIME) + if (PreserveAtime) + flags|=O_NOATIME; +#endif + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + + int handle=open(NameA,flags); #ifdef LOCK_EX #ifdef _OSF_SOURCE extern "C" int flock(int, int); #endif - - if (!OpenShared && Update && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) + if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) { close(handle); - return(false); + return false; } + #endif + if (handle==-1) + hNewFile=FILE_BAD_HANDLE; + else + { +#ifdef FILE_USE_OPEN + hNewFile=handle; +#else + hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY); #endif - hNewFile=handle==-1 ? BAD_HANDLE:fdopen(handle,Update ? UPDATEBINARY:READBINARY); - if (hNewFile==BAD_HANDLE && errno==ENOENT) + } + if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT) ErrorType=FILE_NOTFOUND; #endif NewFile=false; HandleType=FILE_HANDLENORMAL; SkipClose=false; - bool Success=hNewFile!=BAD_HANDLE; + bool Success=hNewFile!=FILE_BAD_HANDLE; if (Success) { hFile=hNewFile; - if (NameW!=NULL) - strcpyw(FileNameW,NameW); - else - *FileNameW=0; - if (Name!=NULL) - strcpy(FileName,Name); - else - WideToChar(NameW,FileName); - AddFileToList(hFile); + wcsncpyz(FileName,Name,ASIZE(FileName)); + TruncatedAfterReadError=false; } - return(Success); + return Success; } -#if !defined(SHELL_EXT) && !defined(SFX_MODULE) -void File::TOpen(const char *Name,const wchar *NameW) +#if !defined(SFX_MODULE) +void File::TOpen(const wchar *Name) { - if (!WOpen(Name,NameW)) - ErrHandler.Exit(OPEN_ERROR); + if (!WOpen(Name)) + ErrHandler.Exit(RARX_OPEN); } #endif -bool File::WOpen(const char *Name,const wchar *NameW) +bool File::WOpen(const wchar *Name) { - if (Open(Name,NameW)) - return(true); + if (Open(Name)) + return true; ErrHandler.OpenErrorMsg(Name); - return(false); -} + return false; +} + + +bool File::Create(const wchar *Name,uint Mode) +{ + // OpenIndiana based NAS and CIFS shares fail to set the file time if file + // was created in read+write mode and some data was written and not flushed + // before SetFileTime call. So we should use the write only mode if we plan + // SetFileTime call and do not need to read from file. + bool WriteMode=(Mode & FMF_WRITE)!=0; + bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared; +#ifdef _WIN_ALL + CreateMode=Mode; + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE; + DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0; + + // Windows automatically removes dots and spaces in the end of file name, + // So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + + if (Special && (Mode & FMF_STANDARDNAMES)==0) + hFile=FILE_BAD_HANDLE; + else + hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + if (hFile==FILE_BAD_HANDLE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + } -bool File::Create(const char *Name,const wchar *NameW,bool ShareRead) -{ -#ifdef _WIN_32 - DWORD ShareMode=(ShareRead || File::OpenShared) ? FILE_SHARE_READ:0; - if (WinNT() && NameW!=NULL && *NameW!=0) - hFile=CreateFileW(NameW,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL, - CREATE_ALWAYS,0,NULL); - else - hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL, - CREATE_ALWAYS,0,NULL); #else - hFile=fopen(Name,CREATEBINARY); + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); +#ifdef FILE_USE_OPEN + hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); +#else + hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY); +#endif #endif NewFile=true; HandleType=FILE_HANDLENORMAL; SkipClose=false; - if (NameW!=NULL) - strcpyw(FileNameW,NameW); - else - *FileNameW=0; - if (Name!=NULL) - strcpy(FileName,Name); - else - WideToChar(NameW,FileName); - AddFileToList(hFile); - return(hFile!=BAD_HANDLE); + wcsncpyz(FileName,Name,ASIZE(FileName)); + return hFile!=FILE_BAD_HANDLE; } -void File::AddFileToList(FileHandle hFile) +#if !defined(SFX_MODULE) +void File::TCreate(const wchar *Name,uint Mode) { - if (hFile!=BAD_HANDLE) - for (int I=0;I<sizeof(CreatedFiles)/sizeof(CreatedFiles[0]);I++) - if (CreatedFiles[I]==NULL) - { - CreatedFiles[I]=this; - break; - } -} - - -#if !defined(SHELL_EXT) && !defined(SFX_MODULE) -void File::TCreate(const char *Name,const wchar *NameW,bool ShareRead) -{ - if (!WCreate(Name,NameW,ShareRead)) - ErrHandler.Exit(FATAL_ERROR); + if (!WCreate(Name,Mode)) + ErrHandler.Exit(RARX_FATAL); } #endif -bool File::WCreate(const char *Name,const wchar *NameW,bool ShareRead) +bool File::WCreate(const wchar *Name,uint Mode) { - if (Create(Name,NameW,ShareRead)) - return(true); - ErrHandler.SetErrorCode(CREATE_ERROR); + if (Create(Name,Mode)) + return true; ErrHandler.CreateErrorMsg(Name); - return(false); + return false; } bool File::Close() { bool Success=true; - if (HandleType!=FILE_HANDLENORMAL) - HandleType=FILE_HANDLENORMAL; - else - if (hFile!=BAD_HANDLE) + + if (hFile!=FILE_BAD_HANDLE) + { + if (!SkipClose) { - if (!SkipClose) - { -#ifdef _WIN_32 - Success=CloseHandle(hFile); +#ifdef _WIN_ALL + // We use the standard system handle for stdout in Windows + // and it must not be closed here. + if (HandleType==FILE_HANDLENORMAL) + Success=CloseHandle(hFile)==TRUE; #else - Success=fclose(hFile)!=EOF; -#endif - if (Success || !RemoveCreatedActive) - for (int I=0;I<sizeof(CreatedFiles)/sizeof(CreatedFiles[0]);I++) - if (CreatedFiles[I]==this) - { - CreatedFiles[I]=NULL; - break; - } - } - hFile=BAD_HANDLE; - if (!Success && AllowExceptions) - ErrHandler.CloseError(FileName); - } - CloseCount++; - return(Success); -} - - -void File::Flush() -{ -#ifdef _WIN_32 - FlushFileBuffers(hFile); +#ifdef FILE_USE_OPEN + Success=close(hFile)!=-1; #else - fflush(hFile); + Success=fclose(hFile)!=EOF; #endif +#endif + } + hFile=FILE_BAD_HANDLE; + } + HandleType=FILE_HANDLENORMAL; + if (!Success && AllowExceptions) + ErrHandler.CloseError(FileName); + return Success; } bool File::Delete() { if (HandleType!=FILE_HANDLENORMAL) - return(false); - if (hFile!=BAD_HANDLE) + return false; + if (hFile!=FILE_BAD_HANDLE) Close(); if (!AllowDelete) - return(false); - return(DelFile(FileName,FileNameW)); + return false; + return DelFile(FileName); } -bool File::Rename(const char *NewName,const wchar *NewNameW) +bool File::Rename(const wchar *NewName) { - // we do not need to rename if names are already same - bool Success=strcmp(FileName,NewName)==0; - if (Success && *FileNameW!=0 && *NullToEmpty(NewNameW)!=0) - Success=strcmpw(FileNameW,NewNameW)==0; + // No need to rename if names are already same. + bool Success=wcscmp(FileName,NewName)==0; if (!Success) - Success=RenameFile(FileName,FileNameW,NewName,NewNameW); + Success=RenameFile(FileName,NewName); if (Success) - { - // renamed successfully, storing the new name - strcpy(FileName,NewName); - strcpyw(FileNameW,NullToEmpty(NewNameW)); - } - return(Success); + wcsncpyz(FileName,NewName,ASIZE(FileName)); + + return Success; } -void File::Write(const void *Data,int Size) +bool File::Write(const void *Data,size_t Size) { if (Size==0) - return; -#ifndef _WIN_CE - if (HandleType!=FILE_HANDLENORMAL) - switch(HandleType) - { - case FILE_HANDLESTD: -#ifdef _WIN_32 - hFile=GetStdHandle(STD_OUTPUT_HANDLE); + return true; + if (HandleType==FILE_HANDLESTD) + { +#ifdef _WIN_ALL + hFile=GetStdHandle(STD_OUTPUT_HANDLE); #else - hFile=stdout; -#endif - break; - case FILE_HANDLEERR: -#ifdef _WIN_32 - hFile=GetStdHandle(STD_ERROR_HANDLE); + // Cannot use the standard stdout here, because it already has wide orientation. + if (hFile==FILE_BAD_HANDLE) + { +#ifdef FILE_USE_OPEN + hFile=dup(STDOUT_FILENO); // Open new stdout stream. #else - hFile=stderr; + hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream. #endif - break; } #endif + } + bool Success; while (1) { - bool Success=false; -#ifdef _WIN_32 + Success=false; +#ifdef _WIN_ALL DWORD Written=0; if (HandleType!=FILE_HANDLENORMAL) { // writing to stdout can fail in old Windows if data block is too large - const int MaxSize=0x4000; - for (int I=0;I<Size;I+=MaxSize) - if (!(Success=WriteFile(hFile,(byte *)Data+I,Min(Size-I,MaxSize),&Written,NULL))) + const size_t MaxSize=0x4000; + for (size_t I=0;I<Size;I+=MaxSize) + { + Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE; + if (!Success) break; + } } else - Success=WriteFile(hFile,Data,Size,&Written,NULL); + Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE; +#else +#ifdef FILE_USE_OPEN + ssize_t Written=write(hFile,Data,Size); + Success=Written==Size; #else int Written=fwrite(Data,1,Size,hFile); Success=Written==Size && !ferror(hFile); #endif +#endif if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL) { -#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(RARDLL) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL) int ErrCode=GetLastError(); - Int64 FilePos=Tell(); - Int64 FreeSize=GetFreeDisk(FileName); + int64 FilePos=Tell(); + uint64 FreeSize=GetFreeDisk(FileName); SetLastError(ErrCode); if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff) ErrHandler.WriteErrorFAT(FileName); #endif - if (ErrHandler.AskRepeatWrite(FileName)) + if (ErrHandler.AskRepeatWrite(FileName,false)) { -#ifndef _WIN_32 +#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN) clearerr(hFile); #endif if (Written<Size && Written>0) @@ -336,75 +367,134 @@ void File::Write(const void *Data,int Size) break; } LastWrite=true; + return Success; // It can return false only if AllowExceptions is disabled. } -int File::Read(void *Data,int Size) +int File::Read(void *Data,size_t Size) { - Int64 FilePos=0; //initialized only to suppress some compilers warning + if (TruncatedAfterReadError) + return 0; - if (IgnoreReadErrors) + int64 FilePos=0; // Initialized only to suppress some compilers warning. + + if (ReadErrorMode==FREM_IGNORE) FilePos=Tell(); - int ReadSize; + int TotalRead=0; while (true) { - ReadSize=DirectRead(Data,Size); + int ReadSize=DirectRead(Data,Size); + if (ReadSize==-1) { ErrorType=FILE_READERROR; if (AllowExceptions) - if (IgnoreReadErrors) + if (ReadErrorMode==FREM_IGNORE) { ReadSize=0; - for (int I=0;I<Size;I+=512) + for (size_t I=0;I<Size;I+=512) { Seek(FilePos+I,SEEK_SET); - int SizeToRead=Min(Size-I,512); + size_t SizeToRead=Min(Size-I,512); int ReadCode=DirectRead(Data,SizeToRead); ReadSize+=(ReadCode==-1) ? 512:ReadCode; + if (ReadSize!=-1) + TotalRead+=ReadSize; } } else { - if (HandleType==FILE_HANDLENORMAL && ErrHandler.AskRepeatRead(FileName)) - continue; + bool Ignore=false,Retry=false,Quit=false; + if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL) + { + ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit); + if (Retry) + continue; + } + if (Ignore || ReadErrorMode==FREM_TRUNCATE) + { + TruncatedAfterReadError=true; + return 0; + } ErrHandler.ReadError(FileName); } } + TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here. + + if (HandleType==FILE_HANDLESTD && !LineInput && ReadSize>0 && (uint)ReadSize<Size) + { + // Unlike regular files, for pipe we can read only as much as was + // written at the other end of pipe. We had seen data coming in small + // ~80 byte chunks when piping from 'type arc.rar'. Extraction code + // would fail if we read an incomplete archive header from stdin. + // So here we ensure that requested size is completely read. + // But we return the available data immediately in "line input" mode, + // when processing user's input in console prompts. Otherwise apps + // piping user responses to multiple Ask() prompts can hang if no more + // data is available yet and pipe isn't closed. + Data=(byte*)Data+ReadSize; + Size-=ReadSize; + continue; + } break; } - return(ReadSize); + if (TotalRead>0) // Can be -1 for error and AllowExceptions disabled. + CurFilePos+=TotalRead; + return TotalRead; // It can return -1 only if AllowExceptions is disabled. } -int File::DirectRead(void *Data,int Size) +// Returns -1 in case of error. +int File::DirectRead(void *Data,size_t Size) { -#ifdef _WIN_32 - const int MaxDeviceRead=20000; +#ifdef _WIN_ALL + const size_t MaxDeviceRead=20000; + const size_t MaxLockedRead=32768; #endif -#ifndef _WIN_CE if (HandleType==FILE_HANDLESTD) { -#ifdef _WIN_32 - if (Size>MaxDeviceRead) - Size=MaxDeviceRead; +#ifdef _WIN_ALL +// if (Size>MaxDeviceRead) +// Size=MaxDeviceRead; hFile=GetStdHandle(STD_INPUT_HANDLE); #else +#ifdef FILE_USE_OPEN + hFile=STDIN_FILENO; +#else hFile=stdin; #endif - } #endif -#ifdef _WIN_32 + } +#ifdef _WIN_ALL + // For pipes like 'type file.txt | rar -si arcname' ReadFile may return + // data in small ~4KB blocks. It may slightly reduce the compression ratio. DWORD Read; - if (!ReadFile(hFile,Data,Size,&Read,NULL)) + if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL)) { if (IsDevice() && Size>MaxDeviceRead) - return(DirectRead(Data,MaxDeviceRead)); + return DirectRead(Data,MaxDeviceRead); if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE) - return(0); - return(-1); + return 0; + + // We had a bug report about failure to archive 1C database lock file + // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB + // permanently locked. If our first read request uses too large buffer + // and if we are in -dh mode, so we were able to open the file, + // we'll fail with "Read error". So now we use try a smaller buffer size + // in case of lock error. + if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead && + GetLastError()==ERROR_LOCK_VIOLATION) + return DirectRead(Data,MaxLockedRead); + + return -1; } - return(Read); + return Read; +#else +#ifdef FILE_USE_OPEN + ssize_t ReadSize=read(hFile,Data,Size); + if (ReadSize==-1) + return -1; + return (int)ReadSize; #else if (LastWrite) { @@ -412,78 +502,130 @@ int File::DirectRead(void *Data,int Size) LastWrite=false; } clearerr(hFile); - int ReadSize=fread(Data,1,Size,hFile); + size_t ReadSize=fread(Data,1,Size,hFile); if (ferror(hFile)) - return(-1); - return(ReadSize); + return -1; + return (int)ReadSize; +#endif #endif } -void File::Seek(Int64 Offset,int Method) +void File::Seek(int64 Offset,int Method) { if (!RawSeek(Offset,Method) && AllowExceptions) ErrHandler.SeekError(FileName); } -bool File::RawSeek(Int64 Offset,int Method) +bool File::RawSeek(int64 Offset,int Method) { - if (hFile==BAD_HANDLE) - return(true); - if (!is64plus(Offset) && Method!=SEEK_SET) + if (hFile==FILE_BAD_HANDLE) + return true; + if (!IsSeekable()) // To extract archives from stdin with -si. + { + // We tried to dynamically allocate 32 KB buffer here, but it improved + // speed in Windows 10 by mere ~1.5%. + byte Buf[4096]; + if (Method==SEEK_CUR || Method==SEEK_SET && Offset>=CurFilePos) + { + uint64 SkipSize=Method==SEEK_CUR ? Offset:Offset-CurFilePos; + while (SkipSize>0) // Reading to emulate seek forward. + { + int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf))); + if (ReadSize<=0) + return false; + SkipSize-=ReadSize; + CurFilePos+=ReadSize; + } + return true; + } + // May need it in FileLength() in Archive::UnexpEndArcMsg() when unpacking + // RAR 4.x archives without the end of archive block created with -en. + if (Method==SEEK_END) + { + int ReadSize; + while ((ReadSize=Read(Buf,ASIZE(Buf)))>0) + CurFilePos+=ReadSize; + return true; + } + + return false; // Backward seek on unseekable file. + } + if (Offset<0 && Method!=SEEK_SET) { Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset; Method=SEEK_SET; } -#ifdef _WIN_32 - LONG HighDist=int64to32(Offset>>32); - if (SetFilePointer(hFile,int64to32(Offset),&HighDist,Method)==0xffffffff && +#ifdef _WIN_ALL + LONG HighDist=(LONG)(Offset>>32); + if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff && GetLastError()!=NO_ERROR) - return(false); + return false; #else LastWrite=false; -#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) +#ifdef FILE_USE_OPEN + if (lseek(hFile,(off_t)Offset,Method)==-1) + return false; +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) if (fseeko(hFile,Offset,Method)!=0) + return false; #else - if (fseek(hFile,(long)int64to32(Offset),Method)!=0) + if (fseek(hFile,(long)Offset,Method)!=0) + return false; #endif - return(false); #endif - return(true); + return true; } -Int64 File::Tell() +int64 File::Tell() { -#ifdef _WIN_32 + if (hFile==FILE_BAD_HANDLE) + if (AllowExceptions) + ErrHandler.SeekError(FileName); + else + return -1; + if (!IsSeekable()) + return CurFilePos; +#ifdef _WIN_ALL LONG HighDist=0; uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT); if (LowDist==0xffffffff && GetLastError()!=NO_ERROR) if (AllowExceptions) ErrHandler.SeekError(FileName); else - return(-1); - return(int32to64(HighDist,LowDist)); + return -1; + return INT32TO64(HighDist,LowDist); #else -#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) - return(ftello(hFile)); +#ifdef FILE_USE_OPEN + return lseek(hFile,0,SEEK_CUR); +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) + return ftello(hFile); #else - return(ftell(hFile)); + return ftell(hFile); #endif #endif } -void File::Prealloc(Int64 Size) +void File::Prealloc(int64 Size) { -#ifdef _WIN_32 +#ifdef _WIN_ALL if (RawSeek(Size,SEEK_SET)) { Truncate(); Seek(0,SEEK_SET); } #endif + +#if defined(_UNIX) && defined(USE_FALLOCATE) + // fallocate is rather new call. Only latest kernels support it. + // So we are not using it by default yet. + int fd = GetFD(); + if (fd >= 0) + fallocate(fd, 0, 0, Size); +#endif } @@ -491,7 +633,7 @@ byte File::GetByte() { byte Byte=0; Read(&Byte,1); - return(Byte); + return Byte; } @@ -503,27 +645,46 @@ void File::PutByte(byte Byte) bool File::Truncate() { -#ifdef _WIN_32 - return(SetEndOfFile(hFile)); +#ifdef _WIN_ALL + return SetEndOfFile(hFile)==TRUE; +#else + return ftruncate(GetFD(),(off_t)Tell())==0; +#endif +} + + +void File::Flush() +{ +#ifdef _WIN_ALL + FlushFileBuffers(hFile); #else - return(false); +#ifndef FILE_USE_OPEN + fflush(hFile); +#endif + fsync(GetFD()); #endif } void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) { -#ifdef _WIN_32 +#ifdef _WIN_ALL + // Workaround for OpenIndiana NAS time bug. If we cannot create a file + // in write only mode, we need to flush the write buffer before calling + // SetFileTime or file time will not be changed. + if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) + FlushFileBuffers(hFile); + bool sm=ftm!=NULL && ftm->IsSet(); bool sc=ftc!=NULL && ftc->IsSet(); bool sa=fta!=NULL && fta->IsSet(); FILETIME fm,fc,fa; if (sm) - ftm->GetWin32(&fm); + ftm->GetWinFT(&fm); if (sc) - ftc->GetWin32(&fc); + ftc->GetWinFT(&fc); if (sa) - fta->GetWin32(&fa); + fta->GetWinFT(&fa); SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); #endif } @@ -531,168 +692,148 @@ void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) void File::SetCloseFileTime(RarTime *ftm,RarTime *fta) { -#if defined(_UNIX) || defined(_EMX) +// Android APP_PLATFORM := android-14 does not support futimens and futimes. +// Newer platforms support futimens, but fail on Android 4.2. +// We have to use utime for Android. +// Also we noticed futimens fail to set timestamps on NTFS partition +// mounted to virtual Linux x86 machine, but utimensat worked correctly. +// So we set timestamps for already closed files in Unix. +#ifdef _UNIX SetCloseFileTimeByName(FileName,ftm,fta); #endif } -void File::SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta) +void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) { -#if defined(_UNIX) || defined(_EMX) +#ifdef _UNIX bool setm=ftm!=NULL && ftm->IsSet(); bool seta=fta!=NULL && fta->IsSet(); if (setm || seta) { - struct utimbuf ut; + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + +#ifdef UNIX_TIME_NS + timespec times[2]; + times[0].tv_sec=seta ? fta->GetUnix() : 0; + times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; + times[1].tv_sec=setm ? ftm->GetUnix() : 0; + times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; + utimensat(AT_FDCWD,NameA,times,0); +#else + utimbuf ut; if (setm) ut.modtime=ftm->GetUnix(); else - ut.modtime=fta->GetUnix(); + ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0. if (seta) ut.actime=fta->GetUnix(); else - ut.actime=ut.modtime; - utime(Name,&ut); + ut.actime=ut.modtime; // Need to set something, cannot left it 0. + utime(NameA,&ut); +#endif } #endif } -void File::GetOpenFileTime(RarTime *ft) +#ifdef _UNIX +void File::StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta) { -#ifdef _WIN_32 - FILETIME FileTime; - GetFileTime(hFile,NULL,NULL,&FileTime); - *ft=FileTime; +#ifdef UNIX_TIME_NS +#if defined(_APPLE) + if (ftm!=NULL) ftm->SetUnixNS(st.st_mtimespec.tv_sec*(uint64)1000000000+st.st_mtimespec.tv_nsec); + if (ftc!=NULL) ftc->SetUnixNS(st.st_ctimespec.tv_sec*(uint64)1000000000+st.st_ctimespec.tv_nsec); + if (fta!=NULL) fta->SetUnixNS(st.st_atimespec.tv_sec*(uint64)1000000000+st.st_atimespec.tv_nsec); +#else + if (ftm!=NULL) ftm->SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); + if (ftc!=NULL) ftc->SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); + if (fta!=NULL) fta->SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); #endif -#if defined(_UNIX) || defined(_EMX) - struct stat st; - fstat(fileno(hFile),&st); - *ft=st.st_mtime; +#else + if (ftm!=NULL) ftm->SetUnix(st.st_mtime); + if (ftc!=NULL) ftc->SetUnix(st.st_ctime); + if (fta!=NULL) fta->SetUnix(st.st_atime); #endif } - - -void File::SetOpenFileStat(RarTime *ftm,RarTime *ftc,RarTime *fta) -{ -#ifdef _WIN_32 - SetOpenFileTime(ftm,ftc,fta); #endif -} -void File::SetCloseFileStat(RarTime *ftm,RarTime *fta,uint FileAttr) +void File::GetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) { -#ifdef _WIN_32 - SetFileAttr(FileName,FileNameW,FileAttr); -#endif -#ifdef _EMX - SetCloseFileTime(ftm,fta); - SetFileAttr(FileName,FileNameW,FileAttr); -#endif -#ifdef _UNIX - SetCloseFileTime(ftm,fta); - chmod(FileName,(mode_t)FileAttr); +#ifdef _WIN_ALL + FILETIME ctime,atime,mtime; + GetFileTime(hFile,&ctime,&atime,&mtime); + if (ftm!=NULL) ftm->SetWinFT(&mtime); + if (ftc!=NULL) ftc->SetWinFT(&ctime); + if (fta!=NULL) fta->SetWinFT(&atime); +#elif defined(_UNIX) + struct stat st; + fstat(GetFD(),&st); + StatToRarTime(st,ftm,ftc,fta); #endif } -Int64 File::FileLength() +int64 File::FileLength() { - SaveFilePos SavePos(*this); + int64 SavePos=Tell(); Seek(0,SEEK_END); - return(Tell()); -} - - -void File::SetHandleType(FILE_HANDLETYPE Type) -{ - HandleType=Type; + int64 Length=Tell(); + Seek(SavePos,SEEK_SET); + return Length; } bool File::IsDevice() { - if (hFile==BAD_HANDLE) - return(false); -#ifdef _WIN_32 + if (hFile==FILE_BAD_HANDLE) + return false; +#ifdef _WIN_ALL uint Type=GetFileType(hFile); - return(Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE); -#else - return(isatty(fileno(hFile))); -#endif -} - - -#ifndef SFX_MODULE -void File::fprintf(const char *fmt,...) -{ - va_list argptr; - va_start(argptr,fmt); - safebuf char Msg[2*NM+1024],OutMsg[2*NM+1024]; - vsprintf(Msg,fmt,argptr); -#ifdef _WIN_32 - for (int Src=0,Dest=0;;Src++) - { - char CurChar=Msg[Src]; - if (CurChar=='\n') - OutMsg[Dest++]='\r'; - OutMsg[Dest++]=CurChar; - if (CurChar==0) - break; - } + return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE; #else - strcpy(OutMsg,Msg); -#endif - Write(OutMsg,strlen(OutMsg)); - va_end(argptr); -} + return isatty(GetFD()); #endif - - -bool File::RemoveCreated() -{ - RemoveCreatedActive++; - bool RetCode=true; - for (int I=0;I<sizeof(CreatedFiles)/sizeof(CreatedFiles[0]);I++) - if (CreatedFiles[I]!=NULL) - { - CreatedFiles[I]->SetExceptions(false); - bool Success; - if (CreatedFiles[I]->NewFile) - Success=CreatedFiles[I]->Delete(); - else - Success=CreatedFiles[I]->Close(); - if (Success) - CreatedFiles[I]=NULL; - else - RetCode=false; - } - RemoveCreatedActive--; - return(RetCode); } #ifndef SFX_MODULE -long File::Copy(File &Dest,Int64 Length) +int64 File::Copy(File &Dest,int64 Length) { - Array<char> Buffer(0x10000); - long CopySize=0; - bool CopyAll=(Length==INT64ERR); + Array<byte> Buffer(File::CopyBufferSize()); + int64 CopySize=0; + bool CopyAll=(Length==INT64NDF); while (CopyAll || Length>0) { Wait(); - int SizeToRead=(!CopyAll && Length<Buffer.Size()) ? int64to32(Length):Buffer.Size(); - int ReadSize=Read(&Buffer[0],SizeToRead); + size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size(); + byte *Buf=&Buffer[0]; + int ReadSize=Read(Buf,SizeToRead); if (ReadSize==0) break; - Dest.Write(&Buffer[0],ReadSize); + size_t WriteSize=ReadSize; +#ifdef _WIN_ALL + // For FAT32 USB flash drives in Windows if first write is 4 KB or more, + // write caching is disabled and "write through" is enabled, resulting + // in bad performance, especially for many small files. It happens when + // we create SFX archive on USB drive, because SFX module is written first. + // So we split the first write to small 1 KB followed by rest of data. + if (CopySize==0 && WriteSize>=4096) + { + const size_t FirstWrite=1024; + Dest.Write(Buf,FirstWrite); + Buf+=FirstWrite; + WriteSize-=FirstWrite; + } +#endif + Dest.Write(Buf,WriteSize); CopySize+=ReadSize; if (!CopyAll) Length-=ReadSize; } - return(CopySize); + return CopySize; } #endif diff --git a/unrar/unrar/file.hpp b/unrar/unrar/file.hpp index e5f768c..5f55de9 100644 --- a/unrar/unrar/file.hpp +++ b/unrar/unrar/file.hpp @@ -1,100 +1,165 @@ #ifndef _RAR_FILE_ #define _RAR_FILE_ -#ifdef _WIN_32 -typedef HANDLE FileHandle; -#define BAD_HANDLE INVALID_HANDLE_VALUE +#define FILE_USE_OPEN + +#ifdef _WIN_ALL + typedef HANDLE FileHandle; + #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE +#elif defined(FILE_USE_OPEN) + typedef off_t FileHandle; + #define FILE_BAD_HANDLE -1 #else -typedef FILE* FileHandle; -#define BAD_HANDLE NULL + typedef FILE* FileHandle; + #define FILE_BAD_HANDLE NULL #endif -class RAROptions; - -enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD,FILE_HANDLEERR}; +enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; -struct FileStat -{ - uint FileAttr; - uint FileTime; - Int64 FileSize; - bool IsDir; +enum FILE_MODE_FLAGS { + // Request read only access to file. Default for Open. + FMF_READ=0, + + // Request both read and write access to file. Default for Create. + FMF_UPDATE=1, + + // Request write only access to file. + FMF_WRITE=2, + + // Open files which are already opened for write by other programs. + FMF_OPENSHARED=4, + + // Open files only if no other program is opened it even in shared mode. + FMF_OPENEXCLUSIVE=8, + + // Provide read access to created file for other programs. + FMF_SHAREREAD=16, + + // Use standard NTFS names without trailing dots and spaces. + FMF_STANDARDNAMES=32, + + // Mode flags are not defined yet. + FMF_UNDEFINED=256 +}; + +enum FILE_READ_ERROR_MODE { + FREM_ASK, // Propose to use the already read part, retry or abort. + FREM_TRUNCATE, // Use the already read part without additional prompt. + FREM_IGNORE // Try to skip unreadable block and read further. }; class File { private: - void AddFileToList(FileHandle hFile); - FileHandle hFile; bool LastWrite; FILE_HANDLETYPE HandleType; + + // If we read the user input in console prompts from stdin, we shall + // process the available line immediately, not waiting for rest of data. + // Otherwise apps piping user responses to multiple Ask() prompts can + // hang if no more data is available yet and pipe isn't closed. + // If we read RAR archive or other file data from stdin, we shall collect + // the entire requested block as long as pipe isn't closed, so we get + // complete archive headers, not split between different reads. + bool LineInput; + bool SkipClose; - bool IgnoreReadErrors; + FILE_READ_ERROR_MODE ReadErrorMode; bool NewFile; bool AllowDelete; bool AllowExceptions; -#ifdef _WIN_32 +#ifdef _WIN_ALL bool NoSequentialRead; + uint CreateMode; #endif + bool PreserveAtime; + bool TruncatedAfterReadError; + + int64 CurFilePos; // Used for forward seeks in stdin files. protected: - bool OpenShared; + bool OpenShared; // Set by 'Archive' class. public: - char FileName[NM]; - wchar FileNameW[NM]; + wchar FileName[NM]; FILE_ERRORTYPE ErrorType; - uint CloseCount; + byte *SeekBuf; // To read instead of seek for stdin files. + static const size_t SeekBufSize=0x10000; public: File(); virtual ~File(); void operator = (File &SrcFile); - bool Open(const char *Name,const wchar *NameW=NULL,bool OpenShared=false,bool Update=false); - void TOpen(const char *Name,const wchar *NameW=NULL); - bool WOpen(const char *Name,const wchar *NameW=NULL); - bool Create(const char *Name,const wchar *NameW=NULL,bool ShareRead=true); - void TCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true); - bool WCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true); - bool Close(); - void Flush(); + + // Several functions below are 'virtual', because they are redefined + // by Archive for QOpen and by MultiFile for split files in WinRAR. + virtual bool Open(const wchar *Name,uint Mode=FMF_READ); + void TOpen(const wchar *Name); + bool WOpen(const wchar *Name); + bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + virtual bool Close(); // 'virtual' for MultiFile class. bool Delete(); - bool Rename(const char *NewName,const wchar *NewNameW=NULL); - void Write(const void *Data,int Size); - int Read(void *Data,int Size); - int DirectRead(void *Data,int Size); - void Seek(Int64 Offset,int Method); - bool RawSeek(Int64 Offset,int Method); - Int64 Tell(); - void Prealloc(Int64 Size); + bool Rename(const wchar *NewName); + bool Write(const void *Data,size_t Size); + virtual int Read(void *Data,size_t Size); + int DirectRead(void *Data,size_t Size); + virtual void Seek(int64 Offset,int Method); + bool RawSeek(int64 Offset,int Method); + virtual int64 Tell(); + void Prealloc(int64 Size); byte GetByte(); void PutByte(byte Byte); bool Truncate(); + void Flush(); void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); - static void SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta); - void SetOpenFileStat(RarTime *ftm,RarTime *ftc,RarTime *fta); - void SetCloseFileStat(RarTime *ftm,RarTime *fta,uint FileAttr); - void GetOpenFileTime(RarTime *ft); - bool IsOpened() {return(hFile!=BAD_HANDLE);}; - Int64 FileLength(); - void SetHandleType(FILE_HANDLETYPE Type); - FILE_HANDLETYPE GetHandleType() {return(HandleType);}; + static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); +#ifdef _UNIX + static void StatToRarTime(struct stat &st,RarTime *ftm,RarTime *ftc,RarTime *fta); +#endif + void GetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); + virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class. + int64 FileLength(); + void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} + void SetLineInputMode(bool Mode) {LineInput=Mode;} + FILE_HANDLETYPE GetHandleType() {return HandleType;} + bool IsSeekable() {return HandleType!=FILE_HANDLESTD;} bool IsDevice(); - void fprintf(const char *fmt,...); static bool RemoveCreated(); - FileHandle GetHandle() {return(hFile);}; - void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;}; - char *GetName() {return(FileName);} - long Copy(File &Dest,Int64 Length=INT64ERR); + FileHandle GetHandle() {return hFile;} + void SetHandle(FileHandle Handle) {Close();hFile=Handle;} + void SetReadErrorMode(FILE_READ_ERROR_MODE Mode) {ReadErrorMode=Mode;} + int64 Copy(File &Dest,int64 Length=INT64NDF); void SetAllowDelete(bool Allow) {AllowDelete=Allow;} void SetExceptions(bool Allow) {AllowExceptions=Allow;} -#ifdef _WIN_32 - void RemoveSequentialFlag() {NoSequentialRead=true;} + void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;} + bool IsTruncatedAfterReadError() {return TruncatedAfterReadError;} +#ifdef _UNIX + int GetFD() + { +#ifdef FILE_USE_OPEN + return hFile; +#else + return fileno(hFile); +#endif + } +#endif + static size_t CopyBufferSize() + { +#ifdef _WIN_ALL + // USB flash performance is poor with 64 KB buffer, 256+ KB resolved it. + // For copying from HDD to same HDD the best performance was with 256 KB + // buffer in XP and with 1 MB buffer in Win10. + return WinNT()==WNT_WXP ? 0x40000:0x100000; +#else + return 0x100000; #endif + } }; #endif diff --git a/unrar/unrar/filefn.cpp b/unrar/unrar/filefn.cpp index 54b0b99..4bcc8bf 100644 --- a/unrar/unrar/filefn.cpp +++ b/unrar/unrar/filefn.cpp @@ -1,146 +1,126 @@ #include "rar.hpp" -MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,uint Attr) +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) { -#ifdef _WIN_32 - int Success; - if (WinNT() && NameW!=NULL && *NameW!=0) - Success=CreateDirectoryW(NameW,NULL); - else - Success=CreateDirectory(Name,NULL); - if (Success) +#ifdef _WIN_ALL + // Windows automatically removes dots and spaces in the end of directory + // name. So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL); + if (RetCode==0 && !FileExist(Name)) { - SetFileAttr(Name,NameW,Attr); - return(MKDIR_SUCCESS); + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + RetCode=CreateDirectory(LongName,NULL); } - int ErrCode=GetLastError(); - if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) - return(MKDIR_BADPATH); - return(MKDIR_ERROR); -#endif -#ifdef _EMX -#ifdef _DJGPP - if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0) -#else - if (__mkdir(Name)==0) -#endif + if (RetCode!=0) // Non-zero return code means success for CreateDirectory. { - SetFileAttr(Name,NameW,Attr); - return(MKDIR_SUCCESS); + if (SetAttr) + SetFileAttr(Name,Attr); + return MKDIR_SUCCESS; } - return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); -#endif -#ifdef _UNIX - int prevmask=umask(0); - int ErrCode=Name==NULL ? -1:mkdir(Name,(mode_t)Attr); - umask(prevmask); + int ErrCode=GetLastError(); + if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) + return MKDIR_BADPATH; + return MKDIR_ERROR; +#elif defined(_UNIX) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + mode_t uattr=SetAttr ? (mode_t)Attr:0777; + int ErrCode=mkdir(NameA,uattr); if (ErrCode==-1) - return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR); - return(MKDIR_SUCCESS); + return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; + return MKDIR_SUCCESS; +#else + return MKDIR_ERROR; #endif } -bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName) +bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent) { -#if defined(_WIN_32) || defined(_EMX) + if (Path==NULL || *Path==0) + return false; + +#if defined(_WIN_ALL) || defined(_EMX) uint DirAttr=0; #else uint DirAttr=0777; #endif -#ifdef UNICODE_SUPPORTED - bool Wide=PathW!=NULL && *PathW!=0 && UnicodeEnabled(); -#else - bool Wide=false; -#endif - bool IgnoreAscii=false; + bool Success=true; - const char *s=Path; - for (int PosW=0;;PosW++) + for (const wchar *s=Path;*s!=0;s++) { - if (s==NULL || s-Path>=NM || *s==0) - IgnoreAscii=true; - if (Wide && (PosW>=NM || PathW[PosW]==0) || !Wide && IgnoreAscii) + wchar DirName[NM]; + if (s-Path>=ASIZE(DirName)) break; - if (Wide && PathW[PosW]==CPATHDIVIDER || !Wide && *s==CPATHDIVIDER) + + // Process all kinds of path separators, so user can enter Unix style + // path in Windows or Windows in Unix. s>Path check avoids attempting + // creating an empty directory for paths starting from path separator. + if (IsPathDiv(*s) && s>Path) { - wchar *DirPtrW=NULL,DirNameW[NM]; - if (Wide) - { - strncpyw(DirNameW,PathW,PosW); - DirNameW[PosW]=0; - DirPtrW=DirNameW; - } - char DirName[NM]; - if (IgnoreAscii) - WideToChar(DirPtrW,DirName); - else - { -#ifndef DBCS_SUPPORTED - if (*s!=CPATHDIVIDER) - for (const char *n=s;*n!=0 && n-Path<NM;n++) - if (*n==CPATHDIVIDER) - { - s=n; - break; - } -#endif - strncpy(DirName,Path,s-Path); - DirName[s-Path]=0; - } - if (MakeDir(DirName,DirPtrW,DirAttr)==MKDIR_SUCCESS) +#ifdef _WIN_ALL + // We must not attempt to create "D:" directory, because first + // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine + // to create "D:" directory. + if (s==Path+2 && Path[1]==':') + continue; +#endif + wcsncpy(DirName,Path,s-Path); + DirName[s-Path]=0; + + Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; + if (Success && !Silent) { -#ifndef GUI mprintf(St(MCreatDir),DirName); - mprintf(" %s",St(MOk)); -#endif + mprintf(L" %s",St(MOk)); } - else - Success=false; } - if (!IgnoreAscii) - s=charnext(s); } if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path))) - if (MakeDir(Path,PathW,DirAttr)!=MKDIR_SUCCESS) - Success=false; - return(Success); + Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; + return Success; } -void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta) +void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) { -#ifdef _WIN_32 - if (!WinNT()) - return; - +#if defined(_WIN_ALL) bool sm=ftm!=NULL && ftm->IsSet(); bool sc=ftc!=NULL && ftc->IsSet(); bool sa=fta!=NULL && fta->IsSet(); - unsigned int DirAttr=GetFileAttr(Name,NameW); - bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FA_RDONLY)!=0); + uint DirAttr=GetFileAttr(Name); + bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0); if (ResetAttr) - SetFileAttr(Name,NameW,0); + SetFileAttr(Name,0); - wchar DirNameW[NM]; - GetWideName(Name,NameW,DirNameW); - HANDLE hFile=CreateFileW(DirNameW,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); if (hFile==INVALID_HANDLE_VALUE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + } + + if (hFile==INVALID_HANDLE_VALUE) return; FILETIME fm,fc,fa; if (sm) - ftm->GetWin32(&fm); + ftm->GetWinFT(&fm); if (sc) - ftc->GetWin32(&fc); + ftc->GetWinFT(&fc); if (sa) - fta->GetWin32(&fa); + fta->GetWinFT(&fa); SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); CloseHandle(hFile); if (ResetAttr) - SetFileAttr(Name,NameW,DirAttr); + SetFileAttr(Name,DirAttr); #endif #if defined(_UNIX) || defined(_EMX) File::SetCloseFileTimeByName(Name,ftm,fta); @@ -148,145 +128,100 @@ void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,Ra } -bool IsRemovable(const char *Name) +bool IsRemovable(const wchar *Name) { -#ifdef _WIN_32 - char Root[NM]; - GetPathRoot(Name,Root); - int Type=GetDriveType(*Root ? Root:NULL); - return(Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM); -#elif defined(_EMX) - char Drive=etoupper(Name[0]); - return((Drive=='A' || Drive=='B') && Name[1]==':'); +#if defined(_WIN_ALL) + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + int Type=GetDriveType(*Root!=0 ? Root:NULL); + return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; #else - return(false); + return false; #endif } #ifndef SFX_MODULE -Int64 GetFreeDisk(const char *Name) +int64 GetFreeDisk(const wchar *Name) { -#ifdef _WIN_32 - char Root[NM]; - GetPathRoot(Name,Root); - - typedef BOOL (WINAPI *GETDISKFREESPACEEX)( - LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER - ); - static GETDISKFREESPACEEX pGetDiskFreeSpaceEx=NULL; - - if (pGetDiskFreeSpaceEx==NULL) - { - HMODULE hKernel=GetModuleHandle("kernel32.dll"); - if (hKernel!=NULL) - pGetDiskFreeSpaceEx=(GETDISKFREESPACEEX)GetProcAddress(hKernel,"GetDiskFreeSpaceExA"); - } - if (pGetDiskFreeSpaceEx!=NULL) - { - GetFilePath(Name,Root,ASIZE(Root)); - ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; - uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; - if (pGetDiskFreeSpaceEx(*Root ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && - uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) - return(int32to64(uiUserFree.u.HighPart,uiUserFree.u.LowPart)); - } - - DWORD SectorsPerCluster,BytesPerSector,FreeClusters,TotalClusters; - if (!GetDiskFreeSpace(*Root ? Root:NULL,&SectorsPerCluster,&BytesPerSector,&FreeClusters,&TotalClusters)) - return(1457664); - Int64 FreeSize=SectorsPerCluster*BytesPerSector; - FreeSize=FreeSize*FreeClusters; - return(FreeSize); -#elif defined(_BEOS) - char Root[NM]; +#ifdef _WIN_ALL + wchar Root[NM]; GetFilePath(Name,Root,ASIZE(Root)); - dev_t Dev=dev_for_path(*Root ? Root:"."); - if (Dev<0) - return(1457664); - fs_info Info; - if (fs_stat_dev(Dev,&Info)!=0) - return(1457664); - Int64 FreeSize=Info.block_size; - FreeSize=FreeSize*Info.free_blocks; - return(FreeSize); + + ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; + uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; + if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && + uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) + return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); + return 0; #elif defined(_UNIX) - return(1457664); -#elif defined(_EMX) - int Drive=(!isalpha(Name[0]) || Name[1]!=':') ? 0:etoupper(Name[0])-'A'+1; -#ifndef _DJGPP - if (_osmode == OS2_MODE) - { - FSALLOCATE fsa; - if (DosQueryFSInfo(Drive,1,&fsa,sizeof(fsa))!=0) - return(1457664); - Int64 FreeSize=fsa.cSectorUnit*fsa.cbSector; - FreeSize=FreeSize*fsa.cUnitAvail; - return(FreeSize); - } - else -#endif - { - union REGS regs,outregs; - memset(®s,0,sizeof(regs)); - regs.h.ah=0x36; - regs.h.dl=Drive; -#ifdef _DJGPP - int86 (0x21,®s,&outregs); + wchar Root[NM]; + GetFilePath(Name,Root,ASIZE(Root)); + char RootA[NM]; + WideToChar(Root,RootA,ASIZE(RootA)); + struct statvfs sfs; + if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0) + return 0; + int64 FreeSize=sfs.f_bsize; + FreeSize=FreeSize*sfs.f_bavail; + return FreeSize; #else - _int86 (0x21,®s,&outregs); + return 0; #endif - if (outregs.x.ax==0xffff) - return(1457664); - Int64 FreeSize=outregs.x.ax*outregs.x.cx; - FreeSize=FreeSize*outregs.x.bx; - return(FreeSize); - } -#else - #define DISABLEAUTODETECT - return(1457664); +} #endif + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +// Return 'true' for FAT and FAT32, so we can adjust the maximum supported +// file size to 4 GB for these file systems. +bool IsFAT(const wchar *Name) +{ + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + wchar FileSystem[MAX_PATH+1]; + if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) + return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; + return false; } #endif -bool FileExist(const char *Name,const wchar *NameW) +bool FileExist(const wchar *Name) { -#ifdef _WIN_32 - if (WinNT() && NameW!=NULL && *NameW!=0) - return(GetFileAttributesW(NameW)!=0xffffffff); - else - return(GetFileAttributes(Name)!=0xffffffff); +#ifdef _WIN_ALL + return GetFileAttr(Name)!=0xffffffff; #elif defined(ENABLE_ACCESS) - return(access(Name,0)==0); + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return access(NameA,0)==0; #else - struct FindData FD; - return(FindFile::FastFind(Name,NameW,&FD)); + FindData FD; + return FindFile::FastFind(Name,&FD); #endif } + - -bool WildFileExist(const char *Name,const wchar *NameW) +bool WildFileExist(const wchar *Name) { - if (IsWildcard(Name,NameW)) + if (IsWildcard(Name)) { FindFile Find; Find.SetMask(Name); - Find.SetMaskW(NameW); - struct FindData fd; - return(Find.Next(&fd)); + FindData fd; + return Find.Next(&fd); } - return(FileExist(Name,NameW)); + return FileExist(Name); } bool IsDir(uint Attr) { -#if defined (_WIN_32) || defined(_EMX) - return(Attr!=0xffffffff && (Attr & 0x10)!=0); +#ifdef _WIN_ALL + return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0; #endif #if defined(_UNIX) - return((Attr & 0xF000)==0x4000); + return (Attr & 0xF000)==0x4000; #endif } @@ -294,28 +229,20 @@ bool IsDir(uint Attr) bool IsUnreadable(uint Attr) { #if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) - return(S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr)); -#endif - return(false); -} - - -bool IsLabel(uint Attr) -{ -#if defined (_WIN_32) || defined(_EMX) - return((Attr & 8)!=0); -#else - return(false); + return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); #endif + return false; } bool IsLink(uint Attr) { #ifdef _UNIX - return((Attr & 0xF000)==0xA000); + return (Attr & 0xF000)==0xA000; +#elif defined(_WIN_ALL) + return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0; #else - return(false); + return false; #endif } @@ -326,249 +253,283 @@ bool IsLink(uint Attr) bool IsDeleteAllowed(uint FileAttr) { -#if defined(_WIN_32) || defined(_EMX) - return((FileAttr & (FA_RDONLY|FA_SYSTEM|FA_HIDDEN))==0); +#ifdef _WIN_ALL + return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0; #else - return((FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR)); + return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR); #endif } -void PrepareToDelete(const char *Name,const wchar *NameW) +void PrepareToDelete(const wchar *Name) { -#if defined(_WIN_32) || defined(_EMX) - SetFileAttr(Name,NameW,0); +#if defined(_WIN_ALL) || defined(_EMX) + SetFileAttr(Name,0); #endif #ifdef _UNIX - chmod(Name,S_IRUSR|S_IWUSR|S_IXUSR); + if (Name!=NULL) + { + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR); + } #endif } -uint GetFileAttr(const char *Name,const wchar *NameW) +uint GetFileAttr(const wchar *Name) { -#ifdef _WIN_32 - if (WinNT() && NameW!=NULL && *NameW!=0) - return(GetFileAttributesW(NameW)); - else - return(GetFileAttributes(Name)); -#elif defined(_DJGPP) - return(_chmod(Name,0)); +#ifdef _WIN_ALL + DWORD Attr=GetFileAttributes(Name); + if (Attr==0xffffffff) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Attr=GetFileAttributes(LongName); + } + return Attr; #else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); struct stat st; - if (stat(Name,&st)!=0) - return(0); -#ifdef _EMX - return(st.st_attr); -#else - return(st.st_mode); -#endif + if (stat(NameA,&st)!=0) + return 0; + return st.st_mode; #endif } -bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr) +bool SetFileAttr(const wchar *Name,uint Attr) { - bool Success; -#ifdef _WIN_32 - if (WinNT() && NameW!=NULL && *NameW!=0) - Success=SetFileAttributesW(NameW,Attr)!=0; - else - Success=SetFileAttributes(Name,Attr)!=0; -#elif defined(_DJGPP) - Success=_chmod(Name,1,Attr)!=-1; -#elif defined(_EMX) - Success=__chmod(Name,1,Attr)!=-1; +#ifdef _WIN_ALL + bool Success=SetFileAttributes(Name,Attr)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=SetFileAttributes(LongName,Attr)!=0; + } + return Success; #elif defined(_UNIX) - Success=chmod(Name,(mode_t)Attr)==0; + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return chmod(NameA,(mode_t)Attr)==0; #else - Success=false; + return false; #endif - return(Success); } -void ConvertNameToFull(const char *Src,char *Dest) +wchar *MkTemp(wchar *Name,size_t MaxSize) { -#ifdef _WIN_32 -#ifndef _WIN_CE - char FullName[NM],*NamePtr; - if (GetFullPathName(Src,sizeof(FullName),FullName,&NamePtr)) - strcpy(Dest,FullName); - else -#endif - if (Src!=Dest) - strcpy(Dest,Src); -#else - char FullName[NM]; - if (IsPathDiv(*Src) || IsDiskLetter(Src)) - strcpy(FullName,Src); - else - { - getcwd(FullName,sizeof(FullName)); - AddEndSlash(FullName); - strcat(FullName,Src); - } - strcpy(Dest,FullName); -#endif -} + size_t Length=wcslen(Name); + RarTime CurTime; + CurTime.SetCurrentTime(); -#ifndef SFX_MODULE -void ConvertNameToFull(const wchar *Src,wchar *Dest) -{ - if (Src==NULL || *Src==0) - { - *Dest=0; - return; - } -#ifdef _WIN_32 -#ifndef _WIN_CE - if (WinNT()) -#endif - { -#ifndef _WIN_CE - wchar FullName[NM],*NamePtr; - if (GetFullPathNameW(Src,sizeof(FullName)/sizeof(FullName[0]),FullName,&NamePtr)) - strcpyw(Dest,FullName); - else -#endif - if (Src!=Dest) - strcpyw(Dest,Src); - } -#ifndef _WIN_CE - else - { - char AnsiName[NM]; - WideToChar(Src,AnsiName); - ConvertNameToFull(AnsiName,AnsiName); - CharToWide(AnsiName,Dest); - } -#endif -#else - char AnsiName[NM]; - WideToChar(Src,AnsiName); - ConvertNameToFull(AnsiName,AnsiName); - CharToWide(AnsiName,Dest); -#endif -} -#endif + // We cannot use CurTime.GetWin() as is, because its lowest bits can + // have low informational value, like being a zero or few fixed numbers. + uint Random=(uint)(CurTime.GetWin()/100000); + // Using PID we guarantee that different RAR copies use different temp names + // even if started in exactly the same time. + uint PID=0; +#ifdef _WIN_ALL + PID=(uint)GetCurrentProcessId(); +#elif defined(_UNIX) + PID=(uint)getpid(); +#endif -#ifndef SFX_MODULE -char *MkTemp(char *Name) -{ - size_t Length=strlen(Name); - if (Length<=6) - return(NULL); - int Random=clock(); - for (int Attempt=0;;Attempt++) + for (uint Attempt=0;;Attempt++) { - sprintf(Name+Length-6,"%06u",Random+Attempt); - Name[Length-4]='.'; + uint Ext=Random%50000+Attempt; + wchar RndText[50]; + swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext); + if (Length+wcslen(RndText)>=MaxSize || Attempt==1000) + return NULL; + wcsncpyz(Name+Length,RndText,MaxSize-Length); if (!FileExist(Name)) break; - if (Attempt==1000) - return(NULL); } - return(Name); + return Name; } -#endif +#if !defined(SFX_MODULE) +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) +{ + int64 SavePos=SrcFile->Tell(); +#ifndef SILENT + int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; +#endif + + if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0) + uiMsg(UIEVENT_FILESUMSTART); + if ((Flags & CALCFSUM_CURPOS)==0) + SrcFile->Seek(0,SEEK_SET); -#ifndef SFX_MODULE -uint CalcFileCRC(File *SrcFile,Int64 Size,CALCCRC_SHOWMODE ShowMode) -{ - SaveFilePos SavePos(*SrcFile); - const int BufSize=0x10000; + const size_t BufSize=0x100000; Array<byte> Data(BufSize); - Int64 BlockCount=0; - uint DataCRC=0xffffffff; - int ReadSize; -#if !defined(SILENT) && !defined(_WIN_CE) - Int64 FileLength=SrcFile->FileLength(); - if (ShowMode!=CALCCRC_SHOWNONE) - { - mprintf(St(MCalcCRC)); - mprintf(" "); - } -#endif + DataHash HashCRC,HashBlake2; + HashCRC.Init(HASH_CRC32,Threads); + HashBlake2.Init(HASH_BLAKE2,Threads); - SrcFile->Seek(0,SEEK_SET); - while ((ReadSize=SrcFile->Read(&Data[0],int64to32(Size==INT64ERR ? Int64(BufSize):Min(Int64(BufSize),Size))))!=0) + int64 BlockCount=0; + int64 TotalRead=0; + while (true) { - ++BlockCount; - if ((BlockCount & 15)==0) + size_t SizeToRead; + if (Size==INT64NDF) // If we process the entire file. + SizeToRead=BufSize; // Then always attempt to read the entire buffer. + else + SizeToRead=(size_t)Min((int64)BufSize,Size); + int ReadSize=SrcFile->Read(&Data[0],SizeToRead); + if (ReadSize==0) + break; + TotalRead+=ReadSize; + + if ((++BlockCount & 0xf)==0) { -#if !defined(SILENT) && !defined(_WIN_CE) - if (ShowMode==CALCCRC_SHOWALL) - mprintf("\b\b\b\b%3d%%",ToPercent(BlockCount*Int64(BufSize),FileLength)); +#ifndef SILENT + if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) + { + // Update only the current file progress in WinRAR, set the total to 0 + // to keep it as is. It looks better for WinRAR. + uiExtractProgress(TotalRead,FileLength,0,0); + } + else + { + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); + } #endif Wait(); } - DataCRC=CRC(DataCRC,&Data[0],ReadSize); - if (Size!=INT64ERR) - Size-=ReadSize; - } -#if !defined(SILENT) && !defined(_WIN_CE) - if (ShowMode==CALCCRC_SHOWALL) - mprintf("\b\b\b\b "); -#endif - return(DataCRC^0xffffffff); -} -#endif + if (CRC32!=NULL) + HashCRC.Update(&Data[0],ReadSize); + if (Blake2!=NULL) + HashBlake2.Update(&Data[0],ReadSize); -bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW) -{ - return(rename(SrcName,DestName)==0); -} + if (Size!=INT64NDF) + Size-=ReadSize; + } + SrcFile->Seek(SavePos,SEEK_SET); + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMEND); -bool DelFile(const char *Name) -{ - return(DelFile(Name,NULL)); + if (CRC32!=NULL) + *CRC32=HashCRC.GetCRC32(); + if (Blake2!=NULL) + { + HashValue Result; + HashBlake2.Result(&Result); + memcpy(Blake2,Result.Digest,sizeof(Result.Digest)); + } } +#endif -bool DelFile(const char *Name,const wchar *NameW) +bool RenameFile(const wchar *SrcName,const wchar *DestName) { - return(remove(Name)==0); +#ifdef _WIN_ALL + bool Success=MoveFile(SrcName,DestName)!=0; + if (!Success) + { + wchar LongName1[NM],LongName2[NM]; + if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) && + GetWinLongPath(DestName,LongName2,ASIZE(LongName2))) + Success=MoveFile(LongName1,LongName2)!=0; + } + return Success; +#else + char SrcNameA[NM],DestNameA[NM]; + WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA)); + WideToChar(DestName,DestNameA,ASIZE(DestNameA)); + bool Success=rename(SrcNameA,DestNameA)==0; + return Success; +#endif } -bool DelDir(const char *Name) +bool DelFile(const wchar *Name) { - return(DelDir(Name,NULL)); +#ifdef _WIN_ALL + bool Success=DeleteFile(Name)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=DeleteFile(LongName)!=0; + } + return Success; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + bool Success=remove(NameA)==0; + return Success; +#endif } -bool DelDir(const char *Name,const wchar *NameW) +bool DelDir(const wchar *Name) { - return(rmdir(Name)==0); +#ifdef _WIN_ALL + bool Success=RemoveDirectory(Name)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=RemoveDirectory(LongName)!=0; + } + return Success; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + bool Success=rmdir(NameA)==0; + return Success; +#endif } -#if defined(_WIN_32) && !defined(_WIN_CE) -bool SetFileCompression(char *Name,wchar *NameW,bool State) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State) { - wchar FileNameW[NM]; - GetWideName(Name,NameW,FileNameW); - HANDLE hFile=CreateFileW(FileNameW,FILE_READ_DATA|FILE_WRITE_DATA, + HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); if (hFile==INVALID_HANDLE_VALUE) - return(false); + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); + } + if (hFile==INVALID_HANDLE_VALUE) + return false; SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; DWORD Result; int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, sizeof(NewState),NULL,0,&Result,NULL); CloseHandle(hFile); - return(RetCode!=0); + return RetCode!=0; +} + + +void ResetFileCache(const wchar *Name) +{ + // To reset file cache in Windows it is enough to open it with + // FILE_FLAG_NO_BUFFERING and then close it. + HANDLE hSrc=CreateFile(Name,GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL); + if (hSrc!=INVALID_HANDLE_VALUE) + CloseHandle(hSrc); } #endif @@ -577,3 +538,69 @@ bool SetFileCompression(char *Name,wchar *NameW,bool State) + + + + + + +// Delete symbolic links in file path, if any, and replace them by directories. +// Prevents extracting files outside of destination folder with symlink chains. +bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked) +{ + // Unlike Unix, Windows doesn't expand lnk1 in symlink targets like + // "lnk1/../dir", but converts the path to "dir". In Unix we need to call + // this function to prevent placing unpacked files outside of destination + // folder if previously we unpacked "dir/lnk1" -> "..", + // "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt". + // We may still need this function to prevent abusing symlink chains + // in link source path if we remove detection of such chains + // in IsRelativeSymlinkSafe. This function seems to make other symlink + // related safety checks redundant, but for now we prefer to keep them too. + // + // 2022.12.01: the performance impact is minimized after adding the check + // against the previous path and enabling this verification only after + // extracting a symlink with ".." in target. So we enabled it for Windows + // as well for extra safety. +//#ifdef _UNIX + wchar Path[NM]; + if (wcslen(SrcName)>=ASIZE(Path)) + return false; // It should not be that long, skip. + wcsncpyz(Path,SrcName,ASIZE(Path)); + + size_t SkipLength=wcslen(SkipPart); + + if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0) + SkipLength=0; // Parameter validation, not really needed now. + + // Do not check parts already checked in previous path to improve performance. + for (uint I=0;Path[I]!=0 && I<LastChecked.size() && Path[I]==LastChecked[I];I++) + if (IsPathDiv(Path[I]) && I>SkipLength) + SkipLength=I; + + wchar *Name=Path; + if (SkipLength>0) + { + // Avoid converting symlinks in destination path part specified by user. + Name+=SkipLength; + while (IsPathDiv(*Name)) + Name++; + } + + for (wchar *s=Path+wcslen(Path)-1;s>Name;s--) + if (IsPathDiv(*s)) + { + *s=0; + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && FD.IsLink) +#ifdef _WIN_ALL + if (!DelDir(Path)) +#else + if (!DelFile(Path)) +#endif + return false; // Couldn't delete the symlink to replace it with directory. + } + LastChecked=SrcName; +//#endif + return true; +} diff --git a/unrar/unrar/filefn.hpp b/unrar/unrar/filefn.hpp index cd8e3fc..d73d55e 100644 --- a/unrar/unrar/filefn.hpp +++ b/unrar/unrar/filefn.hpp @@ -3,42 +3,49 @@ enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; -MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,uint Attr); -bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName); -void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta); -bool IsRemovable(const char *Name); -Int64 GetFreeDisk(const char *Name); -bool FileExist(const char *Name,const wchar *NameW=NULL); -bool WildFileExist(const char *Name,const wchar *NameW=NULL); +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr); +bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent); +void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta); +bool IsRemovable(const wchar *Name); + +#ifndef SFX_MODULE +int64 GetFreeDisk(const wchar *Name); +#endif + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +bool IsFAT(const wchar *Root); +#endif + +bool FileExist(const wchar *Name); +bool WildFileExist(const wchar *Name); bool IsDir(uint Attr); bool IsUnreadable(uint Attr); -bool IsLabel(uint Attr); bool IsLink(uint Attr); -void SetSFXMode(const char *FileName); -void EraseDiskContents(const char *FileName); +void SetSFXMode(const wchar *FileName); +void EraseDiskContents(const wchar *FileName); bool IsDeleteAllowed(uint FileAttr); -void PrepareToDelete(const char *Name,const wchar *NameW=NULL); -uint GetFileAttr(const char *Name,const wchar *NameW=NULL); -bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr); -void ConvertNameToFull(const char *Src,char *Dest); -void ConvertNameToFull(const wchar *Src,wchar *Dest); -char* MkTemp(char *Name); +void PrepareToDelete(const wchar *Name); +uint GetFileAttr(const wchar *Name); +bool SetFileAttr(const wchar *Name,uint Attr); +wchar* MkTemp(wchar *Name,size_t MaxSize); +enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; -enum CALCCRC_SHOWMODE {CALCCRC_SHOWNONE,CALCCRC_SHOWTEXT,CALCCRC_SHOWALL}; -uint CalcFileCRC(File *SrcFile,Int64 Size=INT64ERR,CALCCRC_SHOWMODE ShowMode=CALCCRC_SHOWNONE); +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0); -bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW); -bool DelFile(const char *Name); -bool DelFile(const char *Name,const wchar *NameW); -bool DelDir(const char *Name); -bool DelDir(const char *Name,const wchar *NameW); +bool RenameFile(const wchar *SrcName,const wchar *DestName); +bool DelFile(const wchar *Name); +bool DelDir(const wchar *Name); -#if defined(_WIN_32) && !defined(_WIN_CE) -bool SetFileCompression(char *Name,wchar *NameW,bool State); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State); +void ResetFileCache(const wchar *Name); #endif + +bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked); + #endif diff --git a/unrar/unrar/filestr.cpp b/unrar/unrar/filestr.cpp index c7217af..a5d29d7 100644 --- a/unrar/unrar/filestr.cpp +++ b/unrar/unrar/filestr.cpp @@ -1,182 +1,166 @@ #include "rar.hpp" -static bool IsUnicode(byte *Data,int Size); - -bool ReadTextFile(char *Name,StringList *List,bool Config,bool AbortOnError, - RAR_CHARSET SrcCharset,bool Unquote,bool SkipComments, - bool ExpandEnvStr) +bool ReadTextFile( + const wchar *Name, + StringList *List, + bool Config, + bool AbortOnError, + RAR_CHARSET SrcCharset, + bool Unquote, + bool SkipComments, + bool ExpandEnvStr) { - char FileName[NM]; - if (Config) - GetConfigName(Name,FileName,true); - else - strcpy(FileName,Name); + wchar FileName[NM]; + *FileName=0; + + if (Name!=NULL) + if (Config) + GetConfigName(Name,FileName,ASIZE(FileName),true,false); + else + wcsncpyz(FileName,Name,ASIZE(FileName)); File SrcFile; - if (*FileName) + if (*FileName!=0) { - bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName); + bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0); if (!OpenCode) { if (AbortOnError) - ErrHandler.Exit(OPEN_ERROR); - return(false); + ErrHandler.Exit(RARX_OPEN); + return false; } } else SrcFile.SetHandleType(FILE_HANDLESTD); - unsigned int DataSize=0,ReadSize; - const int ReadBlock=1024; - Array<char> Data(ReadBlock+5); + uint DataSize=0,ReadSize; + const int ReadBlock=4096; + + Array<byte> Data(ReadBlock); while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) { DataSize+=ReadSize; - Data.Add(ReadSize); + Data.Add(ReadSize); // Always have ReadBlock available for next data. } + // Set to really read size, so we can zero terminate it correctly. + Data.Alloc(DataSize); - memset(&Data[DataSize],0,5); + int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0; + int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0; + bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf; - if (SrcCharset==RCH_UNICODE || - SrcCharset==RCH_DEFAULT && IsUnicode((byte *)&Data[0],DataSize)) - { - // Unicode in native system format, can be more than 2 bytes per character - Array<wchar> DataW(Data.Size()/2+1); - for (int I=2;I<Data.Size()-1;I+=2) - DataW[(I-2)/2]=(wchar)Data[I]+(wchar)Data[I+1]*256; + if (SrcCharset==RCH_DEFAULT) + SrcCharset=DetectTextEncoding(&Data[0],DataSize); - wchar *CurStr=&DataW[0]; - Array<char> AnsiName; + Array<wchar> DataW; - while (*CurStr!=0) - { - wchar *NextStr=CurStr,*CmtPtr=NULL; - while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) - { - if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') - { - *NextStr=0; - CmtPtr=NextStr; - } - NextStr++; - } - *NextStr=0; - for (wchar *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) - { - if (*SpacePtr!=' ' && *SpacePtr!='\t') - break; - *SpacePtr=0; - } - if (*CurStr) - { - int Length=strlenw(CurStr); - int AddSize=4*(Length-AnsiName.Size()+1); - if (AddSize>0) - AnsiName.Add(AddSize); - if (Unquote && *CurStr=='\"' && CurStr[Length-1]=='\"') - { - CurStr[Length-1]=0; - CurStr++; - } - WideToChar(CurStr,&AnsiName[0],AnsiName.Size()); - - bool Expanded=false; -#if defined(_WIN_32) && !defined(_WIN_CE) - if (ExpandEnvStr && *CurStr=='%') - { - // expanding environment variables in Windows version - - char ExpName[NM]; - wchar ExpNameW[NM]; - *ExpNameW=0; - int ret,retw=1; - ret=ExpandEnvironmentStrings(&AnsiName[0],ExpName,ASIZE(ExpName)); - if (ret!=0 && WinNT()) - retw=ExpandEnvironmentStringsW(CurStr,ExpNameW,ASIZE(ExpNameW)); - Expanded=ret!=0 && ret<ASIZE(ExpName) && - retw!=0 && retw<ASIZE(ExpNameW); - if (Expanded) - List->AddString(ExpName,ExpNameW); - } + if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI) + { + Data.Push(0); // Zero terminate. +#if defined(_WIN_ALL) + if (SrcCharset==RCH_OEM) + OemToCharA((char *)&Data[0],(char *)&Data[0]); #endif - if (!Expanded) - List->AddString(&AnsiName[0],CurStr); - } - CurStr=NextStr+1; - while (*CurStr=='\r' || *CurStr=='\n') - CurStr++; + DataW.Alloc(Data.Size()); + CharToWide((char *)&Data[0],&DataW[0],DataW.Size()); + } + + if (SrcCharset==RCH_UNICODE) + { + size_t Start=2; // Skip byte order mark. + if (!LittleEndian && !BigEndian) // No byte order mask. + { + Start=0; + LittleEndian=1; } + + DataW.Alloc(Data.Size()/2+1); + size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16. + for (size_t I=Start;I<End;I+=2) + DataW[(I-Start)/2]=Data[I+BigEndian]+Data[I+LittleEndian]*256; + DataW[(End-Start)/2]=0; } - else + + if (SrcCharset==RCH_UTF8) + { + Data.Push(0); // Zero terminate data. + DataW.Alloc(Data.Size()); + UtfToWide((const char *)(Data+(Utf8 ? 3:0)),&DataW[0],DataW.Size()); + } + + wchar *CurStr=&DataW[0]; + + while (*CurStr!=0) { - char *CurStr=&Data[0]; - while (*CurStr!=0) + wchar *NextStr=CurStr,*CmtPtr=NULL; + while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) { - char *NextStr=CurStr,*CmtPtr=NULL; - while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0) + if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') { - if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/') - { - *NextStr=0; - CmtPtr=NextStr; - } - NextStr++; + *NextStr=0; + CmtPtr=NextStr; } - *NextStr=0; - for (char *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) + NextStr++; + } + bool Done=*NextStr==0; + + *NextStr=0; + for (wchar *SpacePtr=(CmtPtr!=NULL ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--) + { + if (*SpacePtr!=' ' && *SpacePtr!='\t') + break; + *SpacePtr=0; + } + + if (Unquote && *CurStr=='\"') + { + size_t Length=wcslen(CurStr); + if (CurStr[Length-1]=='\"') { - if (*SpacePtr!=' ' && *SpacePtr!='\t') - break; - *SpacePtr=0; + CurStr[Length-1]=0; + CurStr++; } - if (*CurStr) - { - if (Unquote && *CurStr=='\"') - { - size_t Length=strlen(CurStr); - if (CurStr[Length-1]=='\"') - { - CurStr[Length-1]=0; - CurStr++; - } - } -#if defined(_WIN_32) - if (SrcCharset==RCH_OEM) - OemToChar(CurStr,CurStr); -#endif + } - bool Expanded=false; -#if defined(_WIN_32) && !defined(_WIN_CE) - if (ExpandEnvStr && *CurStr=='%') - { - // expanding environment variables in Windows version - - char ExpName[NM]; - int ret=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName)); - Expanded=ret!=0 && ret<ASIZE(ExpName); - if (Expanded) - List->AddString(ExpName); - } -#endif - if (!Expanded) - List->AddString(CurStr); - } - CurStr=NextStr+1; - while (*CurStr=='\r' || *CurStr=='\n') - CurStr++; + bool Expanded=false; +#if defined(_WIN_ALL) + if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows. + { + wchar ExpName[NM]; + *ExpName=0; + DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName)); + Expanded=Result!=0 && Result<ASIZE(ExpName); + if (Expanded && *ExpName!=0) + List->AddString(ExpName); } +#endif + if (!Expanded && *CurStr!=0) + List->AddString(CurStr); + + if (Done) + break; + CurStr=NextStr+1; + while (*CurStr=='\r' || *CurStr=='\n') + CurStr++; } - return(true); + return true; } -bool IsUnicode(byte *Data,int Size) +RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize) { - if (Size<4 || Data[0]!=0xff || Data[1]!=0xfe) - return(false); - for (int I=2;I<Size;I++) - if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n') - return(true); - return(false); + if (DataSize>3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf && + IsTextUtf8(Data+3,DataSize-3)) + return RCH_UTF8; + + bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254; + bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255; + + if (LittleEndian || BigEndian) + for (size_t I=LittleEndian ? 3 : 2;I<DataSize;I+=2) + if (Data[I]<32 && Data[I]!='\r' && Data[I]!='\n') + return RCH_UNICODE; // High byte in UTF-16 char is found. + + return RCH_DEFAULT; } diff --git a/unrar/unrar/filestr.hpp b/unrar/unrar/filestr.hpp index 927711b..febd0a2 100644 --- a/unrar/unrar/filestr.hpp +++ b/unrar/unrar/filestr.hpp @@ -1,9 +1,17 @@ #ifndef _RAR_FILESTR_ #define _RAR_FILESTR_ -bool ReadTextFile(char *Name,StringList *List,bool Config, - bool AbortOnError=false,RAR_CHARSET SrcCharset=RCH_DEFAULT, - bool Unquote=false,bool SkipComments=false, - bool ExpandEnvStr=false); +bool ReadTextFile( + const wchar *Name, + StringList *List, + bool Config, + bool AbortOnError=false, + RAR_CHARSET SrcCharset=RCH_DEFAULT, + bool Unquote=false, + bool SkipComments=false, + bool ExpandEnvStr=false +); + +RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize); #endif diff --git a/unrar/unrar/find.cpp b/unrar/unrar/find.cpp index 50a0af6..c9f2c57 100644 --- a/unrar/unrar/find.cpp +++ b/unrar/unrar/find.cpp @@ -3,9 +3,8 @@ FindFile::FindFile() { *FindMask=0; - *FindMaskW=0; FirstCall=true; -#ifdef _WIN_32 +#ifdef _WIN_ALL hFind=INVALID_HANDLE_VALUE; #else dirp=NULL; @@ -15,7 +14,7 @@ FindFile::FindFile() FindFile::~FindFile() { -#ifdef _WIN_32 +#ifdef _WIN_ALL if (hFind!=INVALID_HANDLE_VALUE) FindClose(hFind); #else @@ -25,275 +24,187 @@ FindFile::~FindFile() } -void FindFile::SetMask(const char *FindMask) +void FindFile::SetMask(const wchar *Mask) { - strcpy(FindFile::FindMask,FindMask); - if (*FindMaskW==0) - CharToWide(FindMask,FindMaskW); + wcsncpyz(FindMask,Mask,ASIZE(FindMask)); FirstCall=true; } -void FindFile::SetMaskW(const wchar *FindMaskW) -{ - if (FindMaskW==NULL) - return; - strcpyw(FindFile::FindMaskW,FindMaskW); - if (*FindMask==0) - WideToChar(FindMaskW,FindMask); - FirstCall=true; -} - - -bool FindFile::Next(struct FindData *fd,bool GetSymLink) +bool FindFile::Next(FindData *fd,bool GetSymLink) { fd->Error=false; if (*FindMask==0) - return(false); -#ifdef _WIN_32 + return false; +#ifdef _WIN_ALL if (FirstCall) { - if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd))==INVALID_HANDLE_VALUE) - return(false); + if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE) + return false; } else - if (Win32Find(hFind,FindMask,FindMaskW,fd)==INVALID_HANDLE_VALUE) - return(false); + if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE) + return false; #else if (FirstCall) { - char DirName[NM]; - strcpy(DirName,FindMask); + wchar DirName[NM]; + wcsncpyz(DirName,FindMask,ASIZE(DirName)); RemoveNameFromPath(DirName); if (*DirName==0) - strcpy(DirName,"."); -/* - else - { - int Length=strlen(DirName); - if (Length>1 && DirName[Length-1]==CPATHDIVIDER && (Length!=3 || !IsDriveDiv(DirName[1]))) - DirName[Length-1]=0; - } -*/ - if ((dirp=opendir(DirName))==NULL) + wcsncpyz(DirName,L".",ASIZE(DirName)); + char DirNameA[NM]; + WideToChar(DirName,DirNameA,ASIZE(DirNameA)); + if ((dirp=opendir(DirNameA))==NULL) { fd->Error=(errno!=ENOENT); - return(false); + return false; } } while (1) { + wchar Name[NM]; struct dirent *ent=readdir(dirp); if (ent==NULL) - return(false); + return false; if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) continue; - if (CmpName(FindMask,ent->d_name,MATCH_NAMES)) + if (!CharToWide(ent->d_name,Name,ASIZE(Name))) + uiMsg(UIERROR_INVALIDNAME,UINULL,Name); + + if (CmpName(FindMask,Name,MATCH_NAMES)) { - char FullName[NM]; - strcpy(FullName,FindMask); + wchar FullName[NM]; + wcsncpyz(FullName,FindMask,ASIZE(FullName)); *PointToName(FullName)=0; - if (strlen(FullName)+strlen(ent->d_name)>=ASIZE(FullName)-1) + if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1) { -#ifndef SILENT - Log(NULL,"\n%s%s",FullName,ent->d_name); - Log(NULL,St(MPathTooLong)); -#endif - return(false); + uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name); + return false; } - strcat(FullName,ent->d_name); - if (!FastFind(FullName,NULL,fd,GetSymLink)) + wcsncatz(FullName,Name,ASIZE(FullName)); + if (!FastFind(FullName,fd,GetSymLink)) { ErrHandler.OpenErrorMsg(FullName); continue; } - strcpy(fd->Name,FullName); + wcsncpyz(fd->Name,FullName,ASIZE(fd->Name)); break; } } - *fd->NameW=0; -#ifdef _APPLE - if (!LowAscii(fd->Name)) - UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW)); -#elif defined(UNICODE_SUPPORTED) - if (!LowAscii(fd->Name) && UnicodeEnabled()) - CharToWide(fd->Name,fd->NameW); -#endif #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); + fd->IsLink=IsLink(fd->FileAttr); + FirstCall=false; - char *Name=PointToName(fd->Name); - if (strcmp(Name,".")==0 || strcmp(Name,"..")==0) - return(Next(fd)); - return(true); + wchar *NameOnly=PointToName(fd->Name); + if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0) + return Next(fd); + return true; } -bool FindFile::FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink) +bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) { fd->Error=false; #ifndef _UNIX - if (IsWildcard(FindMask,FindMaskW)) - return(false); + if (IsWildcard(FindMask)) + return false; #endif -#ifdef _WIN_32 - HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd); +#ifdef _WIN_ALL + HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd); if (hFind==INVALID_HANDLE_VALUE) - return(false); + return false; FindClose(hFind); -#else +#elif defined(_UNIX) + char FindMaskA[NM]; + WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); + struct stat st; if (GetSymLink) { #ifdef SAVE_LINKS - if (lstat(FindMask,&st)!=0) + if (lstat(FindMaskA,&st)!=0) #else - if (stat(FindMask,&st)!=0) + if (stat(FindMaskA,&st)!=0) #endif { fd->Error=(errno!=ENOENT); - return(false); + return false; } } else - if (stat(FindMask,&st)!=0) + if (stat(FindMaskA,&st)!=0) { fd->Error=(errno!=ENOENT); - return(false); + return false; } -#ifdef _DJGPP - fd->FileAttr=_chmod(FindMask,0); -#elif defined(_EMX) - fd->FileAttr=st.st_attr; -#else fd->FileAttr=st.st_mode; -#endif - fd->IsDir=IsDir(st.st_mode); fd->Size=st.st_size; - fd->mtime=st.st_mtime; - fd->atime=st.st_atime; - fd->ctime=st.st_ctime; - fd->FileTime=fd->mtime.GetDos(); - strcpy(fd->Name,FindMask); - *fd->NameW=0; -#ifdef _APPLE - if (!LowAscii(fd->Name)) - UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW)); -#elif defined(UNICODE_SUPPORTED) - if (!LowAscii(fd->Name) && UnicodeEnabled()) - CharToWide(fd->Name,fd->NameW); -#endif + File::StatToRarTime(st,&fd->mtime,&fd->ctime,&fd->atime); + + wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); #endif fd->Flags=0; fd->IsDir=IsDir(fd->FileAttr); - return(true); + fd->IsLink=IsLink(fd->FileAttr); + + return true; } -#ifdef _WIN_32 -HANDLE FindFile::Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd) +#ifdef _WIN_ALL +HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd) { -#ifndef _WIN_CE - if (WinNT()) -#endif + WIN32_FIND_DATA FindData; + if (hFind==INVALID_HANDLE_VALUE) { - wchar WideMask[NM]; - if (MaskW!=NULL && *MaskW!=0) - strcpyw(WideMask,MaskW); - else - CharToWide(Mask,WideMask); - - WIN32_FIND_DATAW FindData; + hFind=FindFirstFile(Mask,&FindData); if (hFind==INVALID_HANDLE_VALUE) { - hFind=FindFirstFileW(WideMask,&FindData); - if (hFind==INVALID_HANDLE_VALUE) - { - int SysErr=GetLastError(); - fd->Error=(SysErr!=ERROR_FILE_NOT_FOUND && - SysErr!=ERROR_PATH_NOT_FOUND && - SysErr!=ERROR_NO_MORE_FILES); - } + wchar LongMask[NM]; + if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask))) + hFind=FindFirstFile(LongMask,&FindData); } - else - if (!FindNextFileW(hFind,&FindData)) - { - hFind=INVALID_HANDLE_VALUE; - fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; - } - - if (hFind!=INVALID_HANDLE_VALUE) + if (hFind==INVALID_HANDLE_VALUE) { - strcpyw(fd->NameW,WideMask); - strcpyw(PointToName(fd->NameW),FindData.cFileName); - WideToChar(fd->NameW,fd->Name); - fd->Size=int32to64(FindData.nFileSizeHigh,FindData.nFileSizeLow); - fd->FileAttr=FindData.dwFileAttributes; - WideToChar(FindData.cAlternateFileName,fd->ShortName); - fd->ftCreationTime=FindData.ftCreationTime; - fd->ftLastAccessTime=FindData.ftLastAccessTime; - fd->ftLastWriteTime=FindData.ftLastWriteTime; - fd->mtime=FindData.ftLastWriteTime; - fd->ctime=FindData.ftCreationTime; - fd->atime=FindData.ftLastAccessTime; - fd->FileTime=fd->mtime.GetDos(); - -#ifndef _WIN_CE - if (LowAscii(fd->NameW)) - *fd->NameW=0; -#endif + int SysErr=GetLastError(); + // We must not issue an error for "file not found" and "path not found", + // because it is normal to not find anything for wildcard mask when + // archiving. Also searching for non-existent file is normal in some + // other modules, like WinRAR scanning for winrar_theme_description.txt + // to check if any themes are available. + fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && + SysErr!=ERROR_PATH_NOT_FOUND && + SysErr!=ERROR_NO_MORE_FILES; } } -#ifndef _WIN_CE else - { - char CharMask[NM]; - if (Mask!=NULL && *Mask!=0) - strcpy(CharMask,Mask); - else - WideToChar(MaskW,CharMask); - - WIN32_FIND_DATA FindData; - if (hFind==INVALID_HANDLE_VALUE) + if (!FindNextFile(hFind,&FindData)) { - hFind=FindFirstFile(CharMask,&FindData); - if (hFind==INVALID_HANDLE_VALUE) - { - int SysErr=GetLastError(); - fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && SysErr!=ERROR_PATH_NOT_FOUND; - } + hFind=INVALID_HANDLE_VALUE; + fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; } - else - if (!FindNextFile(hFind,&FindData)) - { - hFind=INVALID_HANDLE_VALUE; - fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; - } - if (hFind!=INVALID_HANDLE_VALUE) - { - strcpy(fd->Name,CharMask); - strcpy(PointToName(fd->Name),FindData.cFileName); - CharToWide(fd->Name,fd->NameW); - fd->Size=int32to64(FindData.nFileSizeHigh,FindData.nFileSizeLow); - fd->FileAttr=FindData.dwFileAttributes; - strcpy(fd->ShortName,FindData.cAlternateFileName); - fd->ftCreationTime=FindData.ftCreationTime; - fd->ftLastAccessTime=FindData.ftLastAccessTime; - fd->ftLastWriteTime=FindData.ftLastWriteTime; - fd->mtime=FindData.ftLastWriteTime; - fd->ctime=FindData.ftCreationTime; - fd->atime=FindData.ftLastAccessTime; - fd->FileTime=fd->mtime.GetDos(); - if (LowAscii(fd->Name)) - *fd->NameW=0; - } + if (hFind!=INVALID_HANDLE_VALUE) + { + wcsncpyz(fd->Name,Mask,ASIZE(fd->Name)); + SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name)); + fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); + fd->FileAttr=FindData.dwFileAttributes; + fd->ftCreationTime=FindData.ftCreationTime; + fd->ftLastAccessTime=FindData.ftLastAccessTime; + fd->ftLastWriteTime=FindData.ftLastWriteTime; + fd->mtime.SetWinFT(&FindData.ftLastWriteTime); + fd->ctime.SetWinFT(&FindData.ftCreationTime); + fd->atime.SetWinFT(&FindData.ftLastAccessTime); + + } -#endif fd->Flags=0; - return(hFind); + return hFind; } #endif diff --git a/unrar/unrar/find.hpp b/unrar/unrar/find.hpp index 3320d50..250637f 100644 --- a/unrar/unrar/find.hpp +++ b/unrar/unrar/find.hpp @@ -2,22 +2,20 @@ #define _RAR_FINDDATA_ enum FINDDATA_FLAGS { - FDDF_SECONDDIR=1 // second entry of directory in SCAN_GETDIRSTWICE ScanTree mode + FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode. }; struct FindData { - char Name[NM]; - wchar NameW[NM]; - Int64 Size; + wchar Name[NM]; + uint64 Size; uint FileAttr; - uint FileTime; bool IsDir; + bool IsLink; RarTime mtime; RarTime ctime; RarTime atime; -#ifdef _WIN_32 - char ShortName[NM]; +#ifdef _WIN_ALL FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; @@ -29,14 +27,13 @@ struct FindData class FindFile { private: -#ifdef _WIN_32 - static HANDLE Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd); +#ifdef _WIN_ALL + static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd); #endif - char FindMask[NM]; - wchar FindMaskW[NM]; + wchar FindMask[NM]; bool FirstCall; -#ifdef _WIN_32 +#ifdef _WIN_ALL HANDLE hFind; #else DIR *dirp; @@ -44,10 +41,9 @@ class FindFile public: FindFile(); ~FindFile(); - void SetMask(const char *FindMask); - void SetMaskW(const wchar *FindMaskW); - bool Next(struct FindData *fd,bool GetSymLink=false); - static bool FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink=false); + void SetMask(const wchar *Mask); + bool Next(FindData *fd,bool GetSymLink=false); + static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false); }; #endif diff --git a/unrar/unrar/getbits.cpp b/unrar/unrar/getbits.cpp index 71ecfc8..5d5ad2b 100644 --- a/unrar/unrar/getbits.cpp +++ b/unrar/unrar/getbits.cpp @@ -1,24 +1,52 @@ #include "rar.hpp" -BitInput::BitInput() +BitInput::BitInput(bool AllocBuffer) { - InBuf=new byte[MAX_SIZE]; + ExternalBuffer=false; + if (AllocBuffer) + { + // getbits*() attempt to read data from InAddr, ... InAddr+4 positions. + // So let's allocate 4 additional bytes for situation, when we need to + // read only 1 byte from the last position of buffer and avoid a crash + // from access to next 4 bytes, which contents we do not need. + size_t BufSize=MAX_SIZE+4; + InBuf=new byte[BufSize]; + + // Ensure that we get predictable results when accessing bytes in area + // not filled with read data. + memset(InBuf,0,BufSize); + } + else + InBuf=NULL; } BitInput::~BitInput() { - delete[] InBuf; + if (!ExternalBuffer) + delete[] InBuf; } -void BitInput::faddbits(int Bits) +void BitInput::faddbits(uint Bits) { + // Function wrapped version of inline addbits to save code size. addbits(Bits); } -unsigned int BitInput::fgetbits() +uint BitInput::fgetbits() +{ + // Function wrapped version of inline getbits to save code size. + return getbits(); +} + + +void BitInput::SetExternalBuffer(byte *Buf) { - return(getbits()); + if (InBuf!=NULL && !ExternalBuffer) + delete[] InBuf; + InBuf=Buf; + ExternalBuffer=true; } + diff --git a/unrar/unrar/getbits.hpp b/unrar/unrar/getbits.hpp index eff4daf..00acbea 100644 --- a/unrar/unrar/getbits.hpp +++ b/unrar/unrar/getbits.hpp @@ -4,35 +4,77 @@ class BitInput { public: - enum BufferSize {MAX_SIZE=0x8000}; - protected: - int InAddr,InBit; + enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer. + + int InAddr; // Curent byte position in the buffer. + int InBit; // Current bit position in the current byte. + + bool ExternalBuffer; public: - BitInput(); + BitInput(bool AllocBuffer); ~BitInput(); - byte *InBuf; + byte *InBuf; // Dynamically allocated input buffer. void InitBitInput() { InAddr=InBit=0; } - void addbits(int Bits) + + // Move forward by 'Bits' bits. + void addbits(uint Bits) { Bits+=InBit; InAddr+=Bits>>3; InBit=Bits&7; } - unsigned int getbits() + + // Return 16 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits() { - unsigned int BitField=(uint)InBuf[InAddr] << 16; +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + uint32 BitField=*(uint32*)(InBuf+InAddr); + BitField=ByteSwap32(BitField); + BitField >>= (16-InBit); +#else + uint BitField=(uint)InBuf[InAddr] << 16; BitField|=(uint)InBuf[InAddr+1] << 8; BitField|=(uint)InBuf[InAddr+2]; BitField >>= (8-InBit); - return(BitField & 0xffff); +#endif + return BitField & 0xffff; + } + + + // Return 32 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits32() + { +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + uint32 BitField=*(uint32*)(InBuf+InAddr); + BitField=ByteSwap32(BitField); +#else + uint BitField=(uint)InBuf[InAddr] << 24; + BitField|=(uint)InBuf[InAddr+1] << 16; + BitField|=(uint)InBuf[InAddr+2] << 8; + BitField|=(uint)InBuf[InAddr+3]; +#endif + BitField <<= InBit; + BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); + return BitField & 0xffffffff; } - void faddbits(int Bits); - unsigned int fgetbits(); - bool Overflow(int IncPtr) {return(InAddr+IncPtr>=MAX_SIZE);} + + void faddbits(uint Bits); + uint fgetbits(); + + // Check if buffer has enough space for IncPtr bytes. Returns 'true' + // if buffer will be overflown. + bool Overflow(uint IncPtr) + { + return InAddr+IncPtr>=MAX_SIZE; + } + + void SetExternalBuffer(byte *Buf); }; #endif diff --git a/unrar/unrar/global.cpp b/unrar/unrar/global.cpp index 593a057..771f000 100644 --- a/unrar/unrar/global.cpp +++ b/unrar/unrar/global.cpp @@ -1,4 +1,7 @@ #define INCLUDEGLOBAL +#ifdef _MSC_VER +#pragma hdrstop +#endif #include "rar.hpp" diff --git a/unrar/unrar/hardlinks.cpp b/unrar/unrar/hardlinks.cpp new file mode 100644 index 0000000..171b5fa --- /dev/null +++ b/unrar/unrar/hardlinks.cpp @@ -0,0 +1,37 @@ +bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +{ + if (!FileExist(NameExisting)) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + uiMsg(UIERROR_NOLINKTARGET); + ErrHandler.SetErrorCode(RARX_CREATE); + return false; + } + CreatePath(NameNew,true,Cmd->DisableNames); + +#ifdef _WIN_ALL + bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#elif defined(_UNIX) + char NameExistingA[NM],NameNewA[NM]; + WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA)); + WideToChar(NameNew,NameNewA,ASIZE(NameNewA)); + bool Success=link(NameExistingA,NameNewA)==0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#else + return false; +#endif +} + diff --git a/unrar/unrar/hash.cpp b/unrar/unrar/hash.cpp new file mode 100644 index 0000000..106cc60 --- /dev/null +++ b/unrar/unrar/hash.cpp @@ -0,0 +1,135 @@ +#include "rar.hpp" + +void HashValue::Init(HASH_TYPE Type) +{ + HashValue::Type=Type; + + // Zero length data CRC32 is 0. It is important to set it when creating + // headers with no following data like directories or symlinks. + if (Type==HASH_RAR14 || Type==HASH_CRC32) + CRC32=0; + if (Type==HASH_BLAKE2) + { + // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f + // is BLAKE2sp hash of empty data. We init the structure to this value, + // so if we create a file or service header with no following data like + // "file copy" or "symlink", we set the checksum to proper value avoiding + // additional header type or size checks when extracting. + static byte EmptyHash[32]={ + 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43, + 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25, + 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1, + 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f + }; + memcpy(Digest,EmptyHash,sizeof(Digest)); + } +} + + +bool HashValue::operator == (const HashValue &cmp) const +{ + if (Type==HASH_NONE || cmp.Type==HASH_NONE) + return true; + if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 || + Type==HASH_CRC32 && cmp.Type==HASH_CRC32) + return CRC32==cmp.CRC32; + if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2) + return memcmp(Digest,cmp.Digest,sizeof(Digest))==0; + return false; +} + + +DataHash::DataHash() +{ + blake2ctx=NULL; + HashType=HASH_NONE; +#ifdef RAR_SMP + ThPool=NULL; + MaxThreads=0; +#endif +} + + +DataHash::~DataHash() +{ +#ifdef RAR_SMP + delete ThPool; +#endif + cleandata(&CurCRC32, sizeof(CurCRC32)); + if (blake2ctx!=NULL) + { + cleandata(blake2ctx, sizeof(blake2sp_state)); + delete blake2ctx; + } +} + + +void DataHash::Init(HASH_TYPE Type,uint MaxThreads) +{ + if (blake2ctx==NULL) + blake2ctx=new blake2sp_state; + HashType=Type; + if (Type==HASH_RAR14) + CurCRC32=0; + if (Type==HASH_CRC32) + CurCRC32=0xffffffff; // Initial CRC32 value. + if (Type==HASH_BLAKE2) + blake2sp_init(blake2ctx); +#ifdef RAR_SMP + DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads); +#endif +} + + +void DataHash::Update(const void *Data,size_t DataSize) +{ +#ifndef SFX_MODULE + if (HashType==HASH_RAR14) + CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize); +#endif + if (HashType==HASH_CRC32) + CurCRC32=CRC32(CurCRC32,Data,DataSize); + + if (HashType==HASH_BLAKE2) + { +#ifdef RAR_SMP + if (MaxThreads>1 && ThPool==NULL) + ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER); + blake2ctx->ThPool=ThPool; + blake2ctx->MaxThreads=MaxThreads; +#endif + blake2sp_update( blake2ctx, (byte *)Data, DataSize); + } +} + + +void DataHash::Result(HashValue *Result) +{ + Result->Type=HashType; + if (HashType==HASH_RAR14) + Result->CRC32=CurCRC32; + if (HashType==HASH_CRC32) + Result->CRC32=CurCRC32^0xffffffff; + if (HashType==HASH_BLAKE2) + { + // Preserve the original context, so we can continue hashing if necessary. + blake2sp_state res=*blake2ctx; + blake2sp_final(&res,Result->Digest); + } +} + + +uint DataHash::GetCRC32() +{ + return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0; +} + + +bool DataHash::Cmp(HashValue *CmpValue,byte *Key) +{ + HashValue Final; + Result(&Final); + if (Key!=NULL) + ConvertHashToMAC(&Final,Key); + return Final==*CmpValue; +} diff --git a/unrar/unrar/hash.hpp b/unrar/unrar/hash.hpp new file mode 100644 index 0000000..6315680 --- /dev/null +++ b/unrar/unrar/hash.hpp @@ -0,0 +1,58 @@ +#ifndef _RAR_DATAHASH_ +#define _RAR_DATAHASH_ + +enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2}; + +struct HashValue +{ + void Init(HASH_TYPE Type); + + // Use the const member, so types on both sides of "==" match. + // Otherwise clang -std=c++20 issues "ambiguity is between a regular call + // to this operator and a call with the argument order reversed" warning. + bool operator == (const HashValue &cmp) const; + + // Not actually used now. Const member for same reason as operator == above. + bool operator != (const HashValue &cmp) const {return !(*this==cmp);} + + HASH_TYPE Type; + union + { + uint CRC32; + byte Digest[SHA256_DIGEST_SIZE]; + }; +}; + + +#ifdef RAR_SMP +class ThreadPool; +class DataHash; +#endif + + +class DataHash +{ + private: + HASH_TYPE HashType; + uint CurCRC32; + blake2sp_state *blake2ctx; + +#ifdef RAR_SMP + ThreadPool *ThPool; + + uint MaxThreads; + // Upper limit for maximum threads to prevent wasting threads in pool. + static const uint MaxHashThreads=8; +#endif + public: + DataHash(); + ~DataHash(); + void Init(HASH_TYPE Type,uint MaxThreads); + void Update(const void *Data,size_t DataSize); + void Result(HashValue *Result); + uint GetCRC32(); + bool Cmp(HashValue *CmpValue,byte *Key); + HASH_TYPE Type() {return HashType;} +}; + +#endif diff --git a/unrar/unrar/headers.cpp b/unrar/unrar/headers.cpp new file mode 100644 index 0000000..b441376 --- /dev/null +++ b/unrar/unrar/headers.cpp @@ -0,0 +1,53 @@ +#include "rar.hpp" + +void FileHeader::Reset(size_t SubDataSize) +{ + SubData.Alloc(SubDataSize); + BaseBlock::Reset(); + FileHash.Init(HASH_NONE); + mtime.Reset(); + atime.Reset(); + ctime.Reset(); + SplitBefore=false; + SplitAfter=false; + + UnknownUnpSize=0; + + SubFlags=0; // Important for RAR 3.0 subhead. + + CryptMethod=CRYPT_NONE; + Encrypted=false; + SaltSet=false; + UsePswCheck=false; + UseHashKey=false; + Lg2Count=0; + + Solid=false; + Dir=false; + WinSize=0; + Inherited=false; + SubBlock=false; + CommentInHeader=false; + Version=false; + LargeFile=false; + + RedirType=FSREDIR_NONE; + DirTarget=false; + UnixOwnerSet=false; +} + + +FileHeader& FileHeader::operator = (FileHeader &hd) +{ + SubData.Reset(); + memcpy(this,&hd,sizeof(*this)); + SubData.CleanData(); + SubData=hd.SubData; + return *this; +} + + +void MainHeader::Reset() +{ + *this={}; +} diff --git a/unrar/unrar/headers.hpp b/unrar/unrar/headers.hpp index 94e8aa0..5984f99 100644 --- a/unrar/unrar/headers.hpp +++ b/unrar/unrar/headers.hpp @@ -1,154 +1,158 @@ #ifndef _RAR_HEADERS_ #define _RAR_HEADERS_ -#define SIZEOF_MARKHEAD 7 -#define SIZEOF_OLDMHD 7 -#define SIZEOF_NEWMHD 13 -#define SIZEOF_OLDLHD 21 -#define SIZEOF_NEWLHD 32 -#define SIZEOF_SHORTBLOCKHEAD 7 +#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header. +#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header. +#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. +#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. +#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header. +#define SIZEOF_SHORTBLOCKHEAD 7 // Smallest RAR 4.x block size. #define SIZEOF_LONGBLOCKHEAD 11 #define SIZEOF_SUBBLOCKHEAD 14 #define SIZEOF_COMMHEAD 13 #define SIZEOF_PROTECTHEAD 26 -#define SIZEOF_AVHEAD 14 -#define SIZEOF_SIGNHEAD 15 -#define SIZEOF_UOHEAD 18 -#define SIZEOF_MACHEAD 22 -#define SIZEOF_EAHEAD 24 -#define SIZEOF_BEEAHEAD 24 #define SIZEOF_STREAMHEAD 26 -#define PACK_VER 29 -#define PACK_CRYPT_VER 29 -#define UNP_VER 36 -#define CRYPT_VER 29 -#define AV_VER 20 -#define PROTECT_VER 20 - -#define MHD_VOLUME 0x0001 -#define MHD_COMMENT 0x0002 -#define MHD_LOCK 0x0004 -#define MHD_SOLID 0x0008 -#define MHD_PACK_COMMENT 0x0010 -#define MHD_NEWNUMBERING 0x0010 -#define MHD_AV 0x0020 -#define MHD_PROTECT 0x0040 -#define MHD_PASSWORD 0x0080 -#define MHD_FIRSTVOLUME 0x0100 -#define MHD_ENCRYPTVER 0x0200 - -#define LHD_SPLIT_BEFORE 0x0001 -#define LHD_SPLIT_AFTER 0x0002 -#define LHD_PASSWORD 0x0004 -#define LHD_COMMENT 0x0008 -#define LHD_SOLID 0x0010 - -#define LHD_WINDOWMASK 0x00e0 -#define LHD_WINDOW64 0x0000 -#define LHD_WINDOW128 0x0020 -#define LHD_WINDOW256 0x0040 -#define LHD_WINDOW512 0x0060 -#define LHD_WINDOW1024 0x0080 -#define LHD_WINDOW2048 0x00a0 -#define LHD_WINDOW4096 0x00c0 -#define LHD_DIRECTORY 0x00e0 - -#define LHD_LARGE 0x0100 -#define LHD_UNICODE 0x0200 -#define LHD_SALT 0x0400 -#define LHD_VERSION 0x0800 -#define LHD_EXTTIME 0x1000 -#define LHD_EXTFLAGS 0x2000 - -#define SKIP_IF_UNKNOWN 0x4000 -#define LONG_BLOCK 0x8000 - -#define EARC_NEXT_VOLUME 0x0001 // not last volume -#define EARC_DATACRC 0x0002 // store CRC32 of RAR archive (now used only in volumes) -#define EARC_REVSPACE 0x0004 // reserve space for end of REV file 7 byte record -#define EARC_VOLNUMBER 0x0008 // store a number of current volume +#define VER_PACK 29U +#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive. +#define VER_UNPACK 29U +#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive. +#define VER_UNKNOWN 9999U // Just some large value. + +#define MHD_VOLUME 0x0001U + +// Old style main archive comment embed into main archive header. Must not +// be used in new archives anymore. +#define MHD_COMMENT 0x0002U + +#define MHD_LOCK 0x0004U +#define MHD_SOLID 0x0008U +#define MHD_PACK_COMMENT 0x0010U +#define MHD_NEWNUMBERING 0x0010U +#define MHD_AV 0x0020U +#define MHD_PROTECT 0x0040U +#define MHD_PASSWORD 0x0080U +#define MHD_FIRSTVOLUME 0x0100U + +#define LHD_SPLIT_BEFORE 0x0001U +#define LHD_SPLIT_AFTER 0x0002U +#define LHD_PASSWORD 0x0004U + +// Old style file comment embed into file header. Must not be used +// in new archives anymore. +#define LHD_COMMENT 0x0008U + +// For non-file subheaders it denotes 'subblock having a parent file' flag. +#define LHD_SOLID 0x0010U + + +#define LHD_WINDOWMASK 0x00e0U +#define LHD_WINDOW64 0x0000U +#define LHD_WINDOW128 0x0020U +#define LHD_WINDOW256 0x0040U +#define LHD_WINDOW512 0x0060U +#define LHD_WINDOW1024 0x0080U +#define LHD_WINDOW2048 0x00a0U +#define LHD_WINDOW4096 0x00c0U +#define LHD_DIRECTORY 0x00e0U + +#define LHD_LARGE 0x0100U +#define LHD_UNICODE 0x0200U +#define LHD_SALT 0x0400U +#define LHD_VERSION 0x0800U +#define LHD_EXTTIME 0x1000U + +#define SKIP_IF_UNKNOWN 0x4000U +#define LONG_BLOCK 0x8000U + +#define EARC_NEXT_VOLUME 0x0001U // Not last volume. +#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes). +#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record. +#define EARC_VOLNUMBER 0x0008U // Store a number of current volume. enum HEADER_TYPE { - MARK_HEAD=0x72,MAIN_HEAD=0x73,FILE_HEAD=0x74,COMM_HEAD=0x75,AV_HEAD=0x76, - SUB_HEAD=0x77,PROTECT_HEAD=0x78,SIGN_HEAD=0x79,NEWSUB_HEAD=0x7a, - ENDARC_HEAD=0x7b + // RAR 5.0 header types. + HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03, + HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff, + + // RAR 1.5 - 4.x header types. + HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75, + HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79, + HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b }; + +// RAR 2.9 and earlier. enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103, NTACL_HEAD=0x104,STREAM_HEAD=0x105 }; + +// Internal implementation, depends on archive format version. enum HOST_SYSTEM { + // RAR 5.0 host OS + HOST5_WINDOWS=0,HOST5_UNIX=1, + + // RAR 3.0 host OS. HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, HOST_BEOS=5,HOST_MAX }; -#define SUBHEAD_TYPE_CMT "CMT" -#define SUBHEAD_TYPE_ACL "ACL" -#define SUBHEAD_TYPE_STREAM "STM" -#define SUBHEAD_TYPE_UOWNER "UOW" -#define SUBHEAD_TYPE_AV "AV" -#define SUBHEAD_TYPE_RR "RR" -#define SUBHEAD_TYPE_OS2EA "EA2" -#define SUBHEAD_TYPE_BEOSEA "EABE" - -/* new file inherits a subblock when updating a host file */ -#define SUBHEAD_FLAGS_INHERITED 0x80000000 +// Unified archive format independent implementation. +enum HOST_SYSTEM_TYPE { + HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN +}; -#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001 -struct OldMainHeader -{ - byte Mark[4]; - ushort HeadSize; - byte Flags; +// We also use these values in extra field, so do not modify them. +enum FILE_SYSTEM_REDIRECT { + FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION, + FSREDIR_HARDLINK, FSREDIR_FILECOPY }; -struct OldFileHeader -{ - uint PackSize; - uint UnpSize; - ushort FileCRC; - ushort HeadSize; - uint FileTime; - byte FileAttr; - byte Flags; - byte UnpVer; - byte NameSize; - byte Method; -}; +#define SUBHEAD_TYPE_CMT L"CMT" +#define SUBHEAD_TYPE_QOPEN L"QO" +#define SUBHEAD_TYPE_ACL L"ACL" +#define SUBHEAD_TYPE_STREAM L"STM" +#define SUBHEAD_TYPE_UOWNER L"UOW" +#define SUBHEAD_TYPE_AV L"AV" +#define SUBHEAD_TYPE_RR L"RR" +#define SUBHEAD_TYPE_OS2EA L"EA2" + +/* new file inherits a subblock when updating a host file */ +#define SUBHEAD_FLAGS_INHERITED 0x80000000 + +#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001 struct MarkHeader { - byte Mark[7]; + byte Mark[8]; + + // Following fields are virtual and not present in real blocks. + uint HeadSize; }; struct BaseBlock { - ushort HeadCRC; - HEADER_TYPE HeadType;//byte - ushort Flags; - ushort HeadSize; + uint HeadCRC; // 'ushort' for RAR 1.5. + HEADER_TYPE HeaderType; // 1 byte for RAR 1.5. + uint Flags; // 'ushort' for RAR 1.5. + uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0. - bool IsSubBlock() + bool SkipIfUnknown; + + void Reset() { - if (HeadType==SUB_HEAD) - return(true); - if (HeadType==NEWSUB_HEAD && (Flags & LHD_SOLID)!=0) - return(true); - return(false); + SkipIfUnknown=false; } }; + struct BlockHeader:BaseBlock { - union { - uint DataSize; - uint PackSize; - }; + uint DataSize; }; @@ -156,75 +160,145 @@ struct MainHeader:BaseBlock { ushort HighPosAV; uint PosAV; - byte EncryptVer; + bool CommentInHeader; + bool PackComment; // For RAR 1.4 archive format only. + bool Locator; + uint64 QOpenOffset; // Offset of quick list record. + uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. + uint64 RROffset; // Offset of recovery record. + uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. + size_t MetaNameMaxSize; // Maximum size of archive name in metadata extra field. + std::wstring OrigName; // Original archive name. + RarTime OrigTime; // Original archive time. + + void Reset(); }; -#define SALT_SIZE 8 - struct FileHeader:BlockHeader { - uint UnpSize; byte HostOS; - uint FileCRC; - uint FileTime; - byte UnpVer; + uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5. byte Method; - ushort NameSize; union { uint FileAttr; uint SubFlags; }; -/* optional */ - uint HighPackSize; - uint HighUnpSize; -/* names */ - char FileName[NM]; - wchar FileNameW[NM]; -/* optional */ + wchar FileName[NM]; + Array<byte> SubData; - byte Salt[SALT_SIZE]; RarTime mtime; RarTime ctime; RarTime atime; - RarTime arctime; -/* dummy */ - Int64 FullPackSize; - Int64 FullUnpSize; - void Clear(int SubDataSize) - { - SubData.Alloc(SubDataSize); - Flags=LONG_BLOCK; - SubFlags=0; - } + int64 PackSize; + int64 UnpSize; + int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size. + + HashValue FileHash; + + uint FileFlags; + + bool SplitBefore; + bool SplitAfter; + + bool UnknownUnpSize; - bool CmpName(const char *Name) + bool Encrypted; + CRYPT_METHOD CryptMethod; + bool SaltSet; + byte Salt[SIZE_SALT50]; + byte InitV[SIZE_INITV]; + bool UsePswCheck; + byte PswCheck[SIZE_PSWCHECK]; + + // Use HMAC calculated from HashKey and checksum instead of plain checksum. + bool UseHashKey; + + // Key to convert checksum to HMAC. Derived from password with PBKDF2 + // using additional iterations. + byte HashKey[SHA256_DIGEST_SIZE]; + + uint Lg2Count; // Log2 of PBKDF2 repetition count. + + bool Solid; + bool Dir; + bool CommentInHeader; // RAR 2.0 file comment. + bool Version; // name.ext;ver file name containing the version number. + size_t WinSize; + bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only). + + // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0. + bool LargeFile; + + // 'true' for HEAD_SERVICE block, which is a child of preceding file block. + // RAR 4.x uses 'solid' flag to indicate children subheader blocks in archives. + bool SubBlock; + + HOST_SYSTEM_TYPE HSType; + + FILE_SYSTEM_REDIRECT RedirType; + wchar RedirName[NM]; + bool DirTarget; + + bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric; + char UnixOwnerName[256],UnixGroupName[256]; +#ifdef _UNIX + uid_t UnixOwnerID; + gid_t UnixGroupID; +#else // Need these Unix fields in Windows too for 'list' command. + uint UnixOwnerID; + uint UnixGroupID; +#endif + + void Reset(size_t SubDataSize=0); + + bool CmpName(const wchar *Name) { - return(strcmp(FileName,Name)==0); + return(wcscmp(FileName,Name)==0); } - FileHeader& operator = (FileHeader &hd) + FileHeader& operator = (FileHeader &hd); +}; + + +struct EndArcHeader:BaseBlock +{ + // Optional CRC32 of entire archive up to start of EndArcHeader block. + // Present in RAR 4.x archives if EARC_DATACRC flag is set. + uint ArcDataCRC; + + uint VolNumber; // Optional number of current volume. + + // 7 additional zero bytes can be stored here if EARC_REVSPACE is set. + + bool NextVolume; // Not last volume. + bool DataCRC; + bool RevSpace; + bool StoreVolNumber; + void Reset() { - SubData.Reset(); - memcpy(this,&hd,sizeof(*this)); - SubData.CleanData(); - SubData=hd.SubData; - return(*this); + BaseBlock::Reset(); + NextVolume=false; + DataCRC=false; + RevSpace=false; + StoreVolNumber=false; } }; -struct EndArcHeader:BaseBlock +struct CryptHeader:BaseBlock { - uint ArcDataCRC; // optional archive CRC32 - ushort VolNumber; // optional current volume number + bool UsePswCheck; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte Salt[SIZE_SALT50]; + byte PswCheck[SIZE_PSWCHECK]; }; // SubBlockHeader and its successors were used in RAR 2.x format. -// RAR 3.x uses FileHeader with NEWSUB_HEAD HeadType for subblocks. +// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks. struct SubBlockHeader:BlockHeader { ushort SubType; @@ -250,33 +324,6 @@ struct ProtectHeader:BlockHeader }; -struct AVHeader:BaseBlock -{ - byte UnpVer; - byte Method; - byte AVVer; - uint AVInfoCRC; -}; - - -struct SignHeader:BaseBlock -{ - uint CreationTime; - ushort ArcNameSize; - ushort UserNameSize; -}; - - -struct UnixOwnersHeader:SubBlockHeader -{ - ushort OwnerNameSize; - ushort GroupNameSize; -/* dummy */ - char OwnerName[NM]; - char GroupName[NM]; -}; - - struct EAHeader:SubBlockHeader { uint UnpSize; @@ -293,15 +340,7 @@ struct StreamHeader:SubBlockHeader byte Method; uint StreamCRC; ushort StreamNameSize; -/* dummy */ - byte StreamName[NM]; -}; - - -struct MacFInfoHeader:SubBlockHeader -{ - uint fileType; - uint fileCreator; + char StreamName[260]; }; diff --git a/unrar/unrar/headers5.hpp b/unrar/unrar/headers5.hpp new file mode 100644 index 0000000..50f5955 --- /dev/null +++ b/unrar/unrar/headers5.hpp @@ -0,0 +1,107 @@ +#ifndef _RAR_HEADERS5_ +#define _RAR_HEADERS5_ + +#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length. +#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size. + +// RAR 5.0 block flags common for all blocks. + +// Additional extra area is present in the end of block header. +#define HFL_EXTRA 0x0001 +// Additional data area is present in the end of block header. +#define HFL_DATA 0x0002 +// Unknown blocks with this flag must be skipped when updating an archive. +#define HFL_SKIPIFUNKNOWN 0x0004 +// Data area of this block is continuing from previous volume. +#define HFL_SPLITBEFORE 0x0008 +// Data area of this block is continuing in next volume. +#define HFL_SPLITAFTER 0x0010 +// Block depends on preceding file block. +#define HFL_CHILD 0x0020 +// Preserve a child block if host is modified. +#define HFL_INHERITED 0x0040 + +// RAR 5.0 main archive header specific flags. +#define MHFL_VOLUME 0x0001 // Volume. +#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first. +#define MHFL_SOLID 0x0004 // Solid archive. +#define MHFL_PROTECT 0x0008 // Recovery record is present. +#define MHFL_LOCK 0x0010 // Locked archive. + +// RAR 5.0 file header specific flags. +#define FHFL_DIRECTORY 0x0001 // Directory. +#define FHFL_UTIME 0x0002 // Time field in Unix format is present. +#define FHFL_CRC32 0x0004 // CRC32 field is present. +#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size. + +// RAR 5.0 end of archive header specific flags. +#define EHFL_NEXTVOLUME 0x0001 // Not last volume. + +// RAR 5.0 archive encryption header specific flags. +#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present. + + +// RAR 5.0 file compression flags. +#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm. +#define FCI_ALGO_BIT1 0x0002 // 0 .. 63. +#define FCI_ALGO_BIT2 0x0004 +#define FCI_ALGO_BIT3 0x0008 +#define FCI_ALGO_BIT4 0x0010 +#define FCI_ALGO_BIT5 0x0020 +#define FCI_SOLID 0x0040 // Solid flag. +#define FCI_METHOD_BIT0 0x0080 // Compression method. +#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used). +#define FCI_METHOD_BIT2 0x0200 +#define FCI_DICT_BIT0 0x0400 // Dictionary size. +#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB. +#define FCI_DICT_BIT2 0x1000 +#define FCI_DICT_BIT3 0x2000 + +// Main header extra field values. +#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. +#define MHEXTRA_METADATA 0x02 // Archive metadata. + +// Flags for MHEXTRA_LOCATOR. +#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. +#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present. + +// Flags for MHEXTRA_METADATA. +#define MHEXTRA_METADATA_NAME 0x01 // Archive name is present. +#define MHEXTRA_METADATA_CTIME 0x02 // Archive creation time is present. +#define MHEXTRA_METADATA_UNIXTIME 0x04 // Use Unix nanosecond time format. +#define MHEXTRA_METADATA_UNIX_NS 0x08 // Unix format with nanosecond precision. + +// File and service header extra field values. +#define FHEXTRA_CRYPT 0x01 // Encryption parameters. +#define FHEXTRA_HASH 0x02 // File hash. +#define FHEXTRA_HTIME 0x03 // High precision file time. +#define FHEXTRA_VERSION 0x04 // File version information. +#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.). +#define FHEXTRA_UOWNER 0x06 // Unix owner and group information. +#define FHEXTRA_SUBDATA 0x07 // Service header subdata array. + + +// Hash type values for FHEXTRA_HASH. +#define FHEXTRA_HASH_BLAKE2 0x00 + +// Flags for FHEXTRA_HTIME. +#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format. +#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present. +#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present. +#define FHEXTRA_HTIME_ATIME 0x08 // atime is present. +#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision. + +// Flags for FHEXTRA_CRYPT. +#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data. +#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums. + +// Flags for FHEXTRA_REDIR. +#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory. + +// Flags for FHEXTRA_UOWNER. +#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present. +#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present. +#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present. +#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present. + +#endif diff --git a/unrar/unrar/int64.cpp b/unrar/unrar/int64.cpp deleted file mode 100644 index 996d0ea..0000000 --- a/unrar/unrar/int64.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include "rar.hpp" - -#ifndef NATIVE_INT64 - -Int64::Int64() -{ -} - - -Int64::Int64(uint n) -{ - HighPart=0; - LowPart=n; -} - - -Int64::Int64(uint HighPart,uint LowPart) -{ - Int64::HighPart=HighPart; - Int64::LowPart=LowPart; -} - - -/* -Int64 Int64::operator = (Int64 n) -{ - HighPart=n.HighPart; - LowPart=n.LowPart; - return(*this); -} -*/ - - -Int64 Int64::operator << (int n) -{ - Int64 res=*this; - while (n--) - { - res.HighPart<<=1; - if (res.LowPart & 0x80000000) - res.HighPart|=1; - res.LowPart<<=1; - } - return(res); -} - - -Int64 Int64::operator >> (int n) -{ - Int64 res=*this; - while (n--) - { - res.LowPart>>=1; - if (res.HighPart & 1) - res.LowPart|=0x80000000; - res.HighPart>>=1; - } - return(res); -} - - -Int64 operator / (Int64 n1,Int64 n2) -{ - if (n1.HighPart==0 && n2.HighPart==0) - return(Int64(0,n1.LowPart/n2.LowPart)); - int ShiftCount=0; - while (n1>n2) - { - n2=n2<<1; - if (++ShiftCount>64) - return(0); - } - Int64 res=0; - while (ShiftCount-- >= 0) - { - res=res<<1; - if (n1>=n2) - { - n1-=n2; - ++res; - } - n2=n2>>1; - } - return(res); -} - - -Int64 operator * (Int64 n1,Int64 n2) -{ - if (n1<0x10000 && n2<0x10000) - return(Int64(0,n1.LowPart*n2.LowPart)); - Int64 res=0; - for (int I=0;I<64;I++) - { - if (n2.LowPart & 1) - res+=n1; - n1=n1<<1; - n2=n2>>1; - } - return(res); -} - - -Int64 operator % (Int64 n1,Int64 n2) -{ - if (n1.HighPart==0 && n2.HighPart==0) - return(Int64(0,n1.LowPart%n2.LowPart)); - return(n1-n1/n2*n2); -} - - -Int64 operator + (Int64 n1,Int64 n2) -{ - n1.LowPart+=n2.LowPart; - if (n1.LowPart<n2.LowPart) - n1.HighPart++; - n1.HighPart+=n2.HighPart; - return(n1); -} - - -Int64 operator - (Int64 n1,Int64 n2) -{ - if (n1.LowPart<n2.LowPart) - n1.HighPart--; - n1.LowPart-=n2.LowPart; - n1.HighPart-=n2.HighPart; - return(n1); -} - - -Int64 operator += (Int64 &n1,Int64 n2) -{ - n1=n1+n2; - return(n1); -} - - -Int64 operator -= (Int64 &n1,Int64 n2) -{ - n1=n1-n2; - return(n1); -} - - -Int64 operator *= (Int64 &n1,Int64 n2) -{ - n1=n1*n2; - return(n1); -} - - -Int64 operator /= (Int64 &n1,Int64 n2) -{ - n1=n1/n2; - return(n1); -} - - -Int64 operator | (Int64 n1,Int64 n2) -{ - n1.LowPart|=n2.LowPart; - n1.HighPart|=n2.HighPart; - return(n1); -} - - -Int64 operator & (Int64 n1,Int64 n2) -{ - n1.LowPart&=n2.LowPart; - n1.HighPart&=n2.HighPart; - return(n1); -} - - -/* -inline void operator -= (Int64 &n1,unsigned int n2) -{ - if (n1.LowPart<n2) - n1.HighPart--; - n1.LowPart-=n2; -} - - -inline void operator ++ (Int64 &n) -{ - if (++n.LowPart == 0) - ++n.HighPart; -} - - -inline void operator -- (Int64 &n) -{ - if (n.LowPart-- == 0) - n.HighPart--; -} -*/ - -bool operator == (Int64 n1,Int64 n2) -{ - return(n1.LowPart==n2.LowPart && n1.HighPart==n2.HighPart); -} - - -bool operator > (Int64 n1,Int64 n2) -{ - return((int)n1.HighPart>(int)n2.HighPart || n1.HighPart==n2.HighPart && n1.LowPart>n2.LowPart); -} - - -bool operator < (Int64 n1,Int64 n2) -{ - return((int)n1.HighPart<(int)n2.HighPart || n1.HighPart==n2.HighPart && n1.LowPart<n2.LowPart); -} - - -bool operator != (Int64 n1,Int64 n2) -{ - return(n1.LowPart!=n2.LowPart || n1.HighPart!=n2.HighPart); -} - - -bool operator >= (Int64 n1,Int64 n2) -{ - return(n1>n2 || n1==n2); -} - - -bool operator <= (Int64 n1,Int64 n2) -{ - return(n1<n2 || n1==n2); -} - - -void Int64::Set(uint HighPart,uint LowPart) -{ - Int64::HighPart=HighPart; - Int64::LowPart=LowPart; -} -#endif - -void itoa(Int64 n,char *Str) -{ - if (n<=0xffffffff) - { - sprintf(Str,"%u",int64to32(n)); - return; - } - - char NumStr[50]; - int Pos=0; - - do - { - NumStr[Pos++]=int64to32(n%10)+'0'; - n=n/10; - } while (n!=0); - - for (int I=0;I<Pos;I++) - Str[I]=NumStr[Pos-I-1]; - Str[Pos]=0; -} - - -Int64 atoil(char *Str) -{ - Int64 n=0; - while (*Str>='0' && *Str<='9') - { - n=n*10+*Str-'0'; - Str++; - } - return(n); -} diff --git a/unrar/unrar/int64.hpp b/unrar/unrar/int64.hpp deleted file mode 100644 index d2f34bb..0000000 --- a/unrar/unrar/int64.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef _RAR_INT64_ -#define _RAR_INT64_ - -#if defined(__BORLANDC__) || defined(_MSC_VER) -#define NATIVE_INT64 -typedef __int64 Int64; -#endif - -#if defined(__GNUC__) || defined(__HP_aCC) || defined(__SUNPRO_CC) -#define NATIVE_INT64 -typedef long long Int64; -#endif - -#ifdef NATIVE_INT64 - -#define int64to32(x) ((uint)(x)) -#define int32to64(high,low) ((((Int64)(high))<<32)+(low)) -#define is64plus(x) (x>=0) - -#else - -class Int64 -{ - public: - Int64(); - Int64(uint n); - Int64(uint HighPart,uint LowPart); - -// Int64 operator = (Int64 n); - Int64 operator << (int n); - Int64 operator >> (int n); - - friend Int64 operator / (Int64 n1,Int64 n2); - friend Int64 operator * (Int64 n1,Int64 n2); - friend Int64 operator % (Int64 n1,Int64 n2); - friend Int64 operator + (Int64 n1,Int64 n2); - friend Int64 operator - (Int64 n1,Int64 n2); - friend Int64 operator += (Int64 &n1,Int64 n2); - friend Int64 operator -= (Int64 &n1,Int64 n2); - friend Int64 operator *= (Int64 &n1,Int64 n2); - friend Int64 operator /= (Int64 &n1,Int64 n2); - friend Int64 operator | (Int64 n1,Int64 n2); - friend Int64 operator & (Int64 n1,Int64 n2); - inline friend void operator -= (Int64 &n1,unsigned int n2) - { - if (n1.LowPart<n2) - n1.HighPart--; - n1.LowPart-=n2; - } - inline friend void operator ++ (Int64 &n) - { - if (++n.LowPart == 0) - ++n.HighPart; - } - inline friend void operator -- (Int64 &n) - { - if (n.LowPart-- == 0) - n.HighPart--; - } - friend bool operator == (Int64 n1,Int64 n2); - friend bool operator > (Int64 n1,Int64 n2); - friend bool operator < (Int64 n1,Int64 n2); - friend bool operator != (Int64 n1,Int64 n2); - friend bool operator >= (Int64 n1,Int64 n2); - friend bool operator <= (Int64 n1,Int64 n2); - - void Set(uint HighPart,uint LowPart); - uint GetLowPart() {return(LowPart);} - - uint LowPart; - uint HighPart; -}; - -inline uint int64to32(Int64 n) {return(n.GetLowPart());} -#define int32to64(high,low) (Int64((high),(low))) -#define is64plus(x) ((int)(x).HighPart>=0) - -#endif - -#define INT64ERR int32to64(0x80000000,0) -#define INT64MAX int32to64(0x7fffffff,0) - -void itoa(Int64 n,char *Str); -Int64 atoil(char *Str); - -#endif diff --git a/unrar/unrar/isnt.cpp b/unrar/unrar/isnt.cpp index 493a9eb..3cc876b 100644 --- a/unrar/unrar/isnt.cpp +++ b/unrar/unrar/isnt.cpp @@ -1,9 +1,9 @@ #include "rar.hpp" -#ifdef _WIN_32 -int WinNT() +DWORD WinNT() { - static int dwPlatformId=-1,dwMajorVersion; + static int dwPlatformId=-1; + static DWORD dwMajorVersion,dwMinorVersion; if (dwPlatformId==-1) { OSVERSIONINFO WinVer; @@ -11,7 +11,104 @@ int WinNT() GetVersionEx(&WinVer); dwPlatformId=WinVer.dwPlatformId; dwMajorVersion=WinVer.dwMajorVersion; + dwMinorVersion=WinVer.dwMinorVersion; + + } + DWORD Result=0; + if (dwPlatformId==VER_PLATFORM_WIN32_NT) + Result=dwMajorVersion*0x100+dwMinorVersion; + + + return Result; +} + + +// Replace it with documented Windows 11 check when available. +#include <comdef.h> +#include <Wbemidl.h> +#pragma comment(lib, "wbemuuid.lib") + +static bool WMI_IsWindows10() +{ + IWbemLocator *pLoc = NULL; + + HRESULT hres = CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER, + IID_IWbemLocator,(LPVOID *)&pLoc); + + if (FAILED(hres)) + return false; + + IWbemServices *pSvc = NULL; + + hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"),NULL,NULL,NULL,NULL,0,0,&pSvc); + + if (FAILED(hres)) + { + pLoc->Release(); + return false; + } + + hres = CoSetProxyBlanket(pSvc,RPC_C_AUTHN_WINNT,RPC_C_AUTHZ_NONE,NULL, + RPC_C_AUTHN_LEVEL_CALL,RPC_C_IMP_LEVEL_IMPERSONATE,NULL,EOAC_NONE); + + if (FAILED(hres)) + { + pSvc->Release(); + pLoc->Release(); + return false; + } + + IEnumWbemClassObject *pEnumerator = NULL; + hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_OperatingSystem"), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); + + if (FAILED(hres)) + { + pSvc->Release(); + pLoc->Release(); + return false; + } + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + bool Win10=false; + while (pEnumerator!=NULL) + { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if (uReturn==0) + break; + + VARIANT vtProp; + + hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0); + Win10|=wcsstr(vtProp.bstrVal,L"Windows 10")!=NULL; + VariantClear(&vtProp); + + pclsObj->Release(); + } + + pSvc->Release(); + pLoc->Release(); + pEnumerator->Release(); + + return Win10; +} + + +// Replace it with actual check when available. +bool IsWindows11OrGreater() +{ + static bool IsSet=false,IsWin11=false; + if (!IsSet) + { + OSVERSIONINFO WinVer; + WinVer.dwOSVersionInfoSize=sizeof(WinVer); + GetVersionEx(&WinVer); + IsWin11=WinVer.dwMajorVersion>10 || + WinVer.dwMajorVersion==10 && WinVer.dwBuildNumber >= 22000 && !WMI_IsWindows10(); + IsSet=true; } - return(dwPlatformId==VER_PLATFORM_WIN32_NT ? dwMajorVersion:0); + return IsWin11; } -#endif diff --git a/unrar/unrar/isnt.hpp b/unrar/unrar/isnt.hpp index 0265236..fed0b51 100644 --- a/unrar/unrar/isnt.hpp +++ b/unrar/unrar/isnt.hpp @@ -1,6 +1,16 @@ #ifndef _RAR_ISNT_ #define _RAR_ISNT_ -int WinNT(); +enum WINNT_VERSION { + WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500, + WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601, + WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00 +}; + +DWORD WinNT(); + + +// Replace it with actual check when available. +bool IsWindows11OrGreater(); #endif diff --git a/unrar/unrar/license.txt b/unrar/unrar/license.txt index ec35e09..0811276 100644 --- a/unrar/unrar/license.txt +++ b/unrar/unrar/license.txt @@ -10,13 +10,15 @@ 1. All copyrights to RAR and the utility UnRAR are exclusively owned by the author - Alexander Roshal. - 2. The UnRAR sources may be used in any software to handle RAR - archives without limitations free of charge, but cannot be used - to re-create the RAR compression algorithm, which is proprietary. - Distribution of modified UnRAR sources in separate form or as a - part of other software is permitted, provided that it is clearly - stated in the documentation and source comments that the code may - not be used to develop a RAR (WinRAR) compatible archiver. + 2. UnRAR source code may be used in any software to handle + RAR archives without limitations free of charge, but cannot be + used to develop RAR (WinRAR) compatible archiver and to + re-create RAR compression algorithm, which is proprietary. + Distribution of modified UnRAR source code in separate form + or as a part of other software is permitted, provided that + full text of this paragraph, starting from "UnRAR source code" + words, is included in license, or in documentation if license + is not available, and in source code comments of resulting package. 3. The UnRAR utility may be freely distributed. It is allowed to distribute UnRAR inside of other software packages. @@ -37,4 +39,4 @@ Thank you for your interest in RAR and UnRAR. - Alexander L. Roshal
\ No newline at end of file + Alexander L. Roshal diff --git a/unrar/unrar/list.cpp b/unrar/unrar/list.cpp index 99d730f..e4444e1 100644 --- a/unrar/unrar/list.cpp +++ b/unrar/unrar/list.cpp @@ -1,140 +1,153 @@ #include "rar.hpp" -static void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare); +static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames); static void ListSymLink(Archive &Arc); -static void ListFileAttr(uint A,int HostOS); +static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize); static void ListOldSubHeader(Archive &Arc); -static void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical); +static void ListNewSubHeader(CommandData *Cmd,Archive &Arc); void ListArchive(CommandData *Cmd) { - Int64 SumPackSize=0,SumUnpSize=0; + int64 SumPackSize=0,SumUnpSize=0; uint ArcCount=0,SumFileCount=0; bool Technical=(Cmd->Command[1]=='T'); + bool ShowService=Technical && Cmd->Command[2]=='A'; bool Bare=(Cmd->Command[1]=='B'); - bool Verbose=(*Cmd->Command=='V'); + bool Verbose=(Cmd->Command[0]=='V'); - char ArcName[NM]; - wchar ArcNameW[NM]; - - while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName))) + wchar ArcName[NM]; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + Archive Arc(Cmd); -#ifdef _WIN_32 - Arc.RemoveSequentialFlag(); -#endif - if (!Arc.WOpen(ArcName,ArcNameW)) + if (!Arc.WOpen(ArcName)) continue; bool FileMatched=true; - while (1) + while (true) { - Int64 TotalPackSize=0,TotalUnpSize=0; + int64 TotalPackSize=0,TotalUnpSize=0; uint FileCount=0; if (Arc.IsArchive(true)) { - if (!Arc.IsOpened()) - break; bool TitleShown=false; if (!Bare) { Arc.ViewComment(); + mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); - // RAR can close a corrupt encrypted archive - if (!Arc.IsOpened()) - break; - - mprintf("\n"); + mprintf(L"\n%s: ",St(MListDetails)); + uint SetCount=0; + const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt); if (Arc.Solid) - mprintf(St(MListSolid)); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid)); if (Arc.SFXSize>0) - mprintf(St(MListSFX)); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX)); if (Arc.Volume) - if (Arc.Solid) - mprintf(St(MListVol1)); - else - mprintf(St(MListVol2)); - else - if (Arc.Solid) - mprintf(St(MListArc1)); + if (Arc.Format==RARFMT50) + { + // RAR 5.0 archives store the volume number in main header, + // so it is already available now. + if (SetCount++ > 0) + mprintf(L", "); + mprintf(St(MVolumeNumber),Arc.VolNumber+1); + } else - mprintf(St(MListArc2)); - mprintf(" %s\n",Arc.FileName); - if (Technical) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume)); + if (Arc.Protected) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR)); + if (Arc.Locked) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); + if (Arc.Encrypted) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); + + if (!Arc.MainHead.OrigName.empty()) + mprintf(L"\n%s: %s",St(MOrigName),Arc.MainHead.OrigName.c_str()); + if (Arc.MainHead.OrigTime.IsSet()) { - if (Arc.Protected) - mprintf(St(MListRecRec)); - if (Arc.Locked) - mprintf(St(MListLock)); + wchar DateStr[50]; + Arc.MainHead.OrigTime.GetText(DateStr,ASIZE(DateStr),Technical); + mprintf(L"\n%s: %s",St(MOriginalTime),DateStr); } + + mprintf(L"\n"); } - while(Arc.ReadHeader()>0) + + wchar VolNumText[50]; + *VolNumText=0; + while (Arc.ReadHeader()>0) { - int HeaderType=Arc.GetHeaderType(); - if (HeaderType==ENDARC_HEAD) + Wait(); // Allow quit listing with Ctrl+C. + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_ENDARC) + { +#ifndef SFX_MODULE + // Only RAR 1.5 archives store the volume number in end record. + if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15) + swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1); +#endif + if (Technical && ShowService) + { + mprintf(L"\n%12ls: %ls",St(MListService),L"EOF"); + if (*VolNumText!=0) + mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText); + mprintf(L"\n"); + } break; + } switch(HeaderType) { - case FILE_HEAD: - IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName); - if ((FileMatched=Cmd->IsProcessFile(Arc.NewLhd))==true) + case HEAD_FILE: + FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0; + if (FileMatched) { - ListFileHeader(Arc.NewLhd,Verbose,Technical,TitleShown,Bare); - if (!(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)) + ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare,Cmd->DisableNames); + if (!Arc.FileHead.SplitBefore) { - TotalUnpSize+=Arc.NewLhd.FullUnpSize; + TotalUnpSize+=Arc.FileHead.UnpSize; FileCount++; } - TotalPackSize+=Arc.NewLhd.FullPackSize; - if (Technical) - ListSymLink(Arc); -#ifndef SFX_MODULE - if (Verbose) - Arc.ViewFileComment(); -#endif + TotalPackSize+=Arc.FileHead.PackSize; } break; -#ifndef SFX_MODULE - case SUB_HEAD: - if (Technical && FileMatched && !Bare) - ListOldSubHeader(Arc); - break; -#endif - case NEWSUB_HEAD: + case HEAD_SERVICE: if (FileMatched && !Bare) { - if (Technical) - ListFileHeader(Arc.SubHead,Verbose,true,TitleShown,false); - ListNewSubHeader(Cmd,Arc,Technical); + if (Technical && ShowService) + ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false,Cmd->DisableNames); } break; } Arc.SeekToNext(); } - if (!Bare) + if (!Bare && !Technical) if (TitleShown) { - mprintf("\n"); - for (int I=0;I<79;I++) - mprintf("-"); - char UnpSizeText[20]; - itoa(TotalUnpSize,UnpSizeText); + wchar UnpSizeText[20]; + itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText)); - char PackSizeText[20]; - itoa(TotalPackSize,PackSizeText); + wchar PackSizeText[20]; + itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText)); - mprintf("\n%5lu %16s %8s %3d%%",FileCount,UnpSizeText, - PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize)); + if (Verbose) + { + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText, + PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize), + VolNumText,FileCount); + } + else + { + mprintf(L"\n----------- --------- ---------- ----- ----"); + mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount); + } + SumFileCount+=FileCount; SumUnpSize+=TotalUnpSize; SumPackSize+=TotalPackSize; -#ifndef SFX_MODULE - if (Arc.EndArcHead.Flags & EARC_VOLNUMBER) - { - mprintf(" "); - mprintf(St(MVolumeNumber),Arc.EndArcHead.VolNumber+1); - } -#endif - mprintf("\n"); + mprintf(L"\n"); } else mprintf(St(MListNoFiles)); @@ -142,254 +155,339 @@ void ListArchive(CommandData *Cmd) ArcCount++; #ifndef NOVOLUME - if (Cmd->VolSize!=0 && ((Arc.NewLhd.Flags & LHD_SPLIT_AFTER) || - Arc.GetHeaderType()==ENDARC_HEAD && - (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)!=0) && - MergeArchive(Arc,NULL,false,*Cmd->Command)) - { + if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter || + Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) && + MergeArchive(Arc,NULL,false,Cmd->Command[0])) Arc.Seek(0,SEEK_SET); - } else #endif break; } else { - if (Cmd->ArcNames->ItemsCount()<2 && !Bare) + if (Cmd->ArcNames.ItemsCount()<2 && !Bare) mprintf(St(MNotRAR),Arc.FileName); break; } } } - if (ArcCount>1 && !Bare) + + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (ArcCount>1 && !Bare && !Technical) { - char UnpSizeText[20],PackSizeText[20]; - itoa(SumUnpSize,UnpSizeText); - itoa(SumPackSize,PackSizeText); - mprintf("\n%5lu %16s %8s %3d%%\n",SumFileCount,UnpSizeText, - PackSizeText,ToPercentUnlim(SumPackSize,SumUnpSize)); + wchar UnpSizeText[20],PackSizeText[20]; + itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText)); + + if (Verbose) + mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText, + ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount); + else + mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount); } } -void ListFileHeader(FileHeader &hd,bool Verbose,bool Technical,bool &TitleShown,bool Bare) +enum LISTCOL_TYPE { + LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR +}; + + +void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare,bool DisableNames) { - if (!Bare) + if (!TitleShown && !Technical && !Bare) { - if (!TitleShown) + if (Verbose) { - if (Verbose) - mprintf(St(MListPathComm)); - else - mprintf(St(MListName)); - mprintf(St(MListTitle)); - if (Technical) - mprintf(St(MListTechTitle)); - for (int I=0;I<79;I++) - mprintf("-"); - TitleShown=true; + mprintf(L"\n%ls",St(MListTitleV)); + if (!DisableNames) + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); } - - if (hd.HeadType==NEWSUB_HEAD) - mprintf(St(MSubHeadType),hd.FileName); - - mprintf("\n%c",(hd.Flags & LHD_PASSWORD) ? '*' : ' '); + else + { + mprintf(L"\n%ls",St(MListTitleL)); + if (!DisableNames) + mprintf(L"\n----------- --------- ---------- ----- ----"); + } + // Must be set even in DisableNames mode to suppress "0 files" output + // unless no files are matched. + TitleShown=true; } + if (DisableNames) + return; - char *Name=hd.FileName; - -#ifdef UNICODE_SUPPORTED - char ConvertedName[NM]; - if ((hd.Flags & LHD_UNICODE)!=0 && *hd.FileNameW!=0 && UnicodeEnabled()) - { - if (WideToChar(hd.FileNameW,ConvertedName) && *ConvertedName!=0) - Name=ConvertedName; - } -#endif + wchar *Name=hd.FileName; + RARFORMAT Format=Arc.Format; if (Bare) { - mprintf("%s\n",Verbose ? Name:PointToName(Name)); + mprintf(L"%s\n",Name); return; } - if (Verbose) - mprintf("%s\n%12s ",Name,""); + wchar UnpSizeText[30],PackSizeText[30]; + if (hd.UnpSize==INT64NDF) + wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText)); else - mprintf("%-12s",PointToName(Name)); + itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText)); - char UnpSizeText[20],PackSizeText[20]; - if (hd.FullUnpSize==INT64MAX) - strcpy(UnpSizeText,"?"); + wchar AttrStr[30]; + if (hd.HeaderType==HEAD_SERVICE) + swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.'); else - itoa(hd.FullUnpSize,UnpSizeText); - itoa(hd.FullPackSize,PackSizeText); + ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr)); - mprintf(" %8s %8s ",UnpSizeText,PackSizeText); + wchar RatioStr[10]; - if ((hd.Flags & LHD_SPLIT_BEFORE) && (hd.Flags & LHD_SPLIT_AFTER)) - mprintf(" <->"); + if (hd.SplitBefore && hd.SplitAfter) + wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr)); else - if (hd.Flags & LHD_SPLIT_BEFORE) - mprintf(" <--"); + if (hd.SplitBefore) + wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr)); else - if (hd.Flags & LHD_SPLIT_AFTER) - mprintf(" -->"); + if (hd.SplitAfter) + wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr)); else - mprintf("%3d%%",ToPercentUnlim(hd.FullPackSize,hd.FullUnpSize)); + swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize)); - char DateStr[50]; - hd.mtime.GetText(DateStr,false); - mprintf(" %s ",DateStr); + wchar DateStr[50]; + hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical); - if (hd.HeadType==NEWSUB_HEAD) - mprintf(" %c....B ",(hd.SubFlags & SUBHEAD_FLAGS_INHERITED) ? 'I' : '.'); - else - ListFileAttr(hd.FileAttr,hd.HostOS); + if (Technical) + { + mprintf(L"\n%12s: %s",St(MListName),Name); - mprintf(" %8.8X",hd.FileCRC); - mprintf(" m%d",hd.Method-0x30); - if ((hd.Flags & LHD_WINDOWMASK)<=6*32) - mprintf("%c",((hd.Flags&LHD_WINDOWMASK)>>5)+'a'); - else - mprintf(" "); - mprintf(" %d.%d",hd.UnpVer/10,hd.UnpVer%10); + bool FileBlock=hd.HeaderType==HEAD_FILE; + + if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + { + mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream)); + wchar StreamName[NM]; + GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); + mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName); + } + else + { + const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService); + + if (hd.RedirType!=FSREDIR_NONE) + switch(hd.RedirType) + { + case FSREDIR_UNIXSYMLINK: + Type=St(MListUSymlink); break; + case FSREDIR_WINSYMLINK: + Type=St(MListWSymlink); break; + case FSREDIR_JUNCTION: + Type=St(MListJunction); break; + case FSREDIR_HARDLINK: + Type=St(MListHardlink); break; + case FSREDIR_FILECOPY: + Type=St(MListCopy); break; + } + mprintf(L"\n%12ls: %ls",St(MListType),Type); + if (hd.RedirType!=FSREDIR_NONE) + if (Format==RARFMT15) + { + char LinkTargetA[NM]; + if (Arc.FileHead.Encrypted) + { + // Link data are encrypted. We would need to ask for password + // and initialize decryption routine to display the link target. + strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA)); + } + else + { + int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1); + Arc.Read(LinkTargetA,DataSize); + LinkTargetA[DataSize > 0 ? DataSize : 0] = 0; + } + wchar LinkTarget[NM]; + CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget)); + mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget); + } + else + mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName); + } + if (!hd.Dir) + { + mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText); + mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText); + mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr); + } + bool WinTitles=false; +#ifdef _WIN_ALL + WinTitles=true; +#endif + if (hd.mtime.IsSet()) + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListModified:MListMtime),DateStr); + if (hd.ctime.IsSet()) + { + hd.ctime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListCreated:MListCtime),DateStr); + } + if (hd.atime.IsSet()) + { + hd.atime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(WinTitles ? MListAccessed:MListAtime),DateStr); + } + mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr); + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"\n%12ls: %8.8X", + hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32", + hd.FileHash.CRC32); + if (hd.FileHash.Type==HASH_BLAKE2) + { + wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1]; + BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr)); + mprintf(L"\n%12ls: %ls", + hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2", + BlakeStr); + } - static const char *RarOS[]={ - "DOS","OS/2","Win95/NT","Unix","MacOS","BeOS","WinCE","","","" - }; + const wchar *HostOS=L""; + if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN) + HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix"; + if (Format==RARFMT15) + { + static const wchar *RarOS[]={ + L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L"" + }; + if (hd.HostOS<ASIZE(RarOS)) + HostOS=RarOS[hd.HostOS]; + } + if (*HostOS!=0) + mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS); - if (Technical) - mprintf("\n%22s %8s %4s", - (hd.HostOS<sizeof(RarOS)/sizeof(RarOS[0]) ? RarOS[hd.HostOS]:""), - (hd.Flags & LHD_SOLID) ? St(MYes):St(MNo), - (hd.Flags & LHD_VERSION) ? St(MYes):St(MNo)); -} + mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo), + Format==RARFMT15 ? L"1.5":L"5.0", + hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method, + hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400, + hd.WinSize>=0x100000 ? L"M":L"K"); + if (hd.Solid || hd.Encrypted) + { + mprintf(L"\n%12ls: ",St(MListFlags)); + if (hd.Solid) + mprintf(L"%ls ",St(MListSolid)); + if (hd.Encrypted) + mprintf(L"%ls ",St(MListEnc)); + } -void ListSymLink(Archive &Arc) -{ - if (Arc.NewLhd.HostOS==HOST_UNIX && (Arc.NewLhd.FileAttr & 0xF000)==0xA000) - if ((Arc.NewLhd.Flags & LHD_PASSWORD)==0) + if (hd.Version) { - char FileName[NM]; - int DataSize=Min(Arc.NewLhd.PackSize,sizeof(FileName)-1); - Arc.Read(FileName,DataSize); - FileName[DataSize]=0; - mprintf("\n%22s %s","-->",FileName); + uint Version=ParseVersionFileName(Name,false); + if (Version!=0) + mprintf(L"\n%12ls: %u",St(MListFileVer),Version); } + + if (hd.UnixOwnerSet) + { + mprintf(L"\n%12ls: ",L"Unix owner"); + if (*hd.UnixOwnerName!=0) + mprintf(L"%ls",GetWide(hd.UnixOwnerName)); + else + if (hd.UnixOwnerNumeric) + mprintf(L"#%d",hd.UnixOwnerID); + mprintf(L":"); + if (*hd.UnixGroupName!=0) + mprintf(L"%ls",GetWide(hd.UnixGroupName)); + else + if (hd.UnixGroupNumeric) + mprintf(L"#%d",hd.UnixGroupID); + } + + mprintf(L"\n"); + return; + } + + mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText); + + if (Verbose) + mprintf(L"%9ls %4ls ",PackSizeText,RatioStr); + + mprintf(L" %ls ",DateStr); + + if (Verbose) + { + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"%8.8X ",hd.FileHash.CRC32); else + if (hd.FileHash.Type==HASH_BLAKE2) + { + byte *S=hd.FileHash.Digest; + mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]); + } + else + mprintf(L"???????? "); + } + mprintf(L"%ls",Name); +} + +/* +void ListSymLink(Archive &Arc) +{ + if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000) + if (Arc.FileHead.Encrypted) { // Link data are encrypted. We would need to ask for password // and initialize decryption routine to display the link target. - mprintf("\n%22s %s","-->","*<-?->"); + mprintf(L"\n%22ls %ls",L"-->",L"*<-?->"); + } + else + { + char FileName[NM]; + uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1); + Arc.Read(FileName,DataSize); + FileName[DataSize]=0; + mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName)); } } +*/ - -void ListFileAttr(uint A,int HostOS) +void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize) { - switch(HostOS) + switch(HostType) { - case HOST_MSDOS: - case HOST_OS2: - case HOST_WIN32: - case HOST_MACOS: - mprintf(" %c%c%c%c%c%c%c ", - (A & 0x08) ? 'V' : '.', - (A & 0x10) ? 'D' : '.', - (A & 0x01) ? 'R' : '.', - (A & 0x02) ? 'H' : '.', - (A & 0x04) ? 'S' : '.', - (A & 0x20) ? 'A' : '.', - (A & 0x800) ? 'C' : '.'); + case HSYS_WINDOWS: + swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c", + (A & 0x2000)!=0 ? 'I' : '.', // Not content indexed. + (A & 0x0800)!=0 ? 'C' : '.', // Compressed. + (A & 0x0020)!=0 ? 'A' : '.', // Archive. + (A & 0x0010)!=0 ? 'D' : '.', // Directory. + (A & 0x0004)!=0 ? 'S' : '.', // System. + (A & 0x0002)!=0 ? 'H' : '.', // Hidden. + (A & 0x0001)!=0 ? 'R' : '.'); // Read-only. break; - case HOST_UNIX: - case HOST_BEOS: + case HSYS_UNIX: switch (A & 0xF000) { case 0x4000: - mprintf("d"); + AttrStr[0]='d'; break; case 0xA000: - mprintf("l"); + AttrStr[0]='l'; break; default: - mprintf("-"); + AttrStr[0]='-'; break; } - mprintf("%c%c%c%c%c%c%c%c%c", + swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c", (A & 0x0100) ? 'r' : '-', (A & 0x0080) ? 'w' : '-', - (A & 0x0040) ? ((A & 0x0800) ? 's':'x'):((A & 0x0800) ? 'S':'-'), + (A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'), (A & 0x0020) ? 'r' : '-', (A & 0x0010) ? 'w' : '-', - (A & 0x0008) ? ((A & 0x0400) ? 's':'x'):((A & 0x0400) ? 'S':'-'), + (A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'), (A & 0x0004) ? 'r' : '-', (A & 0x0002) ? 'w' : '-', - (A & 0x0001) ? 'x' : '-'); - break; - } -} - - -#ifndef SFX_MODULE -void ListOldSubHeader(Archive &Arc) -{ - switch(Arc.SubBlockHead.SubType) - { - case EA_HEAD: - mprintf(St(MListEAHead)); + (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-'); break; - case UO_HEAD: - mprintf(St(MListUOHead),Arc.UOHead.OwnerName,Arc.UOHead.GroupName); + case HSYS_UNKNOWN: + wcsncpyz(AttrStr,L"?",AttrSize); break; - case MAC_HEAD: - mprintf(St(MListMACHead1),Arc.MACHead.fileType>>24,Arc.MACHead.fileType>>16,Arc.MACHead.fileType>>8,Arc.MACHead.fileType); - mprintf(St(MListMACHead2),Arc.MACHead.fileCreator>>24,Arc.MACHead.fileCreator>>16,Arc.MACHead.fileCreator>>8,Arc.MACHead.fileCreator); - break; - case BEEA_HEAD: - mprintf(St(MListBeEAHead)); - break; - case NTACL_HEAD: - mprintf(St(MListNTACLHead)); - break; - case STREAM_HEAD: - mprintf(St(MListStrmHead),Arc.StreamHead.StreamName); - break; - default: - mprintf(St(MListUnkHead),Arc.SubBlockHead.SubType); - break; - } -} -#endif - - -void ListNewSubHeader(CommandData *Cmd,Archive &Arc,bool Technical) -{ - if (Arc.SubHead.CmpName(SUBHEAD_TYPE_CMT) && - (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0 && !Cmd->DisableComment) - { - Array<byte> CmtData; - int ReadSize=Arc.ReadCommentData(&CmtData,NULL); - if (ReadSize!=0) - { - mprintf(St(MFileComment)); - OutComment((char *)&CmtData[0],ReadSize); - } - } - if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM) && - (Arc.SubHead.Flags & LHD_SPLIT_BEFORE)==0) - { - int DestSize=Arc.SubHead.SubData.Size()/2; - wchar DestNameW[NM]; - char DestName[NM]; - if (DestSize<sizeof(DestName)) - { - RawToWide(&Arc.SubHead.SubData[0],DestNameW,DestSize); - DestNameW[DestSize]=0; - WideToChar(DestNameW,DestName); - mprintf("\n %s",DestName); - } } } diff --git a/unrar/unrar/loclang.hpp b/unrar/unrar/loclang.hpp index 37da3a0..1e23ece 100644 --- a/unrar/unrar/loclang.hpp +++ b/unrar/unrar/loclang.hpp @@ -1,352 +1,401 @@ -#define MYesNo "_Yes_No" -#define MYesNoAll "_Yes_No_All" -#define MYesNoAllQ "_Yes_No_All_nEver_Quit" -#define MYesNoAllRenQ "_Yes_No_All_nEver_Rename_Quit" -#define MContinueQuit "_Continue_Quit" -#define MRetryAbort "_Retry_Abort" -#define MCopyright "\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d" -#define MRegTo "\nRegistered to %s\n" -#define MShare "\nShareware version Type RAR -? for help\n" -#define MUCopyright "\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n" -#define MBeta "beta" -#define MMonthJan "Jan" -#define MMonthFeb "Feb" -#define MMonthMar "Mar" -#define MMonthApr "Apr" -#define MMonthMay "May" -#define MMonthJun "Jun" -#define MMonthJul "Jul" -#define MMonthAug "Aug" -#define MMonthSep "Sep" -#define MMonthOct "Oct" -#define MMonthNov "Nov" -#define MMonthDec "Dec" -#define MRARTitle1 "\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>" -#define MUNRARTitle1 "\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>" -#define MRARTitle2 "\n <@listfiles...> <path_to_extract\\>" -#define MCHelpCmd "\n\n<Commands>" -#define MCHelpCmdA "\n a Add files to archive" -#define MCHelpCmdC "\n c Add archive comment" -#define MCHelpCmdCF "\n cf Add files comment" -#define MCHelpCmdCH "\n ch Change archive parameters" -#define MCHelpCmdCW "\n cw Write archive comment to file" -#define MCHelpCmdD "\n d Delete files from archive" -#define MCHelpCmdE "\n e Extract files to current directory" -#define MCHelpCmdF "\n f Freshen files in archive" -#define MCHelpCmdI "\n i[par]=<str> Find string in archives" -#define MCHelpCmdK "\n k Lock archive" -#define MCHelpCmdL "\n l[t,b] List archive [technical, bare]" -#define MCHelpCmdM "\n m[f] Move to archive [files only]" -#define MCHelpCmdP "\n p Print file to stdout" -#define MCHelpCmdR "\n r Repair archive" -#define MCHelpCmdRC "\n rc Reconstruct missing volumes" -#define MCHelpCmdRN "\n rn Rename archived files" -#define MCHelpCmdRR "\n rr[N] Add data recovery record" -#define MCHelpCmdRV "\n rv[N] Create recovery volumes" -#define MCHelpCmdS "\n s[name|-] Convert archive to or from SFX" -#define MCHelpCmdT "\n t Test archive files" -#define MCHelpCmdU "\n u Update files in archive" -#define MCHelpCmdV "\n v[t,b] Verbosely list archive [technical,bare]" -#define MCHelpCmdX "\n x Extract files with full path" -#define MCHelpSw "\n\n<Switches>" -#define MCHelpSwm "\n - Stop switches scanning" -#define MCHelpSwAC "\n ac Clear Archive attribute after compression or extraction" -#define MCHelpSwAD "\n ad Append archive name to destination path" -#define MCHelpSwAG "\n ag[format] Generate archive name using the current date" -#define MCHelpSwAO "\n ao Add files with Archive attribute set" -#define MCHelpSwAP "\n ap<path> Set path inside archive" -#define MCHelpSwAS "\n as Synchronize archive contents" -#define MCHelpSwAV "\n av Put authenticity verification (registered versions only)" -#define MCHelpSwAVm "\n av- Disable authenticity verification check" -#define MCHelpSwCm "\n c- Disable comments show" -#define MCHelpSwCFGm "\n cfg- Disable read configuration" -#define MCHelpSwCL "\n cl Convert names to lower case" -#define MCHelpSwCU "\n cu Convert names to upper case" -#define MCHelpSwDF "\n df Delete files after archiving" -#define MCHelpSwDH "\n dh Open shared files" -#define MCHelpSwDR "\n dr Delete files to Recycle Bin" -#define MCHelpSwDS "\n ds Disable name sort for solid archive" -#define MCHelpSwDW "\n dw Wipe files after archiving" -#define MCHelpSwEa "\n e[+]<attr> Set file exclude and include attributes" -#define MCHelpSwED "\n ed Do not add empty directories" -#define MCHelpSwEE "\n ee Do not save and extract extended attributes" -#define MCHelpSwEN "\n en Do not put 'end of archive' block" -#define MCHelpSwEP "\n ep Exclude paths from names" -#define MCHelpSwEP1 "\n ep1 Exclude base directory from names" -#define MCHelpSwEP2 "\n ep2 Expand paths to full" -#define MCHelpSwEP3 "\n ep3 Expand paths to full including the drive letter" -#define MCHelpSwF "\n f Freshen files" -#define MCHelpSwHP "\n hp[password] Encrypt both file data and headers" -#define MCHelpSwIDP "\n id[c,d,p,q] Disable messages" -#define MCHelpSwIEML "\n ieml[addr] Send archive by email" -#define MCHelpSwIERR "\n ierr Send all messages to stderr" -#define MCHelpSwILOG "\n ilog[name] Log errors to file (registered versions only)" -#define MCHelpSwINUL "\n inul Disable all messages" -#define MCHelpSwIOFF "\n ioff Turn PC off after completing an operation" -#define MCHelpSwISND "\n isnd Enable sound" -#define MCHelpSwK "\n k Lock archive" -#define MCHelpSwKB "\n kb Keep broken extracted files" -#define MCHelpSwMn "\n m<0..5> Set compression level (0-store...3-default...5-maximal)" -#define MCHelpSwMC "\n mc<par> Set advanced compression parameters" -#define MCHelpSwMD "\n md<size> Dictionary size in KB (64,128,256,512,1024,2048,4096 or A-G)" -#define MCHelpSwMS "\n ms[ext;ext] Specify file types to store" -#define MCHelpSwMT "\n mt<threads> Set the number of threads" -#define MCHelpSwN "\n n<file> Include only specified file" -#define MCHelpSwNa "\n n@ Read file names to include from stdin" -#define MCHelpSwNal "\n n@<list> Include files in specified list file" -#define MCHelpSwO "\n o[+|-] Set the overwrite mode" -#define MCHelpSwOC "\n oc Set NTFS Compressed attribute" -#define MCHelpSwOL "\n ol Save symbolic links as the link instead of the file" -#define MCHelpSwOR "\n or Rename files automatically" -#define MCHelpSwOS "\n os Save NTFS streams" -#define MCHelpSwOW "\n ow Save or restore file owner and group" -#define MCHelpSwP "\n p[password] Set password" -#define MCHelpSwPm "\n p- Do not query password" -#define MCHelpSwR "\n r Recurse subdirectories" -#define MCHelpSwR0 "\n r0 Recurse subdirectories for wildcard names only" -#define MCHelpSwRI "\n ri<P>[:<S>] Set priority (0-default,1-min..15-max) and sleep time in ms" -#define MCHelpSwRR "\n rr[N] Add data recovery record" -#define MCHelpSwRV "\n rv[N] Create recovery volumes" -#define MCHelpSwS "\n s[<N>,v[-],e] Create solid archive" -#define MCHelpSwSm "\n s- Disable solid archiving" -#define MCHelpSwSC "\n sc<chr>[obj] Specify the character set" -#define MCHelpSwSFX "\n sfx[name] Create SFX archive" -#define MCHelpSwSI "\n si[name] Read data from standard input (stdin)" -#define MCHelpSwSL "\n sl<size> Process files with size less than specified" -#define MCHelpSwSM "\n sm<size> Process files with size more than specified" -#define MCHelpSwT "\n t Test files after archiving" -#define MCHelpSwTK "\n tk Keep original archive time" -#define MCHelpSwTL "\n tl Set archive time to latest file" -#define MCHelpSwTN "\n tn<time> Process files newer than <time>" -#define MCHelpSwTO "\n to<time> Process files older than <time>" -#define MCHelpSwTA "\n ta<date> Process files modified after <date> in YYYYMMDDHHMMSS format" -#define MCHelpSwTB "\n tb<date> Process files modified before <date> in YYYYMMDDHHMMSS format" -#define MCHelpSwTS "\n ts<m,c,a>[N] Save or restore file time (modification, creation, access)" -#define MCHelpSwU "\n u Update files" -#define MCHelpSwV "\n v Create volumes with size autodetection or list all volumes" -#define MCHelpSwVUnr "\n v List all volumes" -#define MCHelpSwVn "\n v<size>[k,b] Create volumes with size=<size>*1000 [*1024, *1]" -#define MCHelpSwVD "\n vd Erase disk contents before creating volume" -#define MCHelpSwVER "\n ver[n] File version control" -#define MCHelpSwVN "\n vn Use the old style volume naming scheme" -#define MCHelpSwVP "\n vp Pause before each volume" -#define MCHelpSwW "\n w<path> Assign work directory" -#define MCHelpSwX "\n x<file> Exclude specified file" -#define MCHelpSwXa "\n x@ Read file names to exclude from stdin" -#define MCHelpSwXal "\n x@<list> Exclude files in specified list file" -#define MCHelpSwY "\n y Assume Yes on all queries" -#define MCHelpSwZ "\n z[file] Read archive comment from file" -#define MBadArc "\nERROR: Bad archive %s\n" -#define MAskPsw "Enter password (will not be echoed)" -#define MAskPswEcho "Enter password" -#define MReAskPsw "\nReenter password: " -#define MFor " for " -#define MNotMatchPsw "\nERROR: Passwords do not match\n" -#define MErrWrite "Write error in the file %s" -#define MErrRead "Read error in the file %s" -#define MErrSeek "Seek error in the file %s" -#define MErrFClose "Cannot close the file %s" -#define MErrOutMem "Not enough memory" -#define MErrBrokenArc "Corrupt archive - use 'Repair' command" -#define MProgAborted "Program aborted" -#define MErrRename "\nCannot rename %s to %s" -#define MAbsNextVol "\nCannot find volume %s" -#define MBreak "\nUser break\n" -#define MAskCreatVol "\nCreate next volume ?" -#define MAskNextDisk "\nDisk full. Insert next" -#define MCreatVol "\n\nCreating %sarchive %s\n" -#define MAskNextVol "\nInsert disk with %s" -#define MTestVol "\n\nTesting archive %s\n" -#define MExtrVol "\n\nExtracting from %s\n" -#define MConverting "\nConverting %s" -#define MCvtToSFX "\nConvert archives to SFX" -#define MCvtFromSFX "\nRemoving SFX module" -#define MNotSFX "\n%s is not SFX archive" -#define MNotRAR "\n%s is not RAR archive" -#define MNotFirstVol "\n%s is not the first volume" -#define MCvtOldFormat "\n%s - cannot convert to SFX archive with old format" -#define MCannotCreate "\nCannot create %s" -#define MCannotOpen "\nCannot open %s" -#define MUnknownMeth "\nUnknown method in %s" -#define MVerRequired "\nYou need RAR %d.%d to unpack it" -#define MOk " OK" -#define MDone "\nDone" -#define MLockingArc "\nLocking archive" -#define MNotMdfOld "\n\nERROR: Cannot modify old format archive" -#define MNotMdfLock "\n\nERROR: Locked archive" -#define MNotMdfVol "\n\nERROR: Cannot modify volume" -#define MVerifyAV "\nVerifying authenticity information ... " -#define MFailedAV " Failed\n" -#define MStrAV1 "\n\nArchive %s" -#define MStrAV2 "\ncreated at %s" -#define MStrAV3 "\nby %s\n" -#define MLogFailedAV "Invalid authenticity information" -#define MAddingAV "\nAdding authenticity verification " -#define MAVOldStyle "\n\nOld style authenticity information" -#define MPackAskReg "\nEvaluation copy. Please register.\n" -#define MCreateArchive "\nCreating %sarchive %s\n" -#define MUpdateArchive "\nUpdating %sarchive %s\n" -#define MAddSolid "solid " -#define MAddFile "\nAdding %-58s " -#define MUpdFile "\nUpdating %-58s " -#define MAddPoints "\n... %-58s " -#define MCannotUpdPswSolid "\nERROR: Cannot update solid archives with password\n" -#define MMoveDelFiles "\n\nDeleting files %s..." -#define MMoveDelDirs "and directories" -#define MMoveDelFile "\nDeleting %-30s" -#define MMoveDeleted " deleted" -#define MMoveNotDeleted " NOT DELETED" -#define MClearAttrib "\n\nClearing attributes..." -#define MMoveDelDir "\nDeleting directory %-30s" -#define MWarErrFOpen "\nWARNING: Cannot open %d %s" -#define MErrOpenFiles "files" -#define MErrOpenFile "file" -#define MAddNoFiles "\nWARNING: No files" -#define MMdfEncrSol "\n%s: encrypted" -#define MCannotMdfEncrSol "\nCannot modify solid archive containing encrypted files" -#define MAddAnalyze "\nAnalyzing archived files: " -#define MRepacking "\nRepacking archived files: " -#define MCRCFailed "\n%-20s - CRC failed" -#define MExtrTest "\n\nTesting archive %s\n" -#define MExtracting "\n\nExtracting from %s\n" -#define MUseCurPsw "\n%s - use current password ?" -#define MCreatDir "\nCreating %-56s" -#define MExtrSkipFile "\nSkipping %-56s" -#define MExtrTestFile "\nTesting %-56s" -#define MExtrFile "\nExtracting %-56s" -#define MExtrPoints "\n... %-56s" -#define MExtrErrMkDir "\nCannot create directory %s" -#define MExtrPrinting "\n------ Printing %s\n\n" -#define MEncrBadCRC "\nEncrypted file: CRC failed in %s (password incorrect ?)" -#define MExtrNoFiles "\nNo files to extract" -#define MExtrAllOk "\nAll OK" -#define MExtrTotalErr "\nTotal errors: %ld" -#define MFileExists "\n\n%s already exists. Overwrite it ?" -#define MAskOverwrite "\nOverwrite %s ?" -#define MAskNewName "\nEnter new name: " -#define MLogMainHead "\nThe archive header is corrupt" -#define MLogFileHead "\n%s - the file header is corrupt" -#define MLogCommHead "\nThe comment header is corrupt\n" -#define MLogProtectHead "The data recovery header is corrupt" -#define MReadStdinCmt "\nReading comment from stdin\n" -#define MReadCommFrom "\nReading comment from %s" -#define MDelComment "\nDeleting comment from %s" -#define MAddComment "\nAdding comment to %s" -#define MFCommAdd "\nAdding file comments" -#define MAskFComm "\n\nReading comment for %s : %s from stdin\n" -#define MLogCommBrk "\nThe archive comment is corrupt" -#define MCommAskCont "\nPress 'Enter' to continue or 'Q' to quit:" -#define MLogBrokFCmt "\nThe file comment is corrupt" -#define MAbsDestName "\nDestination file name required" -#define MWriteCommTo "\nWrite comment to %s" -#define MCommNotPres "\nComment is not present" -#define MDelFrom "\nDeleting from %s" -#define MDeleting "\nDeleting %s" -#define MEraseArc "\nErasing empty archive %s" -#define MNoDelFiles "\nNo files to delete" -#define MLogTitle "\n\n-------- %2d %s %d, archive %s\n" -#define MPathTooLong "\nERROR: Path too long\n" -#define MListSolid "Solid " -#define MListSFX "SFX " -#define MListVol1 "volume" -#define MListVol2 "Volume" -#define MListArc1 "archive" -#define MListArc2 "Archive" -#define MListRecRec "\nRecovery record is present\n" -#define MListLock "\nLock is present\n" -#define MListPathComm "\nPathname/Comment\n " -#define MListName "\n Name " -#define MListTitle " Size Packed Ratio Date Time Attr CRC Meth Ver\n" -#define MListTechTitle " Host OS Solid Old\n" -#define MListEAHead "\n OS/2 extended attributes" -#define MListUOHead "\n Unix Owner/Group data: %-14s %-14s" -#define MListBeEAHead "\n BeOS extended attributes" -#define MListNTACLHead "\n NTFS security data" -#define MListStrmHead "\n NTFS stream: %s" -#define MListUnkHead "\n Unknown subheader type: 0x%04x" -#define MFileComment "\nComment: " -#define MYes "Yes" -#define MNo "No" -#define MListNoFiles " 0 files\n" -#define MRprReconstr "\nReconstructing %s" -#define MRprBuild "\nBuilding %s" -#define MRprOldFormat "\nCannot repair archive with old format" -#define MRprFind "\nFound %s" -#define MRprAskIsSol "\nThe archive header is corrupt. Mark archive as solid ?" -#define MRprNoFiles "\nNo files found" -#define MRprSuspEntry "\n\nSuspicious entry %s" -#define MRprDir "\nDirectory" -#define MRprSuspSize "\nSize %ld Packed %ld" -#define MRprSuspAdd "\nAdd it to archive ?" -#define MLogUnexpEOF "\nUnexpected end of archive" -#define MRepAskReconst "\nReconstruct archive structure ?" -#define MRecScanning "\nScanning..." -#define MRecRNotFound "\nData recovery record not found" -#define MRecRFound "\nData recovery record found" -#define MRecSecDamage "\nSector %ld (offsets %lX...%lX) damaged" -#define MRecCorrected " - data recovered" -#define MRecFailed " - cannot recover data" -#define MAddRecRec "\nAdding data recovery record" -#define MEraseForVolume "\n\nErasing contents of drive %c:\n" -#define MGetOwnersError "\nWARNING: Cannot get %s owner and group\n" -#define MErrGetOwnerID "\nWARNING: Cannot get owner %s ID\n" -#define MErrGetGroupID "\nWARNING: Cannot get group %s ID\n" -#define MOwnersBroken "\nERROR: %s group and owner data are corrupt\n" -#define MSetOwnersError "\nWARNING: Cannot set %s owner and group\n" -#define MErrLnkRead "\nWARNING: Cannot read symbolic link %s" -#define MErrCreateLnk "\nWARNING: Cannot create link %s" -#define MSymLinkExists "\nWARNING: Symbolic link %s already exists" -#define MAskRetryCreate "\nCannot create %s. Retry ?" -#define MListMACHead1 "\n MacOS file type: %c%c%c%c ; " -#define MListMACHead2 "file creator: %c%c%c%c\n" -#define MDataBadCRC "\n%-20s : packed data CRC failed in volume %s" -#define MFileRO "\n%s is read-only" -#define MACLGetError "\nWARNING: Cannot get %s security data\n" -#define MACLSetError "\nWARNING: Cannot set %s security data\n" -#define MACLBroken "\nERROR: %s security data are corrupt\n" -#define MACLUnknown "\nWARNING: Unknown format of %s security data\n" -#define MStreamBroken "\nERROR: %s stream data are corrupt\n" -#define MStreamUnknown "\nWARNING: Unknown format of %s stream data\n" -#define MInvalidName "\nERROR: Invalid file name %s" -#define MEABroken "\nERROR: %s extended attributes are corrupt\n" -#define MEAUnknHeader "\nWARNING: %s - unknown format of extended attributes\n" -#define MCannotSetEA "\nWARNING: cannot set extended attributes to %s\n" -#define MCannotGetEA "\nERROR: Cannot get extended attributes of %s\n" -#define MShowEA " (+EA)" -#define MSkipEA "\n...skipping extended attributes" -#define MProcessArc "\n\nProcessing archive %s" -#define MSyncScanError "\nFile search errors, cannot synchronize archive" -#define MCorrectingName "\nWARNING: Attempting to correct the invalid file name" -#define MUnpCannotMerge "\nWARNING: You need to start extraction from a previous volume to unpack %s" -#define MUnknownOption "\nERROR: Unknown option: %s" -#define MSubHeadCorrupt "\nERROR: Corrupt data header found, ignored" -#define MSubHeadUnknown "\nWARNING: Unknown data header format, ignored" -#define MSubHeadDataCRC "\nERROR: Corrupt %s data block" -#define MSubHeadType "\nData header type: %s" -#define MScanError "\nCannot read contents of %s" -#define MNotVolume "\n%s is not volume" -#define MRecVolDiffSets "\nERROR: %s and %s belong to different sets" -#define MRecVolMissing "\n%d volumes missing" -#define MRecVolFound "\n%d recovery volumes found" -#define MRecVolAllExist "\nNothing to reconstruct" -#define MRecVolCannotFix "\nReconstruction impossible" -#define MReconstructing "\nReconstructing..." -#define MCreating "\nCreating %s" -#define MRenaming "\nRenaming %s to %s" -#define MNTFSRequired "\nWrite error: only NTFS file system supports files larger than 4 GB" -#define MErrChangeAttr "\nWARNING: Cannot change attributes of %s" -#define MWrongSFXVer "\nERROR: default SFX module does not support RAR %d.%d archives" -#define MCannotEncName "\nCannot encrypt archive already contained encrypted files" -#define MCannotEmail "\nCannot email the file %s" -#define MCopyrightS "\nRAR SFX archive" -#define MSHelpCmd "\n\n<Commands>" -#define MSHelpCmdE "\n -x Extract from archive (default)" -#define MSHelpCmdT "\n -t Test archive files" -#define MSHelpCmdV "\n -v Verbosely list contents of archive" -#define MMaxPathLimit "\nTotal path and file name length must not exceed %d characters" -#define MRecVolLimit "\nTotal number of usual and recovery volumes must not exceed 255" -#define MVolumeNumber "volume %d" -#define MCannotDelete "\nCannot delete %s" -#define MCalcCRC "\nCalculating the control sum" -#define MTooLargeSFXArc "\nWARNING: Too large SFX archive. Windows cannot run the executable file exceeding 4 GB." -#define MCalcCRCAllVol "\nCalculating control sums of all volumes." +#define MYesNo L"_Yes_No" +#define MYesNoAll L"_Yes_No_All" +#define MYesNoAllQ L"_Yes_No_All_nEver_Quit" +#define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit" +#define MContinueQuit L"_Continue_Quit" +#define MRetryAbort L"_Retry_Abort" +#define MIgnoreAllRetryQuit L"_Ignore_iGnore all_Retry_Quit" +#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d" +#define MRegTo L"\nRegistered to %s\n" +#define MShare L"\nTrial version Type 'rar -?' for help\n" +#define MRegKeyWarning L"\nAvailable license key is valid only for %s\n" +#define MUCopyright L"\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n" +#define MBeta L"beta" +#define Mx86 L"x86" +#define Mx64 L"x64" +#define MMonthJan L"Jan" +#define MMonthFeb L"Feb" +#define MMonthMar L"Mar" +#define MMonthApr L"Apr" +#define MMonthMay L"May" +#define MMonthJun L"Jun" +#define MMonthJul L"Jul" +#define MMonthAug L"Aug" +#define MMonthSep L"Sep" +#define MMonthOct L"Oct" +#define MMonthNov L"Nov" +#define MMonthDec L"Dec" +#define MRARTitle1 L"\nUsage: rar <command> -<switch 1> -<switch N> <archive> <files...>" +#define MUNRARTitle1 L"\nUsage: unrar <command> -<switch 1> -<switch N> <archive> <files...>" +#define MRARTitle2 L"\n <@listfiles...> <path_to_extract\\>" +#define MFwrSlTitle2 L"\n <@listfiles...> <path_to_extract/>" +#define MCHelpCmd L"\n\n<Commands>" +#define MCHelpCmdA L"\n a Add files to archive" +#define MCHelpCmdC L"\n c Add archive comment" +#define MCHelpCmdCH L"\n ch Change archive parameters" +#define MCHelpCmdCW L"\n cw Write archive comment to file" +#define MCHelpCmdD L"\n d Delete files from archive" +#define MCHelpCmdE L"\n e Extract files without archived paths" +#define MCHelpCmdF L"\n f Freshen files in archive" +#define MCHelpCmdI L"\n i[par]=<str> Find string in archives" +#define MCHelpCmdK L"\n k Lock archive" +#define MCHelpCmdL L"\n l[t[a],b] List archive contents [technical[all], bare]" +#define MCHelpCmdM L"\n m[f] Move to archive [files only]" +#define MCHelpCmdP L"\n p Print file to stdout" +#define MCHelpCmdR L"\n r Repair archive" +#define MCHelpCmdRC L"\n rc Reconstruct missing volumes" +#define MCHelpCmdRN L"\n rn Rename archived files" +#define MCHelpCmdRR L"\n rr[N] Add the data recovery record" +#define MCHelpCmdRV L"\n rv[N] Create recovery volumes" +#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX" +#define MCHelpCmdT L"\n t Test archive files" +#define MCHelpCmdU L"\n u Update files in archive" +#define MCHelpCmdV L"\n v[t[a],b] Verbosely list archive contents [technical[all],bare]" +#define MCHelpCmdX L"\n x Extract files with full path" +#define MCHelpSw L"\n\n<Switches>" +#define MCHelpSwm L"\n - Stop switches scanning" +#define MCHelpSwAT L"\n @[+] Disable [enable] file lists" +#define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction" +#define MCHelpSwAD L"\n ad[1,2] Alternate destination path" +#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" +#define MCHelpSwAI L"\n ai Ignore file attributes" +#define MCHelpSwAM L"\n am[s,r] Archive name and time [save, restore]" +#define MCHelpSwAO L"\n ao Add files with Archive attribute set" +#define MCHelpSwAP L"\n ap<path> Set path inside archive" +#define MCHelpSwAS L"\n as Synchronize archive contents" +#define MCHelpSwCm L"\n c- Disable comments show" +#define MCHelpSwCFGm L"\n cfg- Disable read configuration" +#define MCHelpSwCL L"\n cl Convert names to lower case" +#define MCHelpSwCU L"\n cu Convert names to upper case" +#define MCHelpSwDF L"\n df Delete files after archiving" +#define MCHelpSwDH L"\n dh Open shared files" +#define MCHelpSwDR L"\n dr Delete files to Recycle Bin" +#define MCHelpSwDS L"\n ds Disable name sort for solid archive" +#define MCHelpSwDW L"\n dw Wipe files after archiving" +#define MCHelpSwEa L"\n e[+]<attr> Set file exclude and include attributes" +#define MCHelpSwED L"\n ed Do not add empty directories" +#define MCHelpSwEP L"\n ep Exclude paths from names" +#define MCHelpSwEP1 L"\n ep1 Exclude base directory from names" +#define MCHelpSwEP2 L"\n ep2 Expand paths to full" +#define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter" +#define MCHelpSwEP4 L"\n ep4<path> Exclude the path prefix from names" +#define MCHelpSwF L"\n f Freshen files" +#define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers" +#define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum" +#define MCHelpSwIDP L"\n id[c,d,n,p,q] Display or disable messages" +#define MCHelpSwIEML L"\n ieml[addr] Send archive by email" +#define MCHelpSwIERR L"\n ierr Send all messages to stderr" +#define MCHelpSwILOG L"\n ilog[name] Log errors to file" +#define MCHelpSwINUL L"\n inul Disable all messages" +#define MCHelpSwIOFF L"\n ioff[n] Turn PC off after completing an operation" +#define MCHelpSwISND L"\n isnd[-] Control notification sounds" +#define MCHelpSwIVER L"\n iver Display the version number" +#define MCHelpSwK L"\n k Lock archive" +#define MCHelpSwKB L"\n kb Keep broken extracted files" +#define MCHelpSwLog L"\n log[f][=name] Write names to log file" +#define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)" +#define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format" +#define MCHelpSwMC L"\n mc<par> Set advanced compression parameters" +#define MCHelpSwMD L"\n md<n>[k,m,g] Dictionary size in KB, MB or GB" +#define MCHelpSwME L"\n me[par] Set encryption parameters" +#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store" +#define MCHelpSwMT L"\n mt<threads> Set the number of threads" +#define MCHelpSwN L"\n n<file> Additionally filter included files" +#define MCHelpSwNa L"\n n@ Read additional filter masks from stdin" +#define MCHelpSwNal L"\n n@<list> Read additional filter masks from list file" +#define MCHelpSwO L"\n o[+|-] Set the overwrite mode" +#define MCHelpSwOC L"\n oc Set NTFS Compressed attribute" +#define MCHelpSwOH L"\n oh Save hard links as the link instead of the file" +#define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references" +#define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]" +#define MCHelpSwONI L"\n oni Allow potentially incompatible names" +#define MCHelpSwOP L"\n op<path> Set the output path for extracted files" +#define MCHelpSwOR L"\n or Rename files automatically" +#define MCHelpSwOS L"\n os Save NTFS streams" +#define MCHelpSwOW L"\n ow Save or restore file owner and group" +#define MCHelpSwP L"\n p[password] Set password" +#define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]" +#define MCHelpSwR L"\n r Recurse subdirectories" +#define MCHelpSwRm L"\n r- Disable recursion" +#define MCHelpSwR0 L"\n r0 Recurse subdirectories for wildcard names only" +#define MCHelpSwRI L"\n ri<P>[:<S>] Set priority (0-default,1-min..15-max) and sleep time in ms" +#define MCHelpSwRR L"\n rr[N] Add data recovery record" +#define MCHelpSwRV L"\n rv[N] Create recovery volumes" +#define MCHelpSwS L"\n s[<N>,v[-],e] Create solid archive" +#define MCHelpSwSm L"\n s- Disable solid archiving" +#define MCHelpSwSC L"\n sc<chr>[obj] Specify the character set" +#define MCHelpSwSFX L"\n sfx[name] Create SFX archive" +#define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)" +#define MCHelpSwSL L"\n sl<size> Process files with size less than specified" +#define MCHelpSwSM L"\n sm<size> Process files with size more than specified" +#define MCHelpSwT L"\n t Test files after archiving" +#define MCHelpSwTK L"\n tk Keep original archive time" +#define MCHelpSwTL L"\n tl Set archive time to latest file" +#define MCHelpSwTN L"\n tn[mcao]<t> Process files newer than <t> time" +#define MCHelpSwTO L"\n to[mcao]<t> Process files older than <t> time" +#define MCHelpSwTA L"\n ta[mcao]<d> Process files modified after <d> YYYYMMDDHHMMSS date" +#define MCHelpSwTB L"\n tb[mcao]<d> Process files modified before <d> YYYYMMDDHHMMSS date" +#define MCHelpSwTS L"\n ts[m,c,a,p] Save or restore time (modification, creation, access, preserve)" +#define MCHelpSwU L"\n u Update files" +#define MCHelpSwV L"\n v Create volumes with size autodetection or list all volumes" +#define MCHelpSwVUnr L"\n v List all volumes" +#define MCHelpSwVn L"\n v<size>[k,b] Create volumes with size=<size>*1000 [*1024, *1]" +#define MCHelpSwVD L"\n vd Erase disk contents before creating volume" +#define MCHelpSwVER L"\n ver[n] File version control" +#define MCHelpSwVN L"\n vn Use the old style volume naming scheme" +#define MCHelpSwVP L"\n vp Pause before each volume" +#define MCHelpSwW L"\n w<path> Assign work directory" +#define MCHelpSwX L"\n x<file> Exclude specified file" +#define MCHelpSwXa L"\n x@ Read file names to exclude from stdin" +#define MCHelpSwXal L"\n x@<list> Exclude files listed in specified list file" +#define MCHelpSwY L"\n y Assume Yes on all queries" +#define MCHelpSwZ L"\n z[file] Read archive comment from file" +#define MBadArc L"\nERROR: Bad archive %s\n" +#define MAskPsw L"Enter password (will not be echoed)" +#define MAskPswFor L"\nEnter password (will not be echoed) for %s: " +#define MReAskPsw L"\nReenter password: " +#define MNotMatchPsw L"\nERROR: Passwords do not match\n" +#define MErrWrite L"Write error in the file %s" +#define MErrRead L"Read error in the file %s" +#define MErrSeek L"Seek error in the file %s" +#define MErrFClose L"Cannot close the file %s" +#define MErrOutMem L"Not enough memory" +#define MErrBrokenArc L"Corrupt archive - use 'Repair' command" +#define MProgAborted L"Program aborted" +#define MErrRename L"\nCannot rename %s to %s" +#define MAbsNextVol L"\nCannot find volume %s" +#define MBreak L"\nUser break\n" +#define MAskCreatVol L"\nCreate next volume ?" +#define MAskNextDisk L"\nDisk full. Insert next" +#define MCreatVol L"\n\nCreating %sarchive %s\n" +#define MAskNextVol L"\nInsert disk with %s" +#define MTestVol L"\n\nTesting archive %s\n" +#define MExtrVol L"\n\nExtracting from %s\n" +#define MConverting L"\nConverting %s" +#define MCvtToSFX L"\nConvert archives to SFX" +#define MCvtFromSFX L"\nRemoving SFX module" +#define MNotSFX L"\n%s is not SFX archive" +#define MNotRAR L"\n%s is not RAR archive" +#define MNotFirstVol L"\n%s is not the first volume" +#define MCvtOldFormat L"\n%s - cannot convert to SFX archive with old format" +#define MCannotCreate L"\nCannot create %s" +#define MCannotOpen L"\nCannot open %s" +#define MUnknownMeth L"\nUnknown method in %s" +#define MNewRarFormat L"\nUnsupported archive format. Please update RAR to a newer version." +#define MOk L" OK" +#define MDone L"\nDone" +#define MLockingArc L"\nLocking archive" +#define MNotMdfOld L"\n\nERROR: Cannot modify old format archive" +#define MNotMdfLock L"\n\nERROR: Locked archive" +#define MNotMdfVol L"\n\nERROR: Cannot modify volume" +#define MPackAskReg L"\nEvaluation copy. Please register.\n" +#define MCreateArchive L"\nCreating %sarchive %s\n" +#define MUpdateArchive L"\nUpdating %sarchive %s\n" +#define MAddSolid L"solid " +#define MAddFile L"\nAdding %-58s " +#define MUpdFile L"\nUpdating %-58s " +#define MAddPoints L"\n... %-58s " +#define MMoveDelFiles L"\n\nDeleting files %s..." +#define MMoveDelDirs L"and directories" +#define MMoveDelFile L"\nDeleting %-30s" +#define MMoveDeleted L" deleted" +#define MMoveNotDeleted L" NOT DELETED" +#define MClearAttrib L"\n\nClearing attributes..." +#define MMoveDelDir L"\nDeleting directory %-30s" +#define MWarErrFOpen L"\nWARNING: Cannot open %d %s" +#define MErrOpenFiles L"files" +#define MErrOpenFile L"file" +#define MAddNoFiles L"\nWARNING: No files" +#define MMdfEncrSol L"\n%s: encrypted" +#define MAddAnalyze L"\nAnalyzing archived files: " +#define MRepacking L"\nRepacking archived files: " +#define MCRCFailed L"\n%-20s - checksum error" +#define MExtrTest L"\n\nTesting archive %s\n" +#define MExtracting L"\n\nExtracting from %s\n" +#define MUseCurPsw L"\n%s - use current password ?" +#define MCreatDir L"\nCreating %-56s" +#define MExtrSkipFile L"\nSkipping %-56s" +#define MExtrTestFile L"\nTesting %-56s" +#define MExtrFile L"\nExtracting %-56s" +#define MExtrPoints L"\n... %-56s" +#define MExtrErrMkDir L"\nCannot create directory %s" +#define MExtrPrinting L"\n------ Printing %s\n\n" +#define MEncrBadCRC L"\nChecksum error in the encrypted file %s. Corrupt file or wrong password." +#define MExtrNoFiles L"\nNo files to extract" +#define MExtrAllOk L"\nAll OK" +#define MExtrTotalErr L"\nTotal errors: %ld" +#define MAskReplace L"\n\nWould you like to replace the existing file %s\n%6s bytes, modified on %s\nwith a new one\n%6s bytes, modified on %s\n" +#define MAskOverwrite L"\nOverwrite %s ?" +#define MAskNewName L"\nEnter new name: " +#define MHeaderBroken L"\nCorrupt header is found" +#define MMainHeaderBroken L"\nMain archive header is corrupt" +#define MLogFileHead L"\n%s - the file header is corrupt" +#define MLogProtectHead L"The data recovery header is corrupt" +#define MReadStdinCmt L"\nReading comment from stdin\n" +#define MReadCommFrom L"\nReading comment from %s" +#define MDelComment L"\nDeleting comment from %s" +#define MAddComment L"\nAdding comment to %s" +#define MFCommAdd L"\nAdding file comments" +#define MAskFComm L"\n\nReading comment for %s : %s from stdin\n" +#define MLogCommBrk L"\nThe archive comment is corrupt" +#define MCommAskCont L"\nPress 'Enter' to continue or 'Q' to quit:" +#define MWriteCommTo L"\nWrite comment to %s" +#define MCommNotPres L"\nComment is not present" +#define MDelFrom L"\nDeleting from %s" +#define MDeleting L"\nDeleting %s" +#define MEraseArc L"\nErasing empty archive %s" +#define MNoDelFiles L"\nNo files to delete" +#define MLogTitle L"-------- %2d %s %d, archive %s" +#define MPathTooLong L"\nERROR: Path too long\n" +#define MListArchive L"Archive" +#define MListDetails L"Details" +#define MListSolid L"solid" +#define MListSFX L"SFX" +#define MListVolume L"volume" +#define MListRR L"recovery record" +#define MListLock L"lock" +#define MListEnc L"encrypted" +#define MListEncHead L"encrypted headers" +#define MListTitleL L" Attributes Size Date Time Name" +#define MListTitleV L" Attributes Size Packed Ratio Date Time Checksum Name" +#define MListName L"Name" +#define MListType L"Type" +#define MListFile L"File" +#define MListDir L"Directory" +#define MListUSymlink L"Unix symbolic link" +#define MListWSymlink L"Windows symbolic link" +#define MListJunction L"NTFS junction point" +#define MListHardlink L"Hard link" +#define MListCopy L"File reference" +#define MListStream L"NTFS alternate data stream" +#define MListTarget L"Target" +#define MListSize L"Size" +#define MListPacked L"Packed size" +#define MListRatio L"Ratio" +#define MListMtime L"mtime" +#define MListCtime L"ctime" +#define MListAtime L"atime" +#define MListModified L"Modified" +#define MListCreated L"Created" +#define MListAccessed L"Accessed" +#define MListAttr L"Attributes" +#define MListFlags L"Flags" +#define MListCompInfo L"Compression" +#define MListHostOS L"Host OS" +#define MListFileVer L"File version" +#define MListService L"Service" +#define MListUOHead L"\n Unix Owner/Group data: %-14s %-14s" +#define MListNTACLHead L"\n NTFS security data" +#define MListStrmHead L"\n NTFS stream: %s" +#define MListUnkHead L"\n Unknown subheader type: 0x%04x" +#define MFileComment L"\nComment: " +#define MYes L"Yes" +#define MNo L"No" +#define MListNoFiles L" 0 files\n" +#define MRprReconstr L"\nReconstructing %s" +#define MRprBuild L"\nBuilding %s" +#define MRprOldFormat L"\nCannot repair archive with old format" +#define MRprFind L"\nFound %s" +#define MRprAskIsSol L"\nThe archive header is corrupt. Mark archive as solid ?" +#define MRprNoFiles L"\nNo files found" +#define MLogUnexpEOF L"\nUnexpected end of archive" +#define MRepAskReconst L"\nReconstruct archive structure ?" +#define MRRSearch L"\nSearching for recovery record" +#define MAnalyzeFileData L"\nAnalyzing file data" +#define MRecRNotFound L"\nData recovery record not found" +#define MRecRFound L"\nData recovery record found" +#define MRecSecDamage L"\nSector %ld (offsets %lX...%lX) damaged" +#define MRecCorrected L" - data recovered" +#define MRecFailed L" - cannot recover data" +#define MAddRecRec L"\nAdding data recovery record" +#define MEraseForVolume L"\n\nErasing contents of drive %c:\n" +#define MGetOwnersError L"\nWARNING: Cannot get %s owner and group\n" +#define MErrGetOwnerID L"\nWARNING: Cannot get owner %s ID\n" +#define MErrGetGroupID L"\nWARNING: Cannot get group %s ID\n" +#define MOwnersBroken L"\nERROR: %s group and owner data are corrupt\n" +#define MSetOwnersError L"\nWARNING: Cannot set %s owner and group\n" +#define MErrLnkRead L"\nWARNING: Cannot read symbolic link %s" +#define MSymLinkExists L"\nWARNING: Symbolic link %s already exists" +#define MAskRetryCreate L"\nCannot create %s. Retry ?" +#define MDataBadCRC L"\n%-20s : packed data checksum error in volume %s" +#define MFileRO L"\n%s is read-only" +#define MACLGetError L"\nWARNING: Cannot get %s security data\n" +#define MACLSetError L"\nWARNING: Cannot set %s security data\n" +#define MACLBroken L"\nERROR: %s security data are corrupt\n" +#define MACLUnknown L"\nWARNING: Unknown format of %s security data\n" +#define MStreamBroken L"\nERROR: %s stream data are corrupt\n" +#define MStreamUnknown L"\nWARNING: Unknown format of %s stream data\n" +#define MInvalidName L"\nERROR: Invalid file name %s" +#define MProcessArc L"\n\nProcessing archive %s" +#define MCorrectingName L"\nWARNING: Attempting to correct the invalid file or directory name" +#define MUnpCannotMerge L"\nWARNING: You need to start extraction from a previous volume to unpack %s" +#define MUnknownOption L"\nERROR: Unknown option: %s" +#define MSubHeadCorrupt L"\nERROR: Corrupt data header found, ignored" +#define MSubHeadUnknown L"\nWARNING: Unknown data header format, ignored" +#define MSubHeadDataCRC L"\nERROR: Corrupt %s data block" +#define MSubHeadType L"\nData header type: %s" +#define MScanError L"\nCannot read contents of %s" +#define MNotVolume L"\n%s is not volume" +#define MRecVolDiffSets L"\nERROR: %s and %s belong to different sets" +#define MRecVolMissing L"\n%d volumes missing" +#define MRecVolFound L"\n%d recovery volumes found" +#define MRecVolAllExist L"\nNothing to reconstruct" +#define MRecVolCannotFix L"\nReconstruction impossible" +#define MReconstructing L"\nReconstructing..." +#define MCreating L"\nCreating %s" +#define MRenaming L"\nRenaming %s to %s" +#define MNTFSRequired L"\nWrite error: only NTFS file system supports files larger than 4 GB" +#define MFAT32Size L"\nWARNING: FAT32 file system does not support 4 GB or larger files" +#define MErrChangeAttr L"\nWARNING: Cannot change attributes of %s" +#define MWrongSFXVer L"\nERROR: default SFX module does not support RAR %d.%d archives" +#define MHeadEncMismatch L"\nCannot change the header encryption mode in already encrypted archive" +#define MCannotEmail L"\nCannot email the file %s" +#define MCopyrightS L"\nRAR SFX archive" +#define MSHelpCmd L"\n\n<Commands>" +#define MSHelpCmdE L"\n -x Extract from archive (default)" +#define MSHelpCmdT L"\n -t Test archive files" +#define MSHelpCmdV L"\n -v Verbosely list contents of archive" +#define MRecVolLimit L"\nTotal number of usual and recovery volumes must not exceed %d" +#define MVolumeNumber L"volume %d" +#define MCannotDelete L"\nCannot delete %s" +#define MRecycleFailed L"\nCannot move some files and directories to Recycle Bin" +#define MCalcCRC L"\nCalculating the checksum" +#define MTooLargeSFXArc L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB." +#define MCalcCRCAllVol L"\nCalculating checksums of all volumes." +#define MNotEnoughDisk L"\nERROR: Not enough disk space for %s." +#define MNewerRAR L"\nYou may need a newer version of RAR." +#define MUnkEncMethod L"\nUnknown encryption method in %s" +#define MWrongPassword L"\nThe specified password is incorrect." +#define MWrongFilePassword L"\nIncorrect password for %s" +#define MAreaDamaged L"\nCorrupt %d bytes at %08x %08x" +#define MBlocksRecovered L"\n%u blocks are recovered, %u blocks are relocated" +#define MRRDamaged L"\nRecovery record is corrupt." +#define MTestingRR L"\nTesting the recovery record" +#define MFailed L"Failed" +#define MIncompatSwitch L"\n%s switch is not supported for RAR %d.x archive format." +#define MSearchDupFiles L"\nSearching for identical files" +#define MNumFound L"%d found." +#define MUnknownExtra L"\nUnknown extra field in %s." +#define MCorruptExtra L"\nCorrupt %s extra field in %s." +#define MCopyError L"\nCannot copy %s to %s." +#define MCopyErrorHint L"\nYou need to unpack the entire archive to create file reference entries." +#define MCopyingData L"\nCopying data" +#define MErrCreateLnkS L"\nCannot create symbolic link %s" +#define MErrCreateLnkH L"\nCannot create hard link %s" +#define MErrLnkTarget L"\nYou need to unpack the link target first" +#define MNeedAdmin L"\nYou may need to run RAR as administrator" +#define MDictOutMem L"\nNot enough memory for %d MB compression dictionary, changed to %d MB." +#define MUseSmalllerDict L"\nPlease use a smaller compression dictionary." +#define MOpenErrAtime L"\nYou may need to remove -tsp switch to open this file." +#define MErrReadInfo L"\nChoose 'Ignore' to continue with the already read file part only, 'Ignore all' to do it for all read errors, 'Retry' to repeat read and 'Quit' to abort." +#define MErrReadTrunc L"\n%s is archived incompletely because of read error.\n" +#define MErrReadCount L"\n%u files are archived incompletely because of read errors." +#define MDirNameExists L"\nDirectory with such name already exists" +#define MStdinNoInput L"\nKeyboard input is not allowed when reading data from stdin" +#define MTruncPsw L"\nPassword exceeds the maximum allowed length of %u characters and will be truncated." +#define MAdjustValue L"\nAdjusting %s value to %s." +#define MOpFailed L"\nOperation failed" +#define MSkipEncArc L"\nSkipping the encrypted archive %s" +#define MOrigName L"Original name" +#define MOriginalTime L"Original time" +#define MFileRenamed L"\n%s is renamed to %s" diff --git a/unrar/unrar/log.cpp b/unrar/unrar/log.cpp index 966e9de..8bbe8ee 100644 --- a/unrar/unrar/log.cpp +++ b/unrar/unrar/log.cpp @@ -1,23 +1,36 @@ #include "rar.hpp" -static char LogName[NM]; +static wchar LogName[NM]; +static RAR_CHARSET LogCharset=RCH_DEFAULT; -void InitLogOptions(char *LogName) +void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet) { - strcpy(::LogName,LogName); + wcsncpyz(LogName,LogFileName,ASIZE(LogName)); + LogCharset=CSet; } #ifndef SILENT -void Log(const char *ArcName,const char *Format,...) +void Log(const wchar *ArcName,const wchar *fmt,...) { - safebuf char Msg[2*NM+1024]; - va_list ArgPtr; - va_start(ArgPtr,Format); - vsprintf(Msg,Format,ArgPtr); - va_end(ArgPtr); - eprintf("%s",Msg); + // Preserve the error code for possible following system error message. + int Code=ErrHandler.GetSystemErrorCode(); + + uiAlarm(UIALARM_ERROR); + + // This buffer is for format string only, not for entire output, + // so it can be short enough. + wchar fmtw[1024]; + PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); + + safebuf wchar Msg[2*NM+1024]; + va_list arglist; + va_start(arglist,fmt); + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + va_end(arglist); + eprintf(L"%ls",Msg); + ErrHandler.SetSystemErrorCode(Code); } #endif diff --git a/unrar/unrar/log.hpp b/unrar/unrar/log.hpp index 52d6b8d..008ef11 100644 --- a/unrar/unrar/log.hpp +++ b/unrar/unrar/log.hpp @@ -1,18 +1,12 @@ #ifndef _RAR_LOG_ #define _RAR_LOG_ -void InitLogOptions(char *LogName); - -#ifndef SILENT -void Log(const char *ArcName,const char *Format,...); -#endif +void InitLogOptions(const wchar *LogFileName,RAR_CHARSET CSet); #ifdef SILENT -#ifdef __GNUC__ -#define Log(args...) +inline void Log(const wchar *ArcName,const wchar *fmt,...) {} #else -inline void Log(const char *a,const char *b,const char *c=NULL,const char *d=NULL) {} -#endif +void Log(const wchar *ArcName,const wchar *fmt,...); #endif #endif diff --git a/unrar/unrar/makefile.unix b/unrar/unrar/makefile index d22ed58..55af49b 100644 --- a/unrar/unrar/makefile.unix +++ b/unrar/unrar/makefile @@ -1,15 +1,14 @@ # # Makefile for UNIX - unrar -# -# Note: you have to 'make clean' before you can build -# the sfx module -# # Linux using GCC -CXX=g++ -CXXFLAGS=-O2 -DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +CXX=c++ +CXXFLAGS=-O2 -Wno-logical-op-parentheses -Wno-switch -Wno-dangling-else +LIBFLAGS=-fPIC +DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP STRIP=strip +AR=ar +LDFLAGS=-pthread DESTDIR=/usr # Linux using LCC @@ -17,6 +16,17 @@ DESTDIR=/usr #CXXFLAGS=-O2 #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=strip +#AR=ar +#DESTDIR=/usr + +# CYGWIN using GCC +#CXX=c++ +#CXXFLAGS=-O2 +#LIBFLAGS= +#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DRAR_SMP +#STRIP=strip +#AR=ar +#LDFLAGS=-pthread #DESTDIR=/usr # HP UX using aCC @@ -24,6 +34,7 @@ DESTDIR=/usr #CXXFLAGS=-AA +O2 +Onolimit #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=strip +#AR=ar #DESTDIR=/usr # IRIX using GCC @@ -31,13 +42,15 @@ DESTDIR=/usr #CXXFLAGS=-O2 #DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1 #STRIP=strip +#AR=ar #DESTDIR=/usr # IRIX using MIPSPro (experimental) #CXX=CC #CXXFLAGS=-O2 -mips3 -woff 1234,1156,3284 -LANG:std -#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -DNATIVE_INT64 -DInt64=int64_t +#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_BSD_COMPAT -Dint64=int64_t #STRIP=strip +#AR=ar #DESTDIR=/usr # AIX using xlC (IBM VisualAge C++ 5.0) @@ -46,13 +59,15 @@ DESTDIR=/usr #DEFINES=-D_LARGE_FILES -D_LARGE_FILE_API #LIBS=-lbsd #STRIP=strip +#AR=ar #DESTDIR=/usr -# Solaris using CC (SUN Forte Developer 7 C++) +# Solaris using CC #CXX=CC -#CXXFLAGS=-xO2 -xbuiltin=%all -xinline=%auto -#DEFINES=-DBIG_ENDIAN -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +#CXXFLAGS=-fast -erroff=wvarhidemem +#DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=strip +#AR=ar #DESTDIR=/usr # Solaris using GCC (optimized for UltraSPARC 1 CPU) @@ -60,19 +75,22 @@ DESTDIR=/usr #CXXFLAGS=-O3 -mcpu=v9 -mtune=ultrasparc -m32 #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=/usr/ccs/bin/strip +#AR=/usr/ccs/bin/ar #DESTDIR=/usr # Tru64 5.1B using GCC3 #CXX=g++ #CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_XOPEN_SOURCE=500 #STRIP=strip +#AR=ar #LDFLAGS=-rpath /usr/local/gcc/lib #DESTDIR=/usr # Tru64 5.1B using DEC C++ #CXX=cxx -#CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DNATIVE_INT64 -DInt64=long +#CXXFLAGS=-O4 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -Dint64=long #STRIP=strip +#AR=ar #LDFLAGS= #DESTDIR=/usr @@ -80,6 +98,7 @@ DESTDIR=/usr #CXX=g++ #CXXFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fexceptions #STRIP=strip +#AR=ar #LDFLAGS=-fexceptions #DESTDIR=/usr @@ -89,21 +108,25 @@ DESTDIR=/usr #CXXFLAGS=-O2 #DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE #STRIP=arm-linux-strip +#AR=arm-linux-ar #LDFLAGS=-static #DESTDIR=/usr ########################## -COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES) +COMPILE=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) LINK=$(CXX) -UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o -LIB_OBJ=filestr.o scantree.o dll.o +WHAT=UNRAR -OBJECTS=rar.o strlist.o strfn.o pathfn.o int64.o savepos.o global.o file.o filefn.o filcreat.o \ - archive.o arcread.o unicode.o system.o isnt.o crypt.o crc.o rawread.o encname.o \ - resource.o match.o timefn.o rdwrfn.o consio.o options.o ulinks.o errhnd.o rarvm.o \ - rijndael.o getbits.o sha1.o extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o +UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o qopen.o +LIB_OBJ=filestr.o scantree.o dll.o qopen.o + +OBJECTS=rar.o strlist.o strfn.o pathfn.o smallfn.o global.o file.o filefn.o filcreat.o \ + archive.o arcread.o unicode.o system.o crypt.o crc.o rawread.o encname.o \ + resource.o match.o timefn.o rdwrfn.o consio.o options.o errhnd.o rarvm.o secpassword.o \ + rijndael.o getbits.o sha1.o sha256.o blake2s.o hash.o extinfo.o extract.o volume.o \ + list.o find.o unpack.o headers.o threadpool.o rs16.o cmddata.o ui.o .cpp.o: $(COMPILE) -D$(WHAT) -c $< @@ -115,9 +138,13 @@ install: install-unrar uninstall: uninstall-unrar clean: - @rm -f *.o *.bak *~ + @rm -f *.bak *~ + @rm -f $(OBJECTS) $(UNRAR_OBJ) $(LIB_OBJ) + @rm -f unrar libunrar.* + +# We removed 'clean' from dependencies, because it prevented parallel +# 'make -Jn' builds. -unrar: WHAT=UNRAR unrar: $(OBJECTS) $(UNRAR_OBJ) @rm -f unrar $(LINK) -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) @@ -130,18 +157,21 @@ sfx: $(OBJECTS) $(STRIP) default.sfx lib: WHAT=RARDLL +lib: CXXFLAGS+=$(LIBFLAGS) lib: $(OBJECTS) $(LIB_OBJ) - @rm -f libunrar.so + @rm -f libunrar.* $(LINK) -shared -o libunrar.so $(LDFLAGS) $(OBJECTS) $(LIB_OBJ) + $(AR) rcs libunrar.a $(OBJECTS) $(LIB_OBJ) install-unrar: - install unrar $(DESTDIR)/bin + install -D unrar $(DESTDIR)/bin/unrar uninstall-unrar: rm -f $(DESTDIR)/bin/unrar install-lib: install libunrar.so $(DESTDIR)/lib + install libunrar.a $(DESTDIR)/lib uninstall-lib: rm -f $(DESTDIR)/lib/libunrar.so diff --git a/unrar/unrar/makefile.bcc b/unrar/unrar/makefile.bcc deleted file mode 100644 index 7b833c6..0000000 --- a/unrar/unrar/makefile.bcc +++ /dev/null @@ -1,501 +0,0 @@ -.AUTODEPEND
-
-basepath = $(BASEPATHCC)
-binpath = $(basepath)\bin
-libpath = $(basepath)\lib
-rarpath = .
-incpath = $(basepath)\include;$(rarpath)
-
-cc = $(binpath)\bcc32
-link = $(binpath)\ilink32
-
-objpath = .
-guiopt = -WC -H=$(objpath)\rar.csm
-
-!ifndef RARDLL
-!ifndef GUI
-guiopt=$(guiopt) -x-
-!endif
-!ifdef SFX_MODULE
-guiopt=$(guiopt) -x-
-!endif
-!endif
-
-!ifdef DEBUG
-optdeb=-Od -k -vi- -DDEBUG
-!else
-# -O is not safe to use with -pr and int64 return values, so let's turn it off
-optdeb=-O1 -O- -k-
-#optdeb=-Ob -Oe -Og -Oi -Ol -Om -Op -OS -Ov -Z -Oc
-!endif
-
-
-optunrar=-DUNRAR
-linkdest=unrar.exe
-
-!ifdef SFX_MODULE
-optunrar=-DUNRAR -DSFX_MODULE
-linkdest=sfx.exe
-!endif
-
-linkopt = -L$(libpath) -ap -c -v -s -V4.0 -Gn
-compopt = -P -c -I$(incpath) -R -v -vi -w-pch -w-par -K -f-\
- -ff- -a4 -pr -RT- $(optdeb) $(guiopt) $(optunrar) -d -w-8072
-
-!ifdef RARDLL
-SILENT=true
-linkdest=unrar.dll
-linkopt=$(linkopt) -Tpd
-compopt=$(compopt) -DRARDLL
-!else
-linkopt=$(linkopt) -Tpe -B:0x400000
-!endif
-
-!ifdef SILENT
-compopt=$(compopt) -DSILENT
-!endif
-
-
-rar: $(linkdest)
-
-Dep_SFX= \
- $(objpath)\strlist.obj\
- $(objpath)\strfn.obj\
- $(objpath)\pathfn.obj\
- $(objpath)\cmddata.obj\
- $(objpath)\consio.obj\
- $(objpath)\int64.obj\
- $(objpath)\savepos.obj\
- $(objpath)\file.obj\
- $(objpath)\filefn.obj\
- $(objpath)\filcreat.obj\
- $(objpath)\sha1.obj\
- $(objpath)\archive.obj\
- $(objpath)\arcread.obj\
- $(objpath)\unicode.obj\
- $(objpath)\system.obj\
- $(objpath)\isnt.obj\
- $(objpath)\crc.obj\
- $(objpath)\crypt.obj\
- $(objpath)\rijndael.obj\
- $(objpath)\rawread.obj\
- $(objpath)\encname.obj\
- $(objpath)\resource.obj\
- $(objpath)\match.obj\
- $(objpath)\find.obj\
- $(objpath)\timefn.obj\
- $(objpath)\getbits.obj\
- $(objpath)\rarvm.obj\
- $(objpath)\rdwrfn.obj\
- $(objpath)\options.obj\
- $(objpath)\ulinks.obj\
- $(objpath)\errhnd.obj\
- $(objpath)\volume.obj\
- $(objpath)\rs.obj\
- $(objpath)\recvol.obj\
- $(objpath)\extinfo.obj\
- $(objpath)\extract.obj\
- $(objpath)\unpack.obj\
- $(objpath)\rar.obj\
- $(objpath)\global.obj
-
-Dep_Unrar = \
- $(objpath)\filestr.obj\
- $(objpath)\scantree.obj
-
-Dep_Dll = \
- $(objpath)\dll.obj
-
-#Dep_SFXOnly = $(objpath)\rtl.obj
-
-!ifndef GUI
-!ifndef SILENT
-Dep_Console = \
- $(objpath)\list.obj
-!endif
-!endif
-
-!ifdef SFX_MODULE
-Dep = $(Dep_SFX) $(Dep_SFXOnly)
-!else
-Dep = $(Dep_SFX) $(Dep_Unrar)
-!endif
-
-!ifndef GUI
-Dep = $(Dep) $(Dep_Console)
-!endif
-
-!ifdef RARDLL
-Dep = $(Dep) $(Dep_Dll)
-!endif
-
-!ifdef GUI
-$(linkdest) : $(Dep)
- echo Done
-!else
-$(linkdest) : $(Dep)
- $(link) @&&|
- $(linkopt) +
-#!ifdef SFX_MODULE
-#$(objpath)\dummy.obj+
-#$(objpath)\ll.obj+
-#$(objpath)\rtl.obj+
-#!else
-!ifdef RARDLL
-$(libpath)\c0d32.obj+
-!else
-$(libpath)\c0x32.obj+
-!endif
-#!endif
-$(objpath)\strlist.obj+
-$(objpath)\strfn.obj+
-$(objpath)\pathfn.obj+
-$(objpath)\int64.obj+
-$(objpath)\savepos.obj+
-$(objpath)\global.obj+
-$(objpath)\file.obj+
-$(objpath)\filefn.obj+
-$(objpath)\filcreat.obj+
-$(objpath)\sha1.obj+
-$(objpath)\archive.obj+
-$(objpath)\arcread.obj+
-$(objpath)\unicode.obj+
-$(objpath)\system.obj+
-$(objpath)\isnt.obj+
-$(objpath)\crc.obj+
-$(objpath)\crypt.obj+
-$(objpath)\rijndael.obj+
-$(objpath)\rawread.obj+
-$(objpath)\encname.obj+
-$(objpath)\resource.obj+
-$(objpath)\match.obj+
-$(objpath)\find.obj+
-!ifndef SFX_MODULE
-$(objpath)\filestr.obj+
-$(objpath)\scantree.obj+
-!endif
-$(objpath)\timefn.obj+
-$(objpath)\getbits.obj+
-$(objpath)\rarvm.obj+
-$(objpath)\rdwrfn.obj+
-$(objpath)\consio.obj+
-$(objpath)\cmddata.obj+
-$(objpath)\options.obj+
-$(objpath)\ulinks.obj+
-$(objpath)\volume.obj+
-$(objpath)\extinfo.obj+
-$(objpath)\extract.obj+
-$(objpath)\rs.obj+
-$(objpath)\recvol.obj+
-!ifndef SILENT
-!ifndef GUI
-$(objpath)\list.obj+
-!endif
-!endif
-!ifdef RARDLL
-$(objpath)\dll.obj+
-!endif
-$(objpath)\errhnd.obj+
-$(objpath)\unpack.obj+
-$(objpath)\rar.obj
-$<,$*
-$(libpath)\cw32.lib+
-$(libpath)\import32.lib
-!ifdef RARDLL
-$(rarpath)\dll.def
-!else
-
-!endif
-|
-!endif
-
-$(objpath)\rar.obj : $(rarpath)\rar.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rar.cpp
-|
-
-$(objpath)\strlist.obj : $(rarpath)\strlist.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\strlist.cpp
-|
-
-$(objpath)\strfn.obj : $(rarpath)\strfn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\strfn.cpp
-|
-
-$(objpath)\pathfn.obj : $(rarpath)\pathfn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\pathfn.cpp
-|
-
-$(objpath)\int64.obj : $(rarpath)\int64.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\int64.cpp
-|
-
-$(objpath)\savepos.obj : $(rarpath)\savepos.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\savepos.cpp
-|
-
-$(objpath)\global.obj : $(rarpath)\global.cpp
- $(cc) -q @&&|
- $(compopt) -H- -o$@ $(rarpath)\global.cpp
-|
-
-$(objpath)\file.obj : $(rarpath)\file.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\file.cpp
-|
-
-$(objpath)\filefn.obj : $(rarpath)\filefn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\filefn.cpp
-|
-
-$(objpath)\filestr.obj : $(rarpath)\filestr.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\filestr.cpp
-|
-
-$(objpath)\filcreat.obj : $(rarpath)\filcreat.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\filcreat.cpp
-|
-
-$(objpath)\sha1.obj : $(rarpath)\sha1.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\sha1.cpp
-|
-
-$(objpath)\ec.obj : $(rarpath)\ec.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\ec.cpp
-|
-
-$(objpath)\av.obj : $(rarpath)\av.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\av.cpp
-|
-
-$(objpath)\archive.obj : $(rarpath)\archive.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\archive.cpp
-|
-
-$(objpath)\arcread.obj : $(rarpath)\arcread.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\arcread.cpp
-|
-
-$(objpath)\unicode.obj : $(rarpath)\unicode.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\unicode.cpp
-|
-
-$(objpath)\system.obj : $(rarpath)\system.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\system.cpp
-|
-
-$(objpath)\isnt.obj : $(rarpath)\isnt.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\isnt.cpp
-|
-
-$(objpath)\crc.obj : $(rarpath)\crc.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\crc.cpp
-|
-
-$(objpath)\crypt.obj : $(rarpath)\crypt.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\crypt.cpp
-|
-
-$(objpath)\rijndael.obj : $(rarpath)\rijndael.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rijndael.cpp
-|
-
-$(objpath)\rawread.obj : $(rarpath)\rawread.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rawread.cpp
-|
-
-$(objpath)\rawwrite.obj : $(rarpath)\rawwrite.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rawwrite.cpp
-|
-
-$(objpath)\encname.obj : $(rarpath)\encname.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\encname.cpp
-|
-
-$(objpath)\resource.obj : $(rarpath)\resource.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\resource.cpp
-|
-
-$(objpath)\match.obj : $(rarpath)\match.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\match.cpp
-|
-
-$(objpath)\find.obj : $(rarpath)\find.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\find.cpp
-|
-
-$(objpath)\scantree.obj : $(rarpath)\scantree.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\scantree.cpp
-|
-
-$(objpath)\timefn.obj : $(rarpath)\timefn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\timefn.cpp
-|
-
-$(objpath)\getbits.obj : $(rarpath)\getbits.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\getbits.cpp
-|
-
-$(objpath)\rarvm.obj : $(rarpath)\rarvm.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rarvm.cpp
-|
-
-$(objpath)\putbits.obj : $(rarpath)\putbits.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\putbits.cpp
-|
-
-$(objpath)\pack.obj : $(rarpath)\pack.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\pack.cpp
-|
-
-$(objpath)\packbord.obj : $(rarpath)\packbord.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\packbord.cpp
-|
-
-$(objpath)\packanlz.obj : $(rarpath)\packanlz.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\packanlz.cpp
-|
-
-$(objpath)\cblock.obj : $(rarpath)\cblock.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\cblock.cpp
-|
-
-$(objpath)\add.obj : $(rarpath)\add.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\add.cpp
-|
-
-$(objpath)\addlist.obj : $(rarpath)\addlist.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\addlist.cpp
-|
-
-$(objpath)\procarc.obj : $(rarpath)\procarc.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\procarc.cpp
-|
-
-$(objpath)\sfx.obj : $(rarpath)\sfx.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\sfx.cpp
-|
-
-$(objpath)\comment.obj : $(rarpath)\comment.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\comment.cpp
-|
-
-$(objpath)\rs.obj : $(rarpath)\rs.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rs.cpp
-|
-
-$(objpath)\recvol.obj : $(rarpath)\recvol.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\recvol.cpp
-|
-
-$(objpath)\repair.obj : $(rarpath)\repair.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\repair.cpp
-|
-
-$(objpath)\rdwrfn.obj : $(rarpath)\rdwrfn.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rdwrfn.cpp
-|
-
-$(objpath)\consio.obj : $(rarpath)\consio.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\consio.cpp
-|
-
-$(objpath)\cmddata.obj : $(rarpath)\cmddata.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\cmddata.cpp
-|
-
-$(objpath)\options.obj : $(rarpath)\options.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\options.cpp
-|
-
-$(objpath)\ulinks.obj : $(rarpath)\ulinks.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\ulinks.cpp
-|
-
-$(objpath)\errhnd.obj : $(rarpath)\errhnd.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\errhnd.cpp
-|
-
-$(objpath)\volume.obj : $(rarpath)\volume.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\volume.cpp
-|
-
-$(objpath)\extinfo.obj : $(rarpath)\extinfo.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\extinfo.cpp
-|
-
-
-$(objpath)\extract.obj : $(rarpath)\extract.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\extract.cpp
-|
-
-$(objpath)\list.obj : $(rarpath)\list.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\list.cpp
-|
-
-$(objpath)\rtl.obj : $(rarpath)\rtl.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\rtl.cpp
-|
-
-$(objpath)\unpack.obj : $(rarpath)\unpack.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\unpack.cpp
-|
-
-$(objpath)\dll.obj : $(rarpath)\dll.cpp
- $(cc) -q @&&|
- $(compopt) -o$@ $(rarpath)\dll.cpp
-|
\ No newline at end of file diff --git a/unrar/unrar/makefile.cygmin b/unrar/unrar/makefile.cygmin deleted file mode 100644 index 9e0cfa8..0000000 --- a/unrar/unrar/makefile.cygmin +++ /dev/null @@ -1,54 +0,0 @@ -# -# Makefile for cygmin/mingw - unrar -# -# Note: you have to 'make clean' before you can build -# the sfx module -# - -# POSIX using Cygmin GCC 3.3.1 -#CXX = g++ -#CXXFLAGS = -O2 -Wno-deprecated -#DEFINES = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DLITTLE_ENDIAN - -# Win32 using Cygmin GCC 3.3.1 -CXX = g++ -mno-cygwin -CXXFLAGS = -O2 -Wno-deprecated -DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE - -# Win32 using Mingw32 GCC 3.3.2 -#CXX = g++ -#CXXFLAGS = -O2 -Wno-deprecated -#DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE - -########################## - -COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES) -LINK=$(CXX) - -UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o - -OBJECTS=rar.o strlist.o strfn.o pathfn.o int64.o savepos.o global.o \ - file.o filefn.o filcreat.o archive.o arcread.o unicode.o \ - system.o isnt.o crypt.o crc.o rawread.o encname.o \ - resource.o match.o timefn.o rdwrfn.o consio.o options.o \ - ulinks.o errhnd.o rarvm.o rijndael.o getbits.o sha1.o \ - extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o - -.cpp.o: - $(COMPILE) -D$(WHAT) -c $< - -all: unrar - -clean: - @rm -f *.o *.bak *~ - -unrar: WHAT=UNRAR -unrar: $(OBJECTS) $(UNRAR_OBJ) - @rm -f makeunrar - $(LINK) -Wl,-s -o unrar $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS) - -sfx: WHAT=SFX_MODULE -sfx: $(OBJECTS) - @rm -f default.sfx - $(LINK) -Wl,-s -o default.sfx $(LDFLAGS) $(OBJECTS) -DSFX_MODULE - diff --git a/unrar/unrar/makefile.dj b/unrar/unrar/makefile.dj deleted file mode 100644 index 4d9dae5..0000000 --- a/unrar/unrar/makefile.dj +++ /dev/null @@ -1,50 +0,0 @@ -#
-# Makefile for DJGPP - unrar
-#
-# Note: you have to 'make clean' before you can build
-# the sfx module
-#
-
-# DOS32 using DJGPP 2.03 Patchlevel 2 and GCC 2.95.3
-CXX = gpp
-CXXFLAGS = -O2 -Wno-deprecated
-#DEFINES = -D_MSC_VER -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
-
-##########################
-
-.PHONY: all clean veryclean
-
-COMPILE=$(CXX) $(CXXFLAGS) $(DEFINES)
-LINK=$(CXX)
-
-UNRAR_OBJ=filestr.o recvol.o rs.o scantree.o
-
-OBJECTS=rar.o strlist.o strfn.o pathfn.o int64.o savepos.o global.o \
- file.o filefn.o filcreat.o archive.o arcread.o unicode.o \
- system.o isnt.o crypt.o crc.o rawread.o encname.o \
- resource.o match.o timefn.o rdwrfn.o consio.o options.o \
- ulinks.o errhnd.o rarvm.o rijndael.o getbits.o sha1.o \
- extinfo.o extract.o volume.o list.o find.o unpack.o cmddata.o
-
-.cpp.o:
- $(COMPILE) -D$(WHAT) -c $<
-
-all: unrar
-
-unrar: WHAT=UNRAR
-unrar: $(OBJECTS) $(UNRAR_OBJ)
- $(LINK) -Wl,-s -o unrar.exe $(LDFLAGS) $(OBJECTS) $(UNRAR_OBJ) $(LIBS)
- exe2coff unrar.exe
- cp -u $(DJDIR)/bin/cwsdstub.exe .
- copy /b cwsdstub.exe+unrar unrar.exe
- -upx --ultra-brute unrar.exe
-
-sfx: WHAT=SFX_MODULE
-sfx: $(OBJECTS)
- $(LINK) -Wl,-s -o default.sfx $(LDFLAGS) $(OBJECTS) -DSFX_MODULE
-
-clean:
- $(RM) $(OBJECTS) $(UNRAR_OBJ)
-
-veryclean: clean
- $(RM) unrar.exe default.sfx cwsdstub.exe unrar
diff --git a/unrar/unrar/makefile.dmc b/unrar/unrar/makefile.dmc deleted file mode 100644 index f0e1198..0000000 --- a/unrar/unrar/makefile.dmc +++ /dev/null @@ -1,54 +0,0 @@ -# Makefile for Digital Mars C++ Compiler
-# http://www.rarlab.com
-# http://www.digitalmars.com
-#
-# DEFINES: UNRAR RARDLL GUI SFX_MODULE SILENT
-
-NAME = unrar
-EXT = exe
-
-CPP = dmc
-
-LINK = link
-
-# --------------
-# Release Build
-# --------------
-DEFINES = -DNDEBUG -D_MSC_VER -DUNRAR
-CPPFLAGS = -o+all -ff -Nc -g- -Ae
-LNKFLAGS = /EXETYPE:NT /MACHINE:i386 /SUBSYSTEM:CONSOLE /NOLOGO /NODEBUG /NOCODEVIEW /PACKFUNCTIONS
-
-# --------------
-# Debug Build
-# --------------
-#DEFINES = -D_DEBUG -D_MSC_VER -DUNRAR
-#CPPFLAGS = -o+none -Nc -S -gf -Ae
-#LNKFLAGS = /EXETYPE:NT /MACHINE:i386 /SUBSYSTEM:CONSOLE /NOLOGO /DEBUG
-
-OBJ = rar.obj strlist.obj strfn.obj pathfn.obj int64.obj savepos.obj global.obj \
- file.obj filefn.obj filcreat.obj archive.obj arcread.obj unicode.obj \
- system.obj isnt.obj crypt.obj crc.obj rawread.obj encname.obj \
- resource.obj match.obj timefn.obj rdwrfn.obj consio.obj options.obj \
- ulinks.obj errhnd.obj rarvm.obj rijndael.obj getbits.obj sha1.obj \
- extinfo.obj extract.obj volume.obj find.obj unpack.obj cmddata.obj \
- filestr.obj recvol.obj rs.obj scantree.obj \
- list.obj \
-# dll.obj \
-
-LIB = kernel32.lib+user32.lib+advapi32.lib
-
-#DEF = dll.def
-
-link: $(OBJ)
- $(LINK) $(LNKFLAGS) $(OBJ), $(NAME).$(EXT), $(NAME).map, $(LIB), $(DEF)
-
-.c.obj:
- $(CPP) $(CPPFLAGS) $(DEFINES) -c $< -o $@
-
-.cpp.obj:
- $(CPP) $(CPPFLAGS) $(DEFINES) -c $< -o $@
-
-clean:
- del $(OBJ)
- del $(NAME).$(EXT)
- del $(NAME).map
diff --git a/unrar/unrar/makefile.msc b/unrar/unrar/makefile.msc deleted file mode 100644 index 77d82ef..0000000 --- a/unrar/unrar/makefile.msc +++ /dev/null @@ -1,564 +0,0 @@ -# Microsoft Developer Studio Generated NMAKE File, Based on unrar.dsp
-!IF "$(CFG)" == ""
-CFG=unrar - Win32 Release
-!MESSAGE No configuration specified. Defaulting to unrar - Win32 Release.
-!ENDIF
-
-!IF "$(CFG)" != "unrar - Win32 Release" && "$(CFG)" != "unrar - Win32 Debug"
-!MESSAGE Invalid configuration "$(CFG)" specified.
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "unrar.mak" CFG="unrar - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "unrar - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "unrar - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE
-!ERROR An invalid configuration is specified.
-!ENDIF
-
-!IF "$(OS)" == "Windows_NT"
-NULL=
-!ELSE
-NULL=nul
-!ENDIF
-
-CPP=cl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "unrar - Win32 Release"
-
-OUTDIR=.\Release
-INTDIR=.\Release
-# Begin Custom Macros
-OutDir=.\Release
-# End Custom Macros
-
-ALL : "$(OUTDIR)\unrar.exe"
-
-
-CLEAN :
- -@erase "$(INTDIR)\archive.obj"
- -@erase "$(INTDIR)\arcread.obj"
- -@erase "$(INTDIR)\cmddata.obj"
- -@erase "$(INTDIR)\consio.obj"
- -@erase "$(INTDIR)\crc.obj"
- -@erase "$(INTDIR)\crypt.obj"
- -@erase "$(INTDIR)\encname.obj"
- -@erase "$(INTDIR)\errhnd.obj"
- -@erase "$(INTDIR)\extinfo.obj"
- -@erase "$(INTDIR)\extract.obj"
- -@erase "$(INTDIR)\filcreat.obj"
- -@erase "$(INTDIR)\file.obj"
- -@erase "$(INTDIR)\filefn.obj"
- -@erase "$(INTDIR)\filestr.obj"
- -@erase "$(INTDIR)\find.obj"
- -@erase "$(INTDIR)\getbits.obj"
- -@erase "$(INTDIR)\global.obj"
- -@erase "$(INTDIR)\int64.obj"
- -@erase "$(INTDIR)\isnt.obj"
- -@erase "$(INTDIR)\list.obj"
- -@erase "$(INTDIR)\match.obj"
- -@erase "$(INTDIR)\options.obj"
- -@erase "$(INTDIR)\pathfn.obj"
- -@erase "$(INTDIR)\rar.obj"
- -@erase "$(INTDIR)\rarvm.obj"
- -@erase "$(INTDIR)\rawread.obj"
- -@erase "$(INTDIR)\rdwrfn.obj"
- -@erase "$(INTDIR)\recvol.obj"
- -@erase "$(INTDIR)\resource.obj"
- -@erase "$(INTDIR)\rijndael.obj"
- -@erase "$(INTDIR)\rs.obj"
- -@erase "$(INTDIR)\savepos.obj"
- -@erase "$(INTDIR)\scantree.obj"
- -@erase "$(INTDIR)\sha1.obj"
- -@erase "$(INTDIR)\strfn.obj"
- -@erase "$(INTDIR)\strlist.obj"
- -@erase "$(INTDIR)\system.obj"
- -@erase "$(INTDIR)\timefn.obj"
- -@erase "$(INTDIR)\ulinks.obj"
- -@erase "$(INTDIR)\unicode.obj"
- -@erase "$(INTDIR)\unpack.obj"
- -@erase "$(INTDIR)\vc60.idb"
- -@erase "$(INTDIR)\volume.obj"
- -@erase "$(OUTDIR)\unrar.exe"
- -@erase "$(OUTDIR)\unrar.map"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "UNRAR" /Fp"$(INTDIR)\unrar.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\unrar.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\unrar.pdb" /map:"$(INTDIR)\unrar.map" /machine:I386 /out:"$(OUTDIR)\unrar.exe"
-LINK32_OBJS= \
- "$(INTDIR)\rar.obj" \
- "$(INTDIR)\strlist.obj" \
- "$(INTDIR)\strfn.obj" \
- "$(INTDIR)\pathfn.obj" \
- "$(INTDIR)\int64.obj" \
- "$(INTDIR)\savepos.obj" \
- "$(INTDIR)\global.obj" \
- "$(INTDIR)\file.obj" \
- "$(INTDIR)\filefn.obj" \
- "$(INTDIR)\filcreat.obj" \
- "$(INTDIR)\archive.obj" \
- "$(INTDIR)\arcread.obj" \
- "$(INTDIR)\unicode.obj" \
- "$(INTDIR)\system.obj" \
- "$(INTDIR)\isnt.obj" \
- "$(INTDIR)\crypt.obj" \
- "$(INTDIR)\crc.obj" \
- "$(INTDIR)\rawread.obj" \
- "$(INTDIR)\encname.obj" \
- "$(INTDIR)\resource.obj" \
- "$(INTDIR)\match.obj" \
- "$(INTDIR)\timefn.obj" \
- "$(INTDIR)\rdwrfn.obj" \
- "$(INTDIR)\consio.obj" \
- "$(INTDIR)\options.obj" \
- "$(INTDIR)\ulinks.obj" \
- "$(INTDIR)\errhnd.obj" \
- "$(INTDIR)\rarvm.obj" \
- "$(INTDIR)\rijndael.obj" \
- "$(INTDIR)\getbits.obj" \
- "$(INTDIR)\sha1.obj" \
- "$(INTDIR)\extinfo.obj" \
- "$(INTDIR)\extract.obj" \
- "$(INTDIR)\volume.obj" \
- "$(INTDIR)\list.obj" \
- "$(INTDIR)\find.obj" \
- "$(INTDIR)\unpack.obj" \
- "$(INTDIR)\cmddata.obj" \
- "$(INTDIR)\filestr.obj" \
- "$(INTDIR)\recvol.obj" \
- "$(INTDIR)\rs.obj" \
- "$(INTDIR)\scantree.obj"
-
-"$(OUTDIR)\unrar.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-!ELSEIF "$(CFG)" == "unrar - Win32 Debug"
-
-OUTDIR=.\Debug
-INTDIR=.\Debug
-# Begin Custom Macros
-OutDir=.\Debug
-# End Custom Macros
-
-ALL : "$(OUTDIR)\unrar.exe"
-
-
-CLEAN :
- -@erase "$(INTDIR)\archive.obj"
- -@erase "$(INTDIR)\arcread.obj"
- -@erase "$(INTDIR)\cmddata.obj"
- -@erase "$(INTDIR)\consio.obj"
- -@erase "$(INTDIR)\crc.obj"
- -@erase "$(INTDIR)\crypt.obj"
- -@erase "$(INTDIR)\encname.obj"
- -@erase "$(INTDIR)\errhnd.obj"
- -@erase "$(INTDIR)\extinfo.obj"
- -@erase "$(INTDIR)\extract.obj"
- -@erase "$(INTDIR)\filcreat.obj"
- -@erase "$(INTDIR)\file.obj"
- -@erase "$(INTDIR)\filefn.obj"
- -@erase "$(INTDIR)\filestr.obj"
- -@erase "$(INTDIR)\find.obj"
- -@erase "$(INTDIR)\getbits.obj"
- -@erase "$(INTDIR)\global.obj"
- -@erase "$(INTDIR)\int64.obj"
- -@erase "$(INTDIR)\isnt.obj"
- -@erase "$(INTDIR)\list.obj"
- -@erase "$(INTDIR)\match.obj"
- -@erase "$(INTDIR)\options.obj"
- -@erase "$(INTDIR)\pathfn.obj"
- -@erase "$(INTDIR)\rar.obj"
- -@erase "$(INTDIR)\rarvm.obj"
- -@erase "$(INTDIR)\rawread.obj"
- -@erase "$(INTDIR)\rdwrfn.obj"
- -@erase "$(INTDIR)\recvol.obj"
- -@erase "$(INTDIR)\resource.obj"
- -@erase "$(INTDIR)\rijndael.obj"
- -@erase "$(INTDIR)\rs.obj"
- -@erase "$(INTDIR)\savepos.obj"
- -@erase "$(INTDIR)\scantree.obj"
- -@erase "$(INTDIR)\sha1.obj"
- -@erase "$(INTDIR)\strfn.obj"
- -@erase "$(INTDIR)\strlist.obj"
- -@erase "$(INTDIR)\system.obj"
- -@erase "$(INTDIR)\timefn.obj"
- -@erase "$(INTDIR)\ulinks.obj"
- -@erase "$(INTDIR)\unicode.obj"
- -@erase "$(INTDIR)\unpack.obj"
- -@erase "$(INTDIR)\vc60.idb"
- -@erase "$(INTDIR)\vc60.pdb"
- -@erase "$(INTDIR)\volume.obj"
- -@erase "$(OUTDIR)\unrar.exe"
- -@erase "$(OUTDIR)\unrar.ilk"
- -@erase "$(OUTDIR)\unrar.pdb"
-
-"$(OUTDIR)" :
- if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
-
-CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "UNRAR" /Fp"$(INTDIR)\unrar.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\unrar.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\unrar.pdb" /debug /machine:I386 /out:"$(OUTDIR)\unrar.exe" /pdbtype:sept
-LINK32_OBJS= \
- "$(INTDIR)\rar.obj" \
- "$(INTDIR)\strlist.obj" \
- "$(INTDIR)\strfn.obj" \
- "$(INTDIR)\pathfn.obj" \
- "$(INTDIR)\int64.obj" \
- "$(INTDIR)\savepos.obj" \
- "$(INTDIR)\global.obj" \
- "$(INTDIR)\file.obj" \
- "$(INTDIR)\filefn.obj" \
- "$(INTDIR)\filcreat.obj" \
- "$(INTDIR)\archive.obj" \
- "$(INTDIR)\arcread.obj" \
- "$(INTDIR)\unicode.obj" \
- "$(INTDIR)\system.obj" \
- "$(INTDIR)\isnt.obj" \
- "$(INTDIR)\crypt.obj" \
- "$(INTDIR)\crc.obj" \
- "$(INTDIR)\rawread.obj" \
- "$(INTDIR)\encname.obj" \
- "$(INTDIR)\resource.obj" \
- "$(INTDIR)\match.obj" \
- "$(INTDIR)\timefn.obj" \
- "$(INTDIR)\rdwrfn.obj" \
- "$(INTDIR)\consio.obj" \
- "$(INTDIR)\options.obj" \
- "$(INTDIR)\ulinks.obj" \
- "$(INTDIR)\errhnd.obj" \
- "$(INTDIR)\rarvm.obj" \
- "$(INTDIR)\rijndael.obj" \
- "$(INTDIR)\getbits.obj" \
- "$(INTDIR)\sha1.obj" \
- "$(INTDIR)\extinfo.obj" \
- "$(INTDIR)\extract.obj" \
- "$(INTDIR)\volume.obj" \
- "$(INTDIR)\list.obj" \
- "$(INTDIR)\find.obj" \
- "$(INTDIR)\unpack.obj" \
- "$(INTDIR)\cmddata.obj" \
- "$(INTDIR)\filestr.obj" \
- "$(INTDIR)\recvol.obj" \
- "$(INTDIR)\rs.obj" \
- "$(INTDIR)\scantree.obj"
-
-"$(OUTDIR)\unrar.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-!ENDIF
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-
-!IF "$(NO_EXTERNAL_DEPS)" != "1"
-!IF EXISTS("msc.dep")
-!INCLUDE "msc.dep"
-!ELSE
-!MESSAGE Warning: cannot find "msc.dep"
-!ENDIF
-!ENDIF
-
-
-!IF "$(CFG)" == "unrar - Win32 Release" || "$(CFG)" == "unrar - Win32 Debug"
-SOURCE=.\archive.cpp
-
-"$(INTDIR)\archive.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\arcread.cpp
-
-"$(INTDIR)\arcread.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\cmddata.cpp
-
-"$(INTDIR)\cmddata.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\consio.cpp
-
-"$(INTDIR)\consio.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\crc.cpp
-
-"$(INTDIR)\crc.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\crypt.cpp
-
-"$(INTDIR)\crypt.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\encname.cpp
-
-"$(INTDIR)\encname.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\errhnd.cpp
-
-"$(INTDIR)\errhnd.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\extinfo.cpp
-
-"$(INTDIR)\extinfo.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\extract.cpp
-
-"$(INTDIR)\extract.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\filcreat.cpp
-
-"$(INTDIR)\filcreat.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\file.cpp
-
-"$(INTDIR)\file.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\filefn.cpp
-
-"$(INTDIR)\filefn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\filestr.cpp
-
-"$(INTDIR)\filestr.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\find.cpp
-
-"$(INTDIR)\find.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\getbits.cpp
-
-"$(INTDIR)\getbits.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\global.cpp
-
-"$(INTDIR)\global.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\int64.cpp
-
-"$(INTDIR)\int64.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\isnt.cpp
-
-"$(INTDIR)\isnt.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\list.cpp
-
-"$(INTDIR)\list.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\match.cpp
-
-"$(INTDIR)\match.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\options.cpp
-
-"$(INTDIR)\options.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\pathfn.cpp
-
-"$(INTDIR)\pathfn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rar.cpp
-
-"$(INTDIR)\rar.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rarvm.cpp
-
-"$(INTDIR)\rarvm.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rawread.cpp
-
-"$(INTDIR)\rawread.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rdwrfn.cpp
-
-"$(INTDIR)\rdwrfn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\recvol.cpp
-
-"$(INTDIR)\recvol.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\resource.cpp
-
-"$(INTDIR)\resource.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rijndael.cpp
-
-"$(INTDIR)\rijndael.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\rs.cpp
-
-"$(INTDIR)\rs.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\savepos.cpp
-
-"$(INTDIR)\savepos.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\scantree.cpp
-
-"$(INTDIR)\scantree.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\sha1.cpp
-
-"$(INTDIR)\sha1.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\strfn.cpp
-
-"$(INTDIR)\strfn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\strlist.cpp
-
-"$(INTDIR)\strlist.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\system.cpp
-
-"$(INTDIR)\system.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\timefn.cpp
-
-"$(INTDIR)\timefn.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\ulinks.cpp
-
-"$(INTDIR)\ulinks.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\unicode.cpp
-
-"$(INTDIR)\unicode.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\unpack.cpp
-
-"$(INTDIR)\unpack.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-SOURCE=.\volume.cpp
-
-"$(INTDIR)\volume.obj" : $(SOURCE) "$(INTDIR)"
- $(CPP) $(CPP_PROJ) $(SOURCE)
-
-
-
-!ENDIF
-
diff --git a/unrar/unrar/match.cpp b/unrar/unrar/match.cpp index bd1c02f..ec88fa6 100644 --- a/unrar/unrar/match.cpp +++ b/unrar/unrar/match.cpp @@ -1,183 +1,82 @@ #include "rar.hpp" -static bool match(char *pattern,char *string,bool ForceCase); -static bool match(wchar *pattern,wchar *string,bool ForceCase); - -static int mstricompc(const char *Str1,const char *Str2,bool ForceCase); -static int mstricompcw(const wchar *Str1,const wchar *Str2,bool ForceCase); -static int mstrnicompc(const char *Str1,const char *Str2,size_t N,bool ForceCase); -static int mstrnicompcw(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase); - -inline uint toupperc(byte ch,bool ForceCase) -{ - if (ForceCase) - return(ch); -#ifdef _WIN_32 - return((uint)(LPARAM)CharUpper((LPTSTR)(ch))); -#elif defined(_UNIX) - return(ch); -#else - return(toupper(ch)); -#endif -} - +static bool match(const wchar *pattern,const wchar *string,bool ForceCase); +static int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase); +static int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase); inline uint touppercw(uint ch,bool ForceCase) { if (ForceCase) - return(ch); + return ch; #if defined(_UNIX) - return(ch); + return ch; #else - return(toupperw(ch)); + return toupperw(ch); #endif } -bool CmpName(char *Wildcard,char *Name,int CmpPath) +bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode) { - bool ForceCase=(CmpPath&MATCH_FORCECASESENSITIVE)!=0; + bool ForceCase=(CmpMode&MATCH_FORCECASESENSITIVE)!=0; - CmpPath&=MATCH_MODEMASK; - - if (CmpPath!=MATCH_NAMES) - { - size_t WildLength=strlen(Wildcard); - if (CmpPath!=MATCH_EXACTPATH && mstrnicompc(Wildcard,Name,WildLength,ForceCase)==0) - { - char NextCh=Name[WildLength]; - if (NextCh=='\\' || NextCh=='/' || NextCh==0) - return(true); - } - char Path1[NM],Path2[NM]; - GetFilePath(Wildcard,Path1,ASIZE(Path1)); - GetFilePath(Name,Path2,ASIZE(Path1)); - if (mstricompc(Wildcard,Path2,ForceCase)==0) - return(true); - if ((CmpPath==MATCH_PATH || CmpPath==MATCH_EXACTPATH) && mstricompc(Path1,Path2,ForceCase)!=0) - return(false); - if (CmpPath==MATCH_SUBPATH || CmpPath==MATCH_WILDSUBPATH) - if (IsWildcard(Path1)) - return(match(Wildcard,Name,ForceCase)); - else - if (CmpPath==MATCH_SUBPATH || IsWildcard(Wildcard)) - { - if (*Path1 && mstrnicompc(Path1,Path2,strlen(Path1),ForceCase)!=0) - return(false); - } - else - if (mstricompc(Path1,Path2,ForceCase)!=0) - return(false); - } - char *Name1=PointToName(Wildcard); - char *Name2=PointToName(Name); - - // always return false for RAR temporary files to exclude them - // from archiving operations - if (mstrnicompc("__rar_",Name2,6,false)==0) - return(false); + CmpMode&=MATCH_MODEMASK; - return(match(Name1,Name2,ForceCase)); -} - - -#ifndef SFX_MODULE -bool CmpName(wchar *Wildcard,wchar *Name,int CmpPath) -{ - bool ForceCase=(CmpPath&MATCH_FORCECASESENSITIVE)!=0; - - CmpPath&=MATCH_MODEMASK; - - if (CmpPath!=MATCH_NAMES) + if (CmpMode!=MATCH_NAMES) { - size_t WildLength=strlenw(Wildcard); - if (CmpPath!=MATCH_EXACTPATH && mstrnicompcw(Wildcard,Name,WildLength,ForceCase)==0) + size_t WildLength=wcslen(Wildcard); + if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH && CmpMode!=MATCH_ALLWILD && + mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0) { + // For all modes except MATCH_NAMES, MATCH_EXACT, MATCH_EXACTPATH, MATCH_ALLWILD, + // "path1" mask must match "path1\path2\filename.ext" and "path1" names. wchar NextCh=Name[WildLength]; if (NextCh==L'\\' || NextCh==L'/' || NextCh==0) return(true); } + + // Nothing more to compare for MATCH_SUBPATHONLY. + if (CmpMode==MATCH_SUBPATHONLY) + return(false); + wchar Path1[NM],Path2[NM]; GetFilePath(Wildcard,Path1,ASIZE(Path1)); GetFilePath(Name,Path2,ASIZE(Path2)); - if ((CmpPath==MATCH_PATH || CmpPath==MATCH_EXACTPATH) && mstricompcw(Path1,Path2,ForceCase)!=0) + + if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) && + mwcsicompc(Path1,Path2,ForceCase)!=0) return(false); - if (CmpPath==MATCH_SUBPATH || CmpPath==MATCH_WILDSUBPATH) - if (IsWildcard(NULL,Path1)) + if (CmpMode==MATCH_ALLWILD) + return match(Wildcard,Name,ForceCase); + if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH) + if (IsWildcard(Path1)) return(match(Wildcard,Name,ForceCase)); else - if (CmpPath==MATCH_SUBPATH || IsWildcard(NULL,Wildcard)) + if (CmpMode==MATCH_SUBPATH || IsWildcard(Wildcard)) { - if (*Path1 && mstrnicompcw(Path1,Path2,strlenw(Path1),ForceCase)!=0) + if (*Path1 && mwcsnicompc(Path1,Path2,wcslen(Path1),ForceCase)!=0) return(false); } else - if (mstricompcw(Path1,Path2,ForceCase)!=0) + if (mwcsicompc(Path1,Path2,ForceCase)!=0) return(false); } wchar *Name1=PointToName(Wildcard); wchar *Name2=PointToName(Name); - // always return false for RAR temporary files to exclude them - // from archiving operations - if (mstrnicompcw(L"__rar_",Name2,6,false)==0) - return(false); - - return(match(Name1,Name2,ForceCase)); -} -#endif + // Always return false for RAR temporary files to exclude them + // from archiving operations. +// if (mwcsnicompc(L"__rar_",Name2,6,false)==0) +// return(false); + if (CmpMode==MATCH_EXACT) + return(mwcsicompc(Name1,Name2,ForceCase)==0); -bool match(char *pattern,char *string,bool ForceCase) -{ - for (;; ++string) - { - char stringc=toupperc(*string,ForceCase); - char patternc=toupperc(*pattern++,ForceCase); - switch (patternc) - { - case 0: - return(stringc==0); - case '?': - if (stringc == 0) - return(false); - break; - case '*': - if (*pattern==0) - return(true); - if (*pattern=='.') - { - if (pattern[1]=='*' && pattern[2]==0) - return(true); - char *dot=strchr(string,'.'); - if (pattern[1]==0) - return (dot==NULL || dot[1]==0); - if (dot!=NULL) - { - string=dot; - if (strpbrk(pattern,"*?")==NULL && strchr(string+1,'.')==NULL) - return(mstricompc(pattern+1,string+1,ForceCase)==0); - } - } - - while (*string) - if (match(pattern,string++,ForceCase)) - return(true); - return(false); - default: - if (patternc != stringc) - if (patternc=='.' && stringc==0) - return(match(pattern,string,ForceCase)); - else - return(false); - break; - } - } + return(match(Name1,Name2,ForceCase)); } -#ifndef SFX_MODULE -bool match(wchar *pattern,wchar *string,bool ForceCase) +bool match(const wchar *pattern,const wchar *string,bool ForceCase) { for (;; ++string) { @@ -198,14 +97,14 @@ bool match(wchar *pattern,wchar *string,bool ForceCase) { if (pattern[1]=='*' && pattern[2]==0) return(true); - wchar *dot=strchrw(string,'.'); + const wchar *dot=wcschr(string,'.'); if (pattern[1]==0) return (dot==NULL || dot[1]==0); if (dot!=NULL) { string=dot; - if (strpbrkw(pattern,L"*?")==NULL && strchrw(string+1,'.')==NULL) - return(mstricompcw(pattern+1,string+1,ForceCase)==0); + if (wcspbrk(pattern,L"*?")==NULL && wcschr(string+1,'.')==NULL) + return(mwcsicompc(pattern+1,string+1,ForceCase)==0); } } @@ -215,56 +114,34 @@ bool match(wchar *pattern,wchar *string,bool ForceCase) return(false); default: if (patternc != stringc) - if (patternc=='.' && stringc==0) + { + // Allow "name." mask match "name" and "name.\" match "name\". + if (patternc=='.' && (stringc==0 || stringc=='\\' || stringc=='.')) return(match(pattern,string,ForceCase)); else return(false); + } break; } } } -#endif - - -int mstricompc(const char *Str1,const char *Str2,bool ForceCase) -{ - if (ForceCase) - return(strcmp(Str1,Str2)); - return(stricompc(Str1,Str2)); -} - - -#ifndef SFX_MODULE -int mstricompcw(const wchar *Str1,const wchar *Str2,bool ForceCase) -{ - if (ForceCase) - return(strcmpw(Str1,Str2)); - return(stricompcw(Str1,Str2)); -} -#endif -int mstrnicompc(const char *Str1,const char *Str2,size_t N,bool ForceCase) +int mwcsicompc(const wchar *Str1,const wchar *Str2,bool ForceCase) { if (ForceCase) - return(strncmp(Str1,Str2,N)); -#if defined(_UNIX) - return(strncmp(Str1,Str2,N)); -#else - return(strnicomp(Str1,Str2,N)); -#endif + return wcscmp(Str1,Str2); + return wcsicompc(Str1,Str2); } -#ifndef SFX_MODULE -int mstrnicompcw(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase) +int mwcsnicompc(const wchar *Str1,const wchar *Str2,size_t N,bool ForceCase) { if (ForceCase) - return(strncmpw(Str1,Str2,N)); + return wcsncmp(Str1,Str2,N); #if defined(_UNIX) - return(strncmpw(Str1,Str2,N)); + return wcsncmp(Str1,Str2,N); #else - return(strnicmpw(Str1,Str2,N)); + return wcsnicomp(Str1,Str2,N); #endif } -#endif diff --git a/unrar/unrar/match.hpp b/unrar/unrar/match.hpp index 0e43514..1e65a3c 100644 --- a/unrar/unrar/match.hpp +++ b/unrar/unrar/match.hpp @@ -2,26 +2,37 @@ #define _RAR_MATCH_ enum { - MATCH_NAMES, // Compare names only. + MATCH_NAMES, // Paths are ignored. + // Compares names only using wildcards. - MATCH_PATH, // Compares names and paths. Both must match exactly. - // Unlike MATCH_EXACTPATH, also matches names if - // mask contains path only and this path is a part - // of name path. + MATCH_SUBPATHONLY, // Paths must match either exactly or path in wildcard + // must be present in the beginning of file path. + // For example, "c:\path1\*" or "c:\path1" will match + // "c:\path1\path2\file". + // Names are not compared. - MATCH_EXACTPATH, // Compares names and paths. Both must match exactly. + MATCH_EXACT, // Paths must match exactly. + // Names must match exactly. + + MATCH_ALLWILD, // Paths and names are compared using wildcards. + // Unlike MATCH_SUBPATH, paths do not match subdirs + // unless a wildcard tells so. + + MATCH_EXACTPATH, // Paths must match exactly. + // Names are compared using wildcards. MATCH_SUBPATH, // Names must be the same, but path in mask is allowed - // to be only a part of name path. + // to be only a part of name path. In other words, + // we match all files matching the file mask + // in current folder and subfolders. - MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if mask contains wildcards - // and as MATCH_PATH otherwise. + MATCH_WILDSUBPATH // Works as MATCH_SUBPATH if file mask contains + // wildcards and as MATCH_EXACTPATH otherwise. }; #define MATCH_MODEMASK 0x0000ffff #define MATCH_FORCECASESENSITIVE 0x80000000 -bool CmpName(char *Wildcard,char *Name,int CmpPath); -bool CmpName(wchar *Wildcard,wchar *Name,int CmpPath); +bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode); #endif diff --git a/unrar/unrar/model.cpp b/unrar/unrar/model.cpp index abc73d2..e4f9e3c 100644 --- a/unrar/unrar/model.cpp +++ b/unrar/unrar/model.cpp @@ -5,10 +5,17 @@ * Contents: model description and encoding/decoding routines * ****************************************************************************/ -inline PPM_CONTEXT* PPM_CONTEXT::createChild(ModelPPM *Model,STATE* pStats, - STATE& FirstState) +static const int MAX_O=64; /* maximum allowed model order */ +const uint TOP=1 << 24, BOT=1 << 15; + +template <class T> +inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; } + + +inline RARPPM_CONTEXT* RARPPM_CONTEXT::createChild(ModelPPM *Model,RARPPM_STATE* pStats, + RARPPM_STATE& FirstState) { - PPM_CONTEXT* pc = (PPM_CONTEXT*) Model->SubAlloc.AllocContext(); + RARPPM_CONTEXT* pc = (RARPPM_CONTEXT*) Model->SubAlloc.AllocContext(); if ( pc ) { pc->NumStats=1; @@ -34,11 +41,15 @@ void ModelPPM::RestartModelRare() memset(CharMask,0,sizeof(CharMask)); SubAlloc.InitSubAllocator(); InitRL=-(MaxOrder < 12 ? MaxOrder:12)-1; - MinContext = MaxContext = (PPM_CONTEXT*) SubAlloc.AllocContext(); + MinContext = MaxContext = (RARPPM_CONTEXT*) SubAlloc.AllocContext(); + if (MinContext == NULL) + throw std::bad_alloc(); MinContext->Suffix=NULL; OrderFall=MaxOrder; MinContext->U.SummFreq=(MinContext->NumStats=256)+1; - FoundState=MinContext->U.Stats=(STATE*)SubAlloc.AllocUnits(256/2); + FoundState=MinContext->U.Stats=(RARPPM_STATE*)SubAlloc.AllocUnits(256/2); + if (FoundState == NULL) + throw std::bad_alloc(); for (RunLength=InitRL, PrevSuccess=i=0;i < 256;i++) { MinContext->U.Stats[i].Symbol=i; @@ -105,10 +116,10 @@ void ModelPPM::StartModelRare(int MaxOrder) } -void PPM_CONTEXT::rescale(ModelPPM *Model) +void RARPPM_CONTEXT::rescale(ModelPPM *Model) { int OldNS=NumStats, i=NumStats-1, Adder, EscFreq; - STATE* p1, * p; + RARPPM_STATE* p1, * p; for (p=Model->FoundState;p != U.Stats;p--) _PPMD_SWAP(p[0],p[-1]); U.Stats->Freq += 4; @@ -122,7 +133,7 @@ void PPM_CONTEXT::rescale(ModelPPM *Model) U.SummFreq += (p->Freq=(p->Freq+Adder) >> 1); if (p[0].Freq > p[-1].Freq) { - STATE tmp=*(p1=p); + RARPPM_STATE tmp=*(p1=p); do { p1[0]=p1[-1]; @@ -139,7 +150,7 @@ void PPM_CONTEXT::rescale(ModelPPM *Model) EscFreq += i; if ((NumStats -= i) == 1) { - STATE tmp=*U.Stats; + RARPPM_STATE tmp=*U.Stats; do { tmp.Freq-=(tmp.Freq >> 1); @@ -152,19 +163,16 @@ void PPM_CONTEXT::rescale(ModelPPM *Model) U.SummFreq += (EscFreq -= (EscFreq >> 1)); int n0=(OldNS+1) >> 1, n1=(NumStats+1) >> 1; if (n0 != n1) - U.Stats = (STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1); + U.Stats = (RARPPM_STATE*) Model->SubAlloc.ShrinkUnits(U.Stats,n0,n1); Model->FoundState=U.Stats; } -inline PPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,STATE* p1) +inline RARPPM_CONTEXT* ModelPPM::CreateSuccessors(bool Skip,RARPPM_STATE* p1) { -#ifdef __ICL - static -#endif - STATE UpState; - PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; - STATE * p, * ps[MAX_O], ** pps=ps; + RARPPM_STATE UpState; + RARPPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; + RARPPM_STATE * p, * ps[MAX_O], ** pps=ps; if ( !Skip ) { *pps++ = FoundState; @@ -195,14 +203,21 @@ LOOP_ENTRY: { pc=p->Successor; break; + } + // We ensure that PPM order input parameter does not exceed MAX_O (64), + // so we do not really need this check and added it for extra safety. + // See CVE-2017-17969 for details. + if (pps>=ps+ASIZE(ps)) + return NULL; + *pps++ = p; } while ( pc->Suffix ); NO_LOOP: if (pps == ps) return pc; UpState.Symbol=*(byte*) UpBranch; - UpState.Successor=(PPM_CONTEXT*) (((byte*) UpBranch)+1); + UpState.Successor=(RARPPM_CONTEXT*) (((byte*) UpBranch)+1); if (pc->NumStats != 1) { if ((byte*) pc <= SubAlloc.pText) @@ -230,8 +245,8 @@ NO_LOOP: inline void ModelPPM::UpdateModel() { - STATE fs = *FoundState, *p = NULL; - PPM_CONTEXT *pc, *Successor; + RARPPM_STATE fs = *FoundState, *p = NULL; + RARPPM_CONTEXT *pc, *Successor; uint ns1, ns, cf, sf, s0; if (fs.Freq < MAX_FREQ/4 && (pc=MinContext->Suffix) != NULL) { @@ -269,7 +284,7 @@ inline void ModelPPM::UpdateModel() return; } *SubAlloc.pText++ = fs.Symbol; - Successor = (PPM_CONTEXT*) SubAlloc.pText; + Successor = (RARPPM_CONTEXT*) SubAlloc.pText; if (SubAlloc.pText >= SubAlloc.FakeUnitsStart) goto RESTART_MODEL; if ( fs.Successor ) @@ -295,7 +310,7 @@ inline void ModelPPM::UpdateModel() { if ((ns1 & 1) == 0) { - pc->U.Stats=(STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1); + pc->U.Stats=(RARPPM_STATE*) SubAlloc.ExpandUnits(pc->U.Stats,ns1 >> 1); if ( !pc->U.Stats ) goto RESTART_MODEL; } @@ -303,7 +318,7 @@ inline void ModelPPM::UpdateModel() } else { - p=(STATE*) SubAlloc.AllocUnits(1); + p=(RARPPM_STATE*) SubAlloc.AllocUnits(1); if ( !p ) goto RESTART_MODEL; *p=pc->OneState; @@ -346,9 +361,9 @@ static const byte ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, -inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) +inline void RARPPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) { - STATE& rs=OneState; + RARPPM_STATE& rs=OneState; Model->HiBitsFlag=Model->HB2Flag[Model->FoundState->Symbol]; ushort& bs=Model->BinSumm[rs.Freq-1][Model->PrevSuccess+ Model->NS2BSIndx[Suffix->NumStats-1]+ @@ -360,14 +375,14 @@ inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) rs.Freq += (rs.Freq < 128); Model->Coder.SubRange.LowCount=0; Model->Coder.SubRange.HighCount=bs; - bs = SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2)); + bs = GET_SHORT16(bs+INTERVAL-GET_MEAN(bs,PERIOD_BITS,2)); Model->PrevSuccess=1; Model->RunLength++; } else { Model->Coder.SubRange.LowCount=bs; - bs = SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2)); + bs = GET_SHORT16(bs-GET_MEAN(bs,PERIOD_BITS,2)); Model->Coder.SubRange.HighCount=BIN_SCALE; Model->InitEsc=ExpEscape[bs >> 10]; Model->NumMasked=1; @@ -378,7 +393,7 @@ inline void PPM_CONTEXT::decodeBinSymbol(ModelPPM *Model) } -inline void PPM_CONTEXT::update1(ModelPPM *Model,STATE* p) +inline void RARPPM_CONTEXT::update1(ModelPPM *Model,RARPPM_STATE* p) { (Model->FoundState=p)->Freq += 4; U.SummFreq += 4; @@ -394,13 +409,13 @@ inline void PPM_CONTEXT::update1(ModelPPM *Model,STATE* p) -inline bool PPM_CONTEXT::decodeSymbol1(ModelPPM *Model) +inline bool RARPPM_CONTEXT::decodeSymbol1(ModelPPM *Model) { Model->Coder.SubRange.scale=U.SummFreq; - STATE* p=U.Stats; + RARPPM_STATE* p=U.Stats; int i, HiCnt; int count=Model->Coder.GetCurrentCount(); - if (count>=Model->Coder.SubRange.scale) + if (count>=(int)Model->Coder.SubRange.scale) return(false); if (count < (HiCnt=p->Freq)) { @@ -439,7 +454,7 @@ inline bool PPM_CONTEXT::decodeSymbol1(ModelPPM *Model) } -inline void PPM_CONTEXT::update2(ModelPPM *Model,STATE* p) +inline void RARPPM_CONTEXT::update2(ModelPPM *Model,RARPPM_STATE* p) { (Model->FoundState=p)->Freq += 4; U.SummFreq += 4; @@ -450,9 +465,9 @@ inline void PPM_CONTEXT::update2(ModelPPM *Model,STATE* p) } -inline SEE2_CONTEXT* PPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) +inline RARPPM_SEE2_CONTEXT* RARPPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) { - SEE2_CONTEXT* psee2c; + RARPPM_SEE2_CONTEXT* psee2c; if (NumStats != 256) { psee2c=Model->SEE2Cont[Model->NS2Indx[Diff-1]]+ @@ -472,11 +487,11 @@ inline SEE2_CONTEXT* PPM_CONTEXT::makeEscFreq2(ModelPPM *Model,int Diff) -inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) +inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model) { int count, HiCnt, i=NumStats-Model->NumMasked; - SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i); - STATE* ps[256], ** pps=ps, * p=U.Stats-1; + RARPPM_SEE2_CONTEXT* psee2c=makeEscFreq2(Model,i); + RARPPM_STATE* ps[256], ** pps=ps, * p=U.Stats-1; HiCnt=0; do { @@ -485,18 +500,29 @@ inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) p++; } while (Model->CharMask[p->Symbol] == Model->EscCount); HiCnt += p->Freq; + + // We do not reuse PPMd coder in unstable state, so we do not really need + // this check and added it for extra safety. See CVE-2017-17969 for details. + if (pps>=ps+ASIZE(ps)) + return false; + *pps++ = p; } while ( --i ); Model->Coder.SubRange.scale += HiCnt; count=Model->Coder.GetCurrentCount(); - if (count>=Model->Coder.SubRange.scale) + if (count>=(int)Model->Coder.SubRange.scale) return(false); p=*(pps=ps); if (count < HiCnt) { HiCnt=0; while ((HiCnt += p->Freq) <= count) - p=*++pps; + { + pps++; + if (pps>=ps+ASIZE(ps)) // Extra safety check. + return false; + p=*pps; + } Model->Coder.SubRange.LowCount = (Model->Coder.SubRange.HighCount=HiCnt)-p->Freq; psee2c->update(); update2(Model,p); @@ -506,15 +532,20 @@ inline bool PPM_CONTEXT::decodeSymbol2(ModelPPM *Model) Model->Coder.SubRange.LowCount=HiCnt; Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale; i=NumStats-Model->NumMasked; - pps--; + + // 2022.12.02: we removed pps-- here and changed the code below to avoid + // "array subscript -1 is outside array bounds" warning in some compilers. do { - Model->CharMask[(*++pps)->Symbol]=Model->EscCount; + if (pps>=ps+ASIZE(ps)) // Extra safety check. + return false; + Model->CharMask[(*pps)->Symbol]=Model->EscCount; + pps++; } while ( --i ); psee2c->Summ += Model->Coder.SubRange.scale; Model->NumMasked = NumStats; } - return(true); + return true; } @@ -540,7 +571,7 @@ void ModelPPM::CleanUp() bool ModelPPM::DecodeInit(Unpack *UnpackRead,int &EscChar) { int MaxOrder=UnpackRead->GetChar(); - bool Reset=MaxOrder & 0x20; + bool Reset=(MaxOrder & 0x20)!=0; int MaxMB; if (Reset) diff --git a/unrar/unrar/model.hpp b/unrar/unrar/model.hpp index f39323b..52abc89 100644 --- a/unrar/unrar/model.hpp +++ b/unrar/unrar/model.hpp @@ -4,16 +4,17 @@ #include "coder.hpp" #include "suballoc.hpp" -const int MAX_O=64; /* maximum allowed model order */ - -const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS, - INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124; - -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #pragma pack(1) #endif -struct SEE2_CONTEXT +struct RARPPM_DEF +{ + static const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=INT_BITS+PERIOD_BITS, + INTERVAL=1 << INT_BITS, BIN_SCALE=1 << TOT_BITS, MAX_FREQ=124; +}; + +struct RARPPM_SEE2_CONTEXT : RARPPM_DEF { // SEE-contexts for PPM-contexts with masked symbols ushort Summ; byte Shift, Count; @@ -24,7 +25,7 @@ struct SEE2_CONTEXT } uint getMean() { - uint RetVal=SHORT16(Summ) >> Shift; + uint RetVal=GET_SHORT16(Summ) >> Shift; Summ -= RetVal; return RetVal+(RetVal == 0); } @@ -40,45 +41,47 @@ struct SEE2_CONTEXT class ModelPPM; -struct PPM_CONTEXT; +struct RARPPM_CONTEXT; -struct STATE +struct RARPPM_STATE { byte Symbol; byte Freq; - PPM_CONTEXT* Successor; + RARPPM_CONTEXT* Successor; }; -struct FreqData -{ - ushort SummFreq; - STATE _PACK_ATTR * Stats; -}; -struct PPM_CONTEXT +struct RARPPM_CONTEXT : RARPPM_DEF { ushort NumStats; + + struct FreqData + { + ushort SummFreq; + RARPPM_STATE RARPPM_PACK_ATTR * Stats; + }; + union { FreqData U; - STATE OneState; + RARPPM_STATE OneState; }; - PPM_CONTEXT* Suffix; + RARPPM_CONTEXT* Suffix; inline void encodeBinSymbol(ModelPPM *Model,int symbol); // MaxOrder: inline void encodeSymbol1(ModelPPM *Model,int symbol); // ABCD context inline void encodeSymbol2(ModelPPM *Model,int symbol); // BCD suffix inline void decodeBinSymbol(ModelPPM *Model); // BCDE successor inline bool decodeSymbol1(ModelPPM *Model); // other orders: inline bool decodeSymbol2(ModelPPM *Model); // BCD context - inline void update1(ModelPPM *Model,STATE* p); // CD suffix - inline void update2(ModelPPM *Model,STATE* p); // BCDE successor + inline void update1(ModelPPM *Model,RARPPM_STATE* p); // CD suffix + inline void update2(ModelPPM *Model,RARPPM_STATE* p); // BCDE successor void rescale(ModelPPM *Model); - inline PPM_CONTEXT* createChild(ModelPPM *Model,STATE* pStats,STATE& FirstState); - inline SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff); + inline RARPPM_CONTEXT* createChild(ModelPPM *Model,RARPPM_STATE* pStats,RARPPM_STATE& FirstState); + inline RARPPM_SEE2_CONTEXT* makeEscFreq2(ModelPPM *Model,int Diff); }; -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #ifdef _AIX #pragma pack(pop) #else @@ -86,28 +89,15 @@ struct PPM_CONTEXT #endif #endif -const uint UNIT_SIZE=Max(sizeof(PPM_CONTEXT),sizeof(RAR_MEM_BLK)); -const uint FIXED_UNIT_SIZE=12; - -/* -inline PPM_CONTEXT::PPM_CONTEXT(STATE* pStats,PPM_CONTEXT* ShorterContext): - NumStats(1), Suffix(ShorterContext) { pStats->Successor=this; } -inline PPM_CONTEXT::PPM_CONTEXT(): NumStats(0) {} -*/ - -template <class T> -inline void _PPMD_SWAP(T& t1,T& t2) { T tmp=t1; t1=t2; t2=tmp; } - - -class ModelPPM +class ModelPPM : RARPPM_DEF { private: - friend struct PPM_CONTEXT; + friend struct RARPPM_CONTEXT; - _PACK_ATTR SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont; + RARPPM_SEE2_CONTEXT SEE2Cont[25][16], DummySEE2Cont; - struct PPM_CONTEXT *MinContext, *MedContext, *MaxContext; - STATE* FoundState; // found next state transition + struct RARPPM_CONTEXT *MinContext, *MedContext, *MaxContext; + RARPPM_STATE* FoundState; // found next state transition int NumMasked, InitEsc, OrderFall, MaxOrder, RunLength, InitRL; byte CharMask[256], NS2Indx[256], NS2BSIndx[256], HB2Flag[256]; byte EscCount, PrevSuccess, HiBitsFlag; @@ -118,7 +108,7 @@ class ModelPPM void RestartModelRare(); void StartModelRare(int MaxOrder); - inline PPM_CONTEXT* CreateSuccessors(bool Skip,STATE* p1); + inline RARPPM_CONTEXT* CreateSuccessors(bool Skip,RARPPM_STATE* p1); inline void UpdateModel(); inline void ClearMask(); diff --git a/unrar/unrar/msc.dep b/unrar/unrar/msc.dep deleted file mode 100644 index 1205afa..0000000 --- a/unrar/unrar/msc.dep +++ /dev/null @@ -1,2532 +0,0 @@ -# Microsoft Developer Studio Generated Dependency File, included by unrar.mak
-
-.\archive.cpp : \
- ".\arccmt.cpp"\
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\arcread.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\cmddata.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\consio.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.cpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\crc.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\crypt.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\encname.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\errhnd.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\extinfo.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
- ".\win32acl.cpp"\
- ".\win32stm.cpp"\
-
-
-.\extract.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\filcreat.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\file.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\filefn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\filestr.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\find.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\getbits.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\global.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\int64.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\isnt.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\list.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\match.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\options.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\pathfn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rar.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.cpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rarvm.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rarvmtbl.cpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rawread.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rdwrfn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\recvol.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\resource.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rijndael.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\rs.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\savepos.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\scantree.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\sha1.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\strfn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\strlist.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\system.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\timefn.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\ulinks.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\unicode.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\unpack.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.cpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.cpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.cpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\unpack15.cpp"\
- ".\unpack20.cpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
-
-.\volume.cpp : \
- ".\archive.hpp"\
- ".\array.hpp"\
- ".\cmddata.hpp"\
- ".\coder.hpp"\
- ".\compress.hpp"\
- ".\consio.hpp"\
- ".\crc.hpp"\
- ".\crypt.hpp"\
- ".\encname.hpp"\
- ".\errhnd.hpp"\
- ".\extinfo.hpp"\
- ".\extract.hpp"\
- ".\filcreat.hpp"\
- ".\file.hpp"\
- ".\filefn.hpp"\
- ".\filestr.hpp"\
- ".\find.hpp"\
- ".\getbits.hpp"\
- ".\global.hpp"\
- ".\headers.hpp"\
- ".\int64.hpp"\
- ".\isnt.hpp"\
- ".\list.hpp"\
- ".\loclang.hpp"\
- ".\log.hpp"\
- ".\match.hpp"\
- ".\model.hpp"\
- ".\options.hpp"\
- ".\os.hpp"\
- ".\pathfn.hpp"\
- ".\rar.hpp"\
- ".\rardefs.hpp"\
- ".\rarfn.hpp"\
- ".\rarlang.hpp"\
- ".\raros.hpp"\
- ".\rartypes.hpp"\
- ".\rarvm.hpp"\
- ".\rawread.hpp"\
- ".\rdwrfn.hpp"\
- ".\recvol.hpp"\
- ".\resource.hpp"\
- ".\rijndael.hpp"\
- ".\rs.hpp"\
- ".\savepos.hpp"\
- ".\scantree.hpp"\
- ".\sha1.hpp"\
- ".\smallfn.hpp"\
- ".\strfn.hpp"\
- ".\strlist.hpp"\
- ".\suballoc.hpp"\
- ".\system.hpp"\
- ".\timefn.hpp"\
- ".\ulinks.hpp"\
- ".\unicode.hpp"\
- ".\unpack.hpp"\
- ".\version.hpp"\
- ".\volume.hpp"\
-
diff --git a/unrar/unrar/options.cpp b/unrar/unrar/options.cpp index 57ca92f..22ae27c 100644 --- a/unrar/unrar/options.cpp +++ b/unrar/unrar/options.cpp @@ -6,23 +6,22 @@ RAROptions::RAROptions() } -RAROptions::~RAROptions() -{ - memset(this,0,sizeof(RAROptions)); -} - - void RAROptions::Init() { memset(this,0,sizeof(RAROptions)); - WinSize=0x400000; + WinSize=0x2000000; Overwrite=OVERWRITE_DEFAULT; Method=3; MsgStream=MSG_STDOUT; ConvertNames=NAMES_ORIGINALCASE; - ProcessEA=true; - xmtime=EXTTIME_HIGH3; - CurVolNum=0; - FileSizeLess=INT64ERR; - FileSizeMore=INT64ERR; + xmtime=EXTTIME_MAX; + FileSizeLess=INT64NDF; + FileSizeMore=INT64NDF; + HashType=HASH_CRC32; +#ifdef RAR_SMP + Threads=GetNumberOfThreads(); +#endif +#ifdef USE_QOPEN + QOpenMode=QOPEN_AUTO; +#endif } diff --git a/unrar/unrar/options.hpp b/unrar/unrar/options.hpp index 8af0e8b..e249eb5 100644 --- a/unrar/unrar/options.hpp +++ b/unrar/unrar/options.hpp @@ -1,37 +1,85 @@ #ifndef _RAR_OPTIONS_ #define _RAR_OPTIONS_ -#define DEFAULT_RECOVERY -1 +#define DEFAULT_RECOVERY -3 #define DEFAULT_RECVOLUMES -10 -enum PathExclMode { - EXCL_NONE,EXCL_BASEPATH,EXCL_SKIPWHOLEPATH,EXCL_SAVEFULLPATH, - EXCL_SKIPABSPATH,EXCL_ABSPATH +#define VOLSIZE_AUTO INT64NDF // Automatically detect the volume size. + +enum PATH_EXCL_MODE { + EXCL_UNCHANGED=0, // Process paths as is (default). + EXCL_SKIPWHOLEPATH, // -ep (exclude the path completely) + EXCL_BASEPATH, // -ep1 (exclude the base part of path) + EXCL_SAVEFULLPATH, // -ep2 (the full path without the disk letter) + EXCL_ABSPATH // -ep3 (the full path with the disk letter) }; + enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4, SOLID_VOLUME_DEPENDENT=8,SOLID_VOLUME_INDEPENDENT=16}; -enum {ARCTIME_NONE,ARCTIME_KEEP,ARCTIME_LATEST}; + +enum {ARCTIME_NONE=0,ARCTIME_KEEP,ARCTIME_LATEST}; + enum EXTTIME_MODE { - EXTTIME_NONE,EXTTIME_1S,EXTTIME_HIGH1,EXTTIME_HIGH2,EXTTIME_HIGH3 + EXTTIME_NONE=0,EXTTIME_1S,EXTTIME_MAX }; -enum {NAMES_ORIGINALCASE,NAMES_UPPERCASE,NAMES_LOWERCASE}; -enum MESSAGE_TYPE {MSG_STDOUT,MSG_STDERR,MSG_ERRONLY,MSG_NULL}; -enum OVERWRITE_MODE { - OVERWRITE_DEFAULT, // ask for extraction, silently overwrite for archiving +enum {NAMES_ORIGINALCASE=0,NAMES_UPPERCASE,NAMES_LOWERCASE}; + +enum MESSAGE_TYPE {MSG_STDOUT=0,MSG_STDERR,MSG_ERRONLY,MSG_NULL}; + +enum RECURSE_MODE +{ + RECURSE_NONE=0, // no recurse switches + RECURSE_DISABLE, // switch -r- + RECURSE_ALWAYS, // switch -r + RECURSE_WILDCARDS, // switch -r0 +}; + +enum OVERWRITE_MODE +{ + OVERWRITE_DEFAULT=0, // Ask when extracting, silently overwrite when archiving. OVERWRITE_ALL, OVERWRITE_NONE, OVERWRITE_AUTORENAME, OVERWRITE_FORCE_ASK }; -enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE }; +enum ARC_METADATA +{ + ARCMETA_NONE=0, + ARCMETA_SAVE, // -ams + ARCMETA_RESTORE // -amr +}; + +enum QOPEN_MODE { QOPEN_NONE, QOPEN_AUTO, QOPEN_ALWAYS }; + +enum RAR_CHARSET { RCH_DEFAULT=0,RCH_ANSI,RCH_OEM,RCH_UNICODE,RCH_UTF8 }; -#define MAX_FILTERS 16 +#define MAX_FILTER_TYPES 16 enum FilterState {FILTER_DEFAULT=0,FILTER_AUTO,FILTER_FORCE,FILTER_DISABLE}; +enum SAVECOPY_MODE { + SAVECOPY_NONE=0, SAVECOPY_SILENT, SAVECOPY_LIST, SAVECOPY_LISTEXIT, + SAVECOPY_DUPLISTEXIT +}; + +enum APPENDARCNAME_MODE +{ + APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNSUBDIR, + APPENDARCNAME_OWNDIR +}; + +enum POWER_MODE { + POWERMODE_KEEP=0,POWERMODE_OFF,POWERMODE_HIBERNATE,POWERMODE_SLEEP, + POWERMODE_RESTART +}; + + +// Need "forced off" state to turn off sound in GUI command line. +enum SOUND_NOTIFY_MODE {SOUND_NOTIFY_DEFAULT=0,SOUND_NOTIFY_ON,SOUND_NOTIFY_OFF}; + struct FilterMode { FilterState State; @@ -39,104 +87,136 @@ struct FilterMode int Param2; }; +#define MAX_GENERATE_MASK 128 + +// Here we store simple data types, which we can clear and move all together +// quickly. Rest of data types goes to CommandData. class RAROptions { public: RAROptions(); - ~RAROptions(); void Init(); uint ExclFileAttr; uint InclFileAttr; + + // We handle -ed and -e+d with special flags instead of attribute mask, + // so it works with both Windows and Unix archives. + bool ExclDir; + bool InclDir; + bool InclAttrSet; - uint WinSize; - char TempPath[NM]; - char SFXModule[NM]; - char ExtrPath[NM]; - wchar ExtrPathW[NM]; - char CommentFile[NM]; + size_t WinSize; + wchar TempPath[NM]; + wchar SFXModule[NM]; + +#ifdef USE_QOPEN + QOPEN_MODE QOpenMode; +#endif + + bool ConfigDisabled; // Switch -cfg-. + wchar ExtrPath[NM]; + wchar CommentFile[NM]; RAR_CHARSET CommentCharset; RAR_CHARSET FilelistCharset; - char ArcPath[NM]; - wchar ArcPathW[NM]; - char Password[MAXPASSWORD]; + RAR_CHARSET ErrlogCharset; + RAR_CHARSET RedirectCharset; + + wchar ArcPath[NM]; // For -ap<path>. + wchar ExclArcPath[NM]; // For -ep4<path> switch. bool EncryptHeaders; - char LogName[NM]; + bool SkipEncrypted; + + bool ManualPassword; // Password entered manually during operation, might need to clean for next archive. + + wchar LogName[NM]; MESSAGE_TYPE MsgStream; - bool Sound; + SOUND_NOTIFY_MODE Sound; OVERWRITE_MODE Overwrite; int Method; + HASH_TYPE HashType; int Recovery; int RecVolNumber; + ARC_METADATA ArcMetadata; bool DisablePercentage; bool DisableCopyright; bool DisableDone; + bool DisableNames; + bool PrintVersion; int Solid; int SolidCount; bool ClearArc; bool AddArcOnly; - bool AV; bool DisableComment; bool FreshFiles; bool UpdateFiles; - PathExclMode ExclPath; - int Recurse; - Int64 VolSize; - Array<Int64> NextVolSizes; - int CurVolNum; + PATH_EXCL_MODE ExclPath; + RECURSE_MODE Recurse; + int64 VolSize; + uint CurVolNum; bool AllYes; - bool DisableViewAV; + bool VerboseOutput; // -iv, display verbose output, used only in "WinRAR t" now. bool DisableSortSolid; int ArcTime; int ConvertNames; bool ProcessOwners; - bool SaveLinks; + bool SaveSymLinks; + bool SaveHardLinks; + bool AbsoluteLinks; int Priority; int SleepTime; bool KeepBroken; - bool EraseDisk; bool OpenShared; - bool ExclEmptyDir; bool DeleteFiles; - bool SyncFiles; + +#ifdef _WIN_ALL + bool AllowIncompatNames; // Allow names with trailing dots and spaces. +#endif + + +#ifndef SFX_MODULE bool GenerateArcName; - char GenerateMask[80]; + wchar GenerateMask[MAX_GENERATE_MASK]; + wchar DefGenerateMask[MAX_GENERATE_MASK]; +#endif + bool SyncFiles; bool ProcessEA; bool SaveStreams; bool SetCompressedAttr; - uint FileTimeOlder; - uint FileTimeNewer; - RarTime FileTimeBefore; - RarTime FileTimeAfter; - Int64 FileSizeLess; - Int64 FileSizeMore; - bool OldNumbering; + bool IgnoreGeneralAttr; + RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore; + bool FileMtimeBeforeOR,FileCtimeBeforeOR,FileAtimeBeforeOR; + RarTime FileMtimeAfter,FileCtimeAfter,FileAtimeAfter; + bool FileMtimeAfterOR,FileCtimeAfterOR,FileAtimeAfterOR; + int64 FileSizeLess; + int64 FileSizeMore; bool Lock; bool Test; bool VolumePause; - FilterMode FilterModes[MAX_FILTERS]; - char EmailTo[NM]; - int VersionControl; - bool NoEndBlock; - bool AppendArcNameToPath; - bool Shutdown; - EXTTIME_MODE xmtime; + FilterMode FilterModes[MAX_FILTER_TYPES]; + wchar EmailTo[NM]; + uint VersionControl; + APPENDARCNAME_MODE AppendArcNameToPath; + POWER_MODE Shutdown; + EXTTIME_MODE xmtime; // Extended time modes (time precision to store). EXTTIME_MODE xctime; EXTTIME_MODE xatime; - EXTTIME_MODE xarctime; - char CompressStdin[NM]; + bool PreserveAtime; + + // Read data from stdin and store in archive under a name specified here + // when archiving. Read an archive from stdin if any non-empty string + // is specified here when extracting. + wchar UseStdin[NM]; + + uint Threads; // We use it to init hash even if RAR_SMP is not defined. -#ifdef PACK_SMP - uint Threads; -#endif #ifdef RARDLL - char DllDestName[NM]; - wchar DllDestNameW[NM]; + wchar DllDestName[NM]; int DllOpMode; int DllError; LPARAM UserData; diff --git a/unrar/unrar/os.hpp b/unrar/unrar/os.hpp index fbcf3a0..4b21e49 100644 --- a/unrar/unrar/os.hpp +++ b/unrar/unrar/os.hpp @@ -8,141 +8,139 @@ #define INCL_BASE #endif -#if defined(_WIN_32) || defined(_EMX) -#define ENABLE_BAD_ALLOC +#if defined(RARDLL) && !defined(SILENT) +#define SILENT #endif +#include <new> +#include <string> +#include <vector> -#if defined(_WIN_32) || defined(_EMX) + +#if defined(_WIN_ALL) || defined(_EMX) #define LITTLE_ENDIAN -#define NM 1024 +#define NM 2048 + +#ifdef _WIN_ALL -#ifdef _WIN_32 - #define STRICT - #undef WINVER - #undef _WIN32_WINNT - #define WINVER 0x0400 - #define _WIN32_WINNT 0x0300 +// We got a report that just "#define STRICT" is incompatible with +// "#define STRICT 1" in Windows 10 SDK minwindef.h and depending on the order +// in which these statements are reached this may cause a compiler warning +// and build break for other projects incorporating this source. +// So we changed it to "#define STRICT 1". +#ifndef STRICT +#define STRICT 1 +#endif + +// 'ifndef' check here is needed for unrar.dll header to avoid macro +// re-definition warnings in third party projects. +#ifndef UNICODE +#define UNICODE +#define _UNICODE // Set _T() macro to convert from narrow to wide strings. +#endif +#if 0 +// 2021.09.05: Allow newer Vista+ APIs like IFileOpenDialog for WinRAR, +// but still keep SFX modules XP compatible. +#define WINVER _WIN32_WINNT_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#else +#define WINVER _WIN32_WINNT_WINXP +#define _WIN32_WINNT _WIN32_WINNT_WINXP +#endif + +#if !defined(ZIPSFX) +#define RAR_SMP +#endif #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <prsht.h> +#include <shlwapi.h> +#pragma comment(lib, "Shlwapi.lib") +#include <PowrProf.h> +#pragma comment(lib, "PowrProf.lib") +#include <shellapi.h> +#include <shlobj.h> +#include <winioctl.h> +#include <wincrypt.h> +#include <wchar.h> +#include <wctype.h> -#ifndef _WIN_CE - #include <shellapi.h> - #include <shlobj.h> - #include <winioctl.h> -#endif -#endif +#endif // _WIN_ALL -#ifndef _WIN_CE - #include <sys/types.h> - #include <sys/stat.h> - #include <dos.h> -#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <dos.h> -#if !defined(_EMX) && !defined(_MSC_VER) && !defined(_WIN_CE) +#if !defined(_EMX) && !defined(_MSC_VER) #include <dir.h> #endif #ifdef _MSC_VER - #define for if (0) ; else for -#ifndef _WIN_CE #include <direct.h> -#endif -#else - #include <dirent.h> -#endif - -#ifndef _WIN_CE - #include <share.h> -#endif - -#if defined(ENABLE_BAD_ALLOC) && !defined(_WIN_CE) - #include <new.h> -#endif + #include <intrin.h> -#ifdef _EMX - #include <unistd.h> - #include <pwd.h> - #include <grp.h> - #include <errno.h> - #ifdef _DJGPP - #include <utime.h> - #else - #include <os2.h> - #include <sys/utime.h> - #include <emx/syscalls.h> + // Use SSE only for x86/x64, not ARM Windows. + #if defined(_M_IX86) || defined(_M_X64) + #define USE_SSE + #define SSE_ALIGNMENT 16 #endif #else - #if defined(_MSC_VER) || defined(__MINGW32__) - #include <exception> - #else - #include <except.h> - #endif -#endif + #include <dirent.h> +#endif // _MSC_VER #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <ctype.h> -#ifndef _WIN_CE - #include <fcntl.h> - #include <dos.h> - #include <io.h> - #include <time.h> - #include <signal.h> -#endif +#include <fcntl.h> +#include <dos.h> +#include <io.h> +#include <time.h> +#include <signal.h> -/* -#ifdef _WIN_32 -#pragma hdrstop -#endif -*/ +#define SAVE_LINKS #define ENABLE_ACCESS -#define DefConfigName "rar.ini" -#define DefLogName "rar.log" +#define DefConfigName L"rar.ini" +#define DefLogName L"rar.log" -#define PATHDIVIDER "\\" -#define PATHDIVIDERW L"\\" +#define SPATHDIVIDER L"\\" #define CPATHDIVIDER '\\' -#define MASKALL "*" -#define MASKALLW L"*" +#define MASKALL L"*" #define READBINARY "rb" #define READTEXT "rt" #define UPDATEBINARY "r+b" #define CREATEBINARY "w+b" +#define WRITEBINARY "wb" #define APPENDTEXT "at" -#if defined(_WIN_32) +#if defined(_WIN_ALL) #ifdef _MSC_VER #define _stdfunction __cdecl + #define _forceinline __forceinline #else #define _stdfunction _USERENTRY + #define _forceinline inline #endif #else #define _stdfunction + #define _forceinline inline #endif -#endif +#endif // defined(_WIN_ALL) || defined(_EMX) #ifdef _UNIX -#define NM 1024 - -#ifdef _BEOS -#include <be/kernel/fs_info.h> -#include <be/kernel/fs_attr.h> -#endif +#define NM 2048 #include <unistd.h> #include <sys/types.h> @@ -151,13 +149,16 @@ #if defined(__QNXNTO__) #include <sys/param.h> #endif -#if defined(__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined(__APPLE__) - #include <sys/param.h> - #include <sys/mount.h> -#else +#if defined(RAR_SMP) && defined(__APPLE__) + #include <sys/sysctl.h> +#endif +#ifndef SFX_MODULE + #include <sys/statvfs.h> #endif #include <pwd.h> #include <grp.h> +#include <wchar.h> +#include <wctype.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -171,29 +172,35 @@ #include <utime.h> #include <locale.h> + #ifdef S_IFLNK #define SAVE_LINKS #endif +#if defined(__linux) || defined(__FreeBSD__) +#include <sys/time.h> +#define USE_LUTIMES +#endif + #define ENABLE_ACCESS -#define DefConfigName ".rarrc" -#define DefLogName ".rarlog" +#define DefConfigName L".rarrc" +#define DefLogName L".rarlog" -#define PATHDIVIDER "/" -#define PATHDIVIDERW L"/" +#define SPATHDIVIDER L"/" #define CPATHDIVIDER '/' -#define MASKALL "*" -#define MASKALLW L"*" +#define MASKALL L"*" #define READBINARY "r" #define READTEXT "r" #define UPDATEBINARY "r+" #define CREATEBINARY "w+" +#define WRITEBINARY "w" #define APPENDTEXT "a" #define _stdfunction +#define _forceinline inline #ifdef _APPLE #if defined(__BIG_ENDIAN__) && !defined(BIG_ENDIAN) @@ -212,31 +219,59 @@ #endif #endif +// Unlike Apple x64, utimensat shall be available in all Apple M1 systems. +#if _POSIX_C_SOURCE >= 200809L || defined(__APPLE__) && defined(__arm64__) + #define UNIX_TIME_NS // Nanosecond time precision in Unix. #endif - typedef const char* MSGID; +#endif // _UNIX + +#if 0 + #define MSGID_INT + typedef int MSGID; +#else + typedef const wchar* MSGID; +#endif + +#ifndef SSE_ALIGNMENT // No SSE use and no special data alignment is required. + #define SSE_ALIGNMENT 1 +#endif #define safebuf static +// Solaris defines _LITTLE_ENDIAN or _BIG_ENDIAN. +#if defined(_LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN) + #define LITTLE_ENDIAN +#endif +#if defined(_BIG_ENDIAN) && !defined(BIG_ENDIAN) + #define BIG_ENDIAN +#endif + +#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) + #if defined(__i386) || defined(i386) || defined(__i386__) || defined(__x86_64) + #define LITTLE_ENDIAN + #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) + #define LITTLE_ENDIAN + #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN || defined(__BIG_ENDIAN__) + #define BIG_ENDIAN + #else + #error "Neither LITTLE_ENDIAN nor BIG_ENDIAN are defined. Define one of them." + #endif +#endif + #if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) #if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN #undef LITTLE_ENDIAN #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN #undef BIG_ENDIAN #else - #error "Both LITTLE_ENDIAN and BIG_ENDIAN are defined. Undef something one" + #error "Both LITTLE_ENDIAN and BIG_ENDIAN are defined. Undef one of them." #endif #endif -#if !defined(BIG_ENDIAN) && !defined(_WIN_CE) && defined(_WIN_32) -/* allow not aligned integer access, increases speed in some operations */ -#define ALLOW_NOT_ALIGNED_INT -#endif - -#if defined(__sparc) || defined(sparc) || defined(__sparcv9) -/* prohibit not aligned access to data structures in text comression - algorithm, increases memory requirements */ -#define STRICT_ALIGNMENT_REQUIRED +#if !defined(BIG_ENDIAN) && defined(_WIN_ALL) || defined(__i386__) || defined(__x86_64__) +// Allow not aligned integer access, increases speed in some operations. +#define ALLOW_MISALIGNED #endif #endif // _RAR_OS_ diff --git a/unrar/unrar/os2ea.cpp b/unrar/unrar/os2ea.cpp deleted file mode 100644 index f418615..0000000 --- a/unrar/unrar/os2ea.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include <os2.h> - - - -void ExtractOS2EA(Archive &Arc,char *FileName) -{ - if (_osmode != OS2_MODE) - { - mprintf(St(MSkipEA)); - return; - } - - if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - - if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER) - { - Log(Arc.FileName,St(MEAUnknHeader),FileName); - ErrHandler.SetErrorCode(WARNING); - return; - } - - struct StructEAOP2 - { - char *GEAPtr; - char *FEAPtr; - unsigned long Error; - } EAOP2; - - ComprDataIO DataIO; - Unpack Unpack(&DataIO); - Unpack.Init(); - - Array<unsigned char> UnpData(Arc.EAHead.UnpSize); - DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); - DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); - DataIO.EnableShowProgress(false); - DataIO.SetFiles(&Arc,NULL); - Unpack.SetDestSize(Arc.EAHead.UnpSize); - Unpack.DoUnpack(Arc.EAHead.UnpVer,false); - - if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC) - { - Log(Arc.FileName,St(MEABroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; - } - - EAOP2.FEAPtr=(char *)&UnpData[0]; - EAOP2.GEAPtr=NULL; - if (DosSetPathInfo((unsigned char *)FileName,2,&EAOP2,sizeof(EAOP2),0x10)!=0) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - } - File::SetCloseFileTimeByName(FileName,&Arc.NewLhd.mtime,&Arc.NewLhd.atime); - mprintf(St(MShowEA)); -} - - -void ExtractOS2EANew(Archive &Arc,char *FileName) -{ - if (_osmode != OS2_MODE) - { - mprintf(St(MSkipEA)); - return; - } - - Array<byte> SubData; - if (!Arc.ReadSubData(&SubData,NULL)) - return; - - struct StructEAOP2 - { - char *GEAPtr; - char *FEAPtr; - unsigned long Error; - } EAOP2; - - EAOP2.FEAPtr=(char *)&SubData[0]; - EAOP2.GEAPtr=NULL; - if (DosSetPathInfo((unsigned char *)FileName,2,&EAOP2,sizeof(EAOP2),0x10)!=0) - { - Log(Arc.FileName,St(MCannotSetEA),FileName); - ErrHandler.SetErrorCode(WARNING); - } - File::SetCloseFileTimeByName(FileName,&Arc.NewLhd.mtime,&Arc.NewLhd.atime); - mprintf(St(MShowEA)); -} - diff --git a/unrar/unrar/pathfn.cpp b/unrar/unrar/pathfn.cpp index 3621916..e959e9d 100644 --- a/unrar/unrar/pathfn.cpp +++ b/unrar/unrar/pathfn.cpp @@ -1,99 +1,47 @@ #include "rar.hpp" -char* PointToName(const char *Path) -{ - const char *Found=NULL; - for (const char *s=Path;*s!=0;s=charnext(s)) - if (IsPathDiv(*s)) - Found=(char*)(s+1); - if (Found!=NULL) - return((char*)Found); - return (char*)((*Path && IsDriveDiv(Path[1]) && charnext(Path)==Path+1) ? Path+2:Path); -} - - wchar* PointToName(const wchar *Path) { - for (int I=strlenw(Path)-1;I>=0;I--) + for (int I=(int)wcslen(Path)-1;I>=0;I--) if (IsPathDiv(Path[I])) return (wchar*)&Path[I+1]; - return (wchar*)((*Path && IsDriveDiv(Path[1])) ? Path+2:Path); + return (wchar*)((*Path!=0 && IsDriveDiv(Path[1])) ? Path+2:Path); } -char* PointToLastChar(const char *Path) -{ - for (const char *s=Path,*p=Path;;p=s,s=charnext(s)) - if (*s==0) - return((char *)p); -} - - - - -char* ConvertPath(const char *SrcPath,char *DestPath) +wchar* PointToLastChar(const wchar *Path) { - const char *DestPtr=SrcPath; - - /* prevents \..\ in any part of path string */ - for (const char *s=DestPtr;*s!=0;s++) - if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) - DestPtr=s+4; - - /* removes any sequence of . and \ in the beginning of path string */ - while (*DestPtr) - { - const char *s=DestPtr; - if (s[0] && IsDriveDiv(s[1])) - s+=2; - else - if (s[0]=='\\' && s[1]=='\\') - { - const char *Slash=strchr(s+2,'\\'); - if (Slash!=NULL && (Slash=strchr(Slash+1,'\\'))!=NULL) - s=Slash+1; - } - for (const char *t=s;*t!=0;t++) - if (IsPathDiv(*t)) - s=t+1; - else - if (*t!='.') - break; - if (s==DestPtr) - break; - DestPtr=s; - } - - /* code above does not remove last "..", doing here */ - if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0) - DestPtr+=2; - - if (DestPath!=NULL) - { - char TmpStr[NM]; - strncpyz(TmpStr,DestPtr,ASIZE(TmpStr)); - strcpy(DestPath,TmpStr); - } - return((char *)DestPtr); + size_t Length=wcslen(Path); + return (wchar*)(Length>0 ? Path+Length-1:Path); } -wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath) +wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize) { const wchar *DestPtr=SrcPath; + + // Prevent \..\ in any part of path string. for (const wchar *s=DestPtr;*s!=0;s++) if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3])) DestPtr=s+4; - while (*DestPtr) + + // Remove any amount of <d>:\ and any sequence of . and \ in the beginning of path string. + while (*DestPtr!=0) { const wchar *s=DestPtr; - if (s[0] && IsDriveDiv(s[1])) + if (s[0]!=0 && IsDriveDiv(s[1])) s+=2; - if (s[0]=='\\' && s[1]=='\\') + + // Skip UNC Windows \\server\share\ or Unix //server/share/ + if (IsPathDiv(s[0]) && IsPathDiv(s[1])) { - const wchar *Slash=strchrw(s+2,'\\'); - if (Slash!=NULL && (Slash=strchrw(Slash+1,'\\'))!=NULL) - s=Slash+1; + uint SlashCount=0; + for (const wchar *t=s+2;*t!=0;t++) + if (IsPathDiv(*t) && ++SlashCount==2) + { + s=t+1; // Found two more path separators after leading two. + break; + } } for (const wchar *t=s;*t!=0;t++) if (IsPathDiv(*t)) @@ -105,122 +53,96 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath) break; DestPtr=s; } + + // Code above does not remove last "..", doing here. + if (DestPtr[0]=='.' && DestPtr[1]=='.' && DestPtr[2]==0) + DestPtr+=2; + if (DestPath!=NULL) { + // SrcPath and DestPath can point to same memory area, + // so we use the temporary buffer for copying. wchar TmpStr[NM]; - strncpyw(TmpStr,DestPtr,sizeof(TmpStr)/sizeof(TmpStr[0])-1); - strcpyw(DestPath,TmpStr); + wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr)); + wcsncpyz(DestPath,TmpStr,DestSize); } - return((wchar *)DestPtr); + return (wchar *)DestPtr; } -void SetExt(char *Name,const char *NewExt) +void SetName(wchar *FullName,const wchar *Name,size_t MaxSize) { - char *Dot=GetExt(Name); - if (NewExt==NULL) - { - if (Dot!=NULL) - *Dot=0; - } - else - if (Dot==NULL) - { - strcat(Name,"."); - strcat(Name,NewExt); - } - else - strcpy(Dot+1,NewExt); + wchar *NamePtr=PointToName(FullName); + wcsncpyz(NamePtr,Name,MaxSize-(NamePtr-FullName)); } -#ifndef SFX_MODULE -void SetExt(wchar *Name,const wchar *NewExt) +void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize) { if (Name==NULL || *Name==0) return; wchar *Dot=GetExt(Name); - if (NewExt==NULL) + if (Dot!=NULL) + *Dot=0; + if (NewExt!=NULL) { - if (Dot!=NULL) - *Dot=0; + wcsncatz(Name,L".",MaxSize); + wcsncatz(Name,NewExt,MaxSize); } - else - if (Dot==NULL) - { - strcatw(Name,L"."); - strcatw(Name,NewExt); - } - else - strcpyw(Dot+1,NewExt); } -#endif #ifndef SFX_MODULE -void SetSFXExt(char *SFXName) -{ -#ifdef _UNIX - SetExt(SFXName,"sfx"); -#endif - -#if defined(_WIN_32) || defined(_EMX) - SetExt(SFXName,"exe"); -#endif -} -#endif - - -#ifndef SFX_MODULE -void SetSFXExt(wchar *SFXName) +void SetSFXExt(wchar *SFXName,size_t MaxSize) { if (SFXName==NULL || *SFXName==0) return; #ifdef _UNIX - SetExt(SFXName,L"sfx"); + SetExt(SFXName,L"sfx",MaxSize); #endif -#if defined(_WIN_32) || defined(_EMX) - SetExt(SFXName,L"exe"); +#if defined(_WIN_ALL) || defined(_EMX) + SetExt(SFXName,L"exe",MaxSize); #endif } #endif -char *GetExt(const char *Name) -{ - return(strrchrd(PointToName(Name),'.')); -} - - +// 'Ext' is an extension with the leading dot, like L".rar". wchar *GetExt(const wchar *Name) { - return(Name==NULL ? (wchar *)L"":strrchrw(PointToName(Name),'.')); + return Name==NULL ? NULL:wcsrchr(PointToName(Name),'.'); } -bool CmpExt(const char *Name,const char *Ext) +// 'Ext' is an extension without the leading dot, like L"rar". +bool CmpExt(const wchar *Name,const wchar *Ext) { - char *NameExt=GetExt(Name); - return(NameExt!=NULL && stricomp(NameExt+1,Ext)==0); + wchar *NameExt=GetExt(Name); + return NameExt!=NULL && wcsicomp(NameExt+1,Ext)==0; } -bool IsWildcard(const char *Str,const wchar *StrW) +bool IsWildcard(const wchar *Str) { - if (StrW!=NULL && *StrW!=0) - return(strpbrkw(StrW,L"*?")!=NULL); - return(Str==NULL ? false:strpbrk(Str,"*?")!=NULL); + if (Str==NULL) + return false; +#ifdef _WIN_ALL + // Not treat the special NTFS \\?\d: path prefix as a wildcard. + if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\') + Str+=4; +#endif + return wcspbrk(Str,L"*?")!=NULL; } bool IsPathDiv(int Ch) { -#if defined(_WIN_32) || defined(_EMX) - return(Ch=='\\' || Ch=='/'); +#ifdef _WIN_ALL + return Ch=='\\' || Ch=='/'; #else - return(Ch==CPATHDIVIDER); + return Ch==CPATHDIVIDER; #endif } @@ -228,70 +150,67 @@ bool IsPathDiv(int Ch) bool IsDriveDiv(int Ch) { #ifdef _UNIX - return(false); + return false; #else - return(Ch==':'); + return Ch==':'; #endif } -int GetPathDisk(const char *Path) +bool IsDriveLetter(const wchar *Path) { - if (IsDiskLetter(Path)) - return(etoupper(*Path)-'A'); - else - return(-1); + wchar Letter=etoupperw(Path[0]); + return Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1]); } -void AddEndSlash(char *Path) +int GetPathDisk(const wchar *Path) { - char *LastChar=PointToLastChar(Path); - if (*LastChar!=0 && *LastChar!=CPATHDIVIDER) - strcat(LastChar,PATHDIVIDER); + if (IsDriveLetter(Path)) + return etoupperw(*Path)-'A'; + else + return -1; } -void AddEndSlash(wchar *Path) +void AddEndSlash(wchar *Path,size_t MaxLength) { - int Length=strlenw(Path); - if (Length>0 && Path[Length-1]!=CPATHDIVIDER) - strcatw(Path,PATHDIVIDERW); + size_t Length=wcslen(Path); + if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1<MaxLength) + { + Path[Length]=CPATHDIVIDER; + Path[Length+1]=0; + } } -// returns file path including the trailing path separator symbol -void GetFilePath(const char *FullName,char *Path,int MaxLength) +void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize) { - int PathLength=Min(MaxLength-1,PointToName(FullName)-FullName); - strncpy(Path,FullName,PathLength); - Path[PathLength]=0; + // 'Path', 'Name' and 'Pathname' can point to same memory area. So we use + // the temporary buffer instead of constructing the name in 'Pathname'. + wchar OutName[NM]; + wcsncpyz(OutName,Path,ASIZE(OutName)); + // Do not add slash to d:, we want to allow relative paths like d:filename. + if (!IsDriveLetter(Path) || Path[2]!=0) + AddEndSlash(OutName,ASIZE(OutName)); + wcsncatz(OutName,Name,ASIZE(OutName)); + wcsncpyz(Pathname,OutName,MaxSize); } -// returns file path including the trailing path separator symbol -void GetFilePath(const wchar *FullName,wchar *Path,int MaxLength) +// Returns file path including the trailing path separator symbol. +void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength) { - int PathLength=Min(MaxLength-1,PointToName(FullName)-FullName); - strncpyw(Path,FullName,PathLength); + if (MaxLength==0) + return; + size_t PathLength=Min(MaxLength-1,size_t(PointToName(FullName)-FullName)); + wcsncpy(Path,FullName,PathLength); Path[PathLength]=0; } -// removes name and returns file path without the trailing -// path separator symbol -void RemoveNameFromPath(char *Path) -{ - char *Name=PointToName(Path); - if (Name>=Path+2 && (!IsDriveDiv(Path[1]) || Name>=Path+4)) - Name--; - *Name=0; -} - - -#ifndef SFX_MODULE -// removes name and returns file path without the trailing -// path separator symbol +// Removes name and returns file path without the trailing +// path separator symbol. void RemoveNameFromPath(wchar *Path) { wchar *Name=PointToName(Path); @@ -299,11 +218,10 @@ void RemoveNameFromPath(wchar *Path) Name--; *Name=0; } -#endif -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) -void GetAppDataPath(char *Path) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create) { LPMALLOC g_pMalloc; SHGetMalloc(&g_pMalloc); @@ -313,106 +231,89 @@ void GetAppDataPath(char *Path) if (SHGetSpecialFolderLocation(NULL,CSIDL_APPDATA,&ppidl)==NOERROR && SHGetPathFromIDList(ppidl,Path) && *Path!=0) { - AddEndSlash(Path); - strcat(Path,"WinRAR"); - Success=FileExist(Path) || MakeDir(Path,NULL,0)==MKDIR_SUCCESS; - } - if (!Success) - { - GetModuleFileName(NULL,Path,NM); - RemoveNameFromPath(Path); + AddEndSlash(Path,MaxSize); + wcsncatz(Path,L"WinRAR",MaxSize); + Success=FileExist(Path); + if (!Success && Create) + Success=MakeDir(Path,false,0)==MKDIR_SUCCESS; } g_pMalloc->Free(ppidl); + return Success; } #endif -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) -void GetRarDataPath(char *Path) +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create) { *Path=0; HKEY hKey; - if (RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\WinRAR\\Paths",0, + if (RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\WinRAR\\Paths",0, KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS) { - DWORD DataSize=NM,Type; - RegQueryValueEx(hKey,"AppData",0,&Type,(BYTE *)Path,&DataSize); + DWORD DataSize=(DWORD)MaxSize,Type; + RegQueryValueEx(hKey,L"AppData",0,&Type,(BYTE *)Path,&DataSize); RegCloseKey(hKey); } if (*Path==0 || !FileExist(Path)) - GetAppDataPath(Path); + if (!GetAppDataPath(Path,MaxSize,Create)) + { + GetModuleFileName(NULL,Path,(DWORD)MaxSize); + RemoveNameFromPath(Path); + } } #endif #ifndef SFX_MODULE -bool EnumConfigPaths(char *Path,int Number) -{ -#ifdef _EMX - static char RARFileName[NM]; - if (Number==-1) - strcpy(RARFileName,Path); - if (Number!=0) - return(false); -#ifndef _DJGPP - if (_osmode==OS2_MODE) - { - PTIB ptib; - PPIB ppib; - DosGetInfoBlocks(&ptib, &ppib); - DosQueryModuleName(ppib->pib_hmte,NM,Path); - } - else -#endif - strcpy(Path,RARFileName); - RemoveNameFromPath(Path); - return(true); -#elif defined(_UNIX) - static const char *AltPath[]={ - "/etc","/etc/rar","/usr/lib","/usr/local/lib","/usr/local/etc" +bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create) +{ +#ifdef _UNIX + static const wchar *ConfPath[]={ + L"/etc", L"/etc/rar", L"/usr/lib", L"/usr/local/lib", L"/usr/local/etc" }; if (Number==0) { char *EnvStr=getenv("HOME"); - strncpy(Path, (EnvStr==NULL) ? AltPath[0] : EnvStr, NM-1); - Path[NM-1]=0; - return(true); + if (EnvStr!=NULL) + CharToWide(EnvStr,Path,MaxSize); + else + wcsncpyz(Path,ConfPath[0],MaxSize); + return true; } Number--; - if (Number<0 || Number>=sizeof(AltPath)/sizeof(AltPath[0])) - return(false); - strcpy(Path,AltPath[Number]); - return(true); -#elif defined(_WIN_32) - - if (Number<0 || Number>1) - return(false); + if (Number>=ASIZE(ConfPath)) + return false; + wcsncpyz(Path,ConfPath[Number], MaxSize); + return true; +#elif defined(_WIN_ALL) + if (Number>1) + return false; if (Number==0) - GetRarDataPath(Path); + GetRarDataPath(Path,MaxSize,Create); else { - GetModuleFileName(NULL,Path,NM); + GetModuleFileName(NULL,Path,(DWORD)MaxSize); RemoveNameFromPath(Path); } - return(true); - + return true; #else - return(false); + return false; #endif } #endif #ifndef SFX_MODULE -void GetConfigName(const char *Name,char *FullName,bool CheckExist) +void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create) { *FullName=0; - for (int I=0;EnumConfigPaths(FullName,I);I++) + for (uint I=0;EnumConfigPaths(I,FullName,MaxSize,Create);I++) { - AddEndSlash(FullName); - strcat(FullName,Name); + AddEndSlash(FullName,MaxSize); + wcsncatz(FullName,Name,MaxSize); if (!CheckExist || WildFileExist(FullName)) break; } @@ -420,52 +321,84 @@ void GetConfigName(const char *Name,char *FullName,bool CheckExist) #endif -// returns a pointer to rightmost digit of volume number -char* GetVolNumPart(char *ArcName) +// Returns a pointer to rightmost digit of volume number or to beginning +// of file name if numeric part is missing. +wchar* GetVolNumPart(const wchar *ArcName) { - char *ChPtr=ArcName+strlen(ArcName)-1; - while (!isdigit(*ChPtr) && ChPtr>ArcName) + // We do not want to increment any characters in path component. + ArcName=PointToName(ArcName); + + if (*ArcName==0) + return (wchar *)ArcName; + + // Pointing to last name character. + const wchar *ChPtr=ArcName+wcslen(ArcName)-1; + + // Skipping the archive extension. + while (!IsDigit(*ChPtr) && ChPtr>ArcName) ChPtr--; - char *NumPtr=ChPtr; - while (isdigit(*NumPtr) && NumPtr>ArcName) + + // Skipping the numeric part of name. + const wchar *NumPtr=ChPtr; + while (IsDigit(*NumPtr) && NumPtr>ArcName) NumPtr--; + + // Searching for first numeric part in names like name.part##of##.rar. + // Stop search on the first dot. while (NumPtr>ArcName && *NumPtr!='.') { - if (isdigit(*NumPtr)) + if (IsDigit(*NumPtr)) { - char *Dot=strchrd(PointToName(ArcName),'.'); + // Validate the first numeric part only if it has a dot somewhere + // before it. + const wchar *Dot=wcschr(ArcName,'.'); if (Dot!=NULL && Dot<NumPtr) ChPtr=NumPtr; break; } NumPtr--; } - return(ChPtr); + return (wchar *)ChPtr; } -void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumbering) +void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering) { - char *ChPtr; + wchar *ChPtr; if ((ChPtr=GetExt(ArcName))==NULL) { - strcat(ArcName,".rar"); + wcsncatz(ArcName,L".rar",MaxLength); ChPtr=GetExt(ArcName); } else - if (ChPtr[1]==0 || stricomp(ChPtr+1,"exe")==0 || stricomp(ChPtr+1,"sfx")==0) - strcpy(ChPtr+1,"rar"); + if (ChPtr[1]==0 || wcsicomp(ChPtr,L".exe")==0 || wcsicomp(ChPtr,L".sfx")==0) + wcsncpyz(ChPtr,L".rar",MaxLength-(ChPtr-ArcName)); + + if (ChPtr==NULL || *ChPtr!='.' || ChPtr[1]==0) + { + // Normally we shall have some extension here. If we don't, it means + // the name has no extension and buffer has no free space to append one. + // Let's clear the name to prevent a new call with same name and return. + *ArcName=0; + return; + } + if (!OldNumbering) { ChPtr=GetVolNumPart(ArcName); + // We should not check for IsDigit(*ChPtr) here and should increment + // even non-digits. If we got a corrupt archive with volume flag, + // but without numeric part, we still need to modify its name somehow, + // so while (exist(name)) {NextVolumeName()} loops do not run infinitely. while ((++(*ChPtr))=='9'+1) { *ChPtr='0'; ChPtr--; - if (ChPtr<ArcName || !isdigit(*ChPtr)) + if (ChPtr<ArcName || !IsDigit(*ChPtr)) { - for (char *EndPtr=ArcName+strlen(ArcName);EndPtr!=ChPtr;EndPtr--) + // Convert .part:.rar (.part9.rar after increment) to part10.rar. + for (wchar *EndPtr=ArcName+wcslen(ArcName);EndPtr!=ChPtr;EndPtr--) *(EndPtr+1)=*EndPtr; *(ChPtr+1)='1'; break; @@ -473,15 +406,15 @@ void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumberi } } else - if (!isdigit(*(ChPtr+2)) || !isdigit(*(ChPtr+3))) - strcpy(ChPtr+2,"00"); + if (!IsDigit(ChPtr[2]) || !IsDigit(ChPtr[3])) + wcsncpyz(ChPtr+2,L"00",MaxLength-(ChPtr-ArcName)-2); // From .rar to .r00. else { - ChPtr+=3; - while ((++(*ChPtr))=='9'+1) - if (*(ChPtr-1)=='.') + ChPtr+=wcslen(ChPtr)-1; // Set to last character. + while (++(*ChPtr)=='9'+1) + if (ChPtr<=ArcName || *(ChPtr-1)=='.') { - *ChPtr='A'; + *ChPtr='a'; // From .999 to .a00 if started from .001 or for too short names. break; } else @@ -490,291 +423,664 @@ void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumberi ChPtr--; } } - if (ArcNameW!=NULL && *ArcNameW!=0) - { - // Copy incremented trailing low ASCII volume name part to Unicode name. - // It is simpler than implementing Unicode version of entire function. - char *NumPtr=GetVolNumPart(ArcName); - - // moving to first digit in volume number - while (NumPtr>ArcName && isdigit(*NumPtr) && isdigit(*(NumPtr-1))) - NumPtr--; - - // also copy the first character before volume number, - // because it can be changed when going from .r99 to .s00 - if (NumPtr>ArcName) - NumPtr--; - - int CharsToCopy=strlen(ArcName)-(NumPtr-ArcName); - int DestPos=strlenw(ArcNameW)-CharsToCopy; - if (DestPos>0) - { - CharToWide(NumPtr,ArcNameW+DestPos,MaxLength-DestPos-1); - ArcNameW[MaxLength-1]=0; - } - } } -bool IsNameUsable(const char *Name) +bool IsNameUsable(const wchar *Name) { -#ifndef _UNIX - if (Name[0] && Name[1] && strchr(Name+2,':')!=NULL) - return(false); - for (const char *s=Name;*s!=0;s=charnext(s)) + // We were asked to apply Windows-like conversion in Linux in case + // files are unpacked to Windows share. This code is invoked only + // if file failed to be created, so it doesn't affect extraction + // of Unix compatible names to native Unix drives. +#ifdef _UNIX + // Windows shares in Unix do not allow the drive letter, + // so unlike Windows version, we check all characters here. + if (wcschr(Name,':')!=NULL) + return false; +#else + if (Name[0]!=0 && Name[1]!=0 && wcschr(Name+2,':')!=NULL) + return false; +#endif + for (const wchar *s=Name;*s!=0;s++) { - if (*s<32) - return(false); - if (*s==' ' && IsPathDiv(s[1])) - return(false); - } + if ((uint)*s<32) + return false; + + // It is for Windows shares in Unix. We can create such names in Windows. +#ifdef _UNIX + // No spaces or dots before the path separator are allowed in Windows + // shares. But they are allowed and automtically removed at the end of + // file or folder name, so it is useless to replace them here. + // Since such files or folders are created successfully, a supposed + // conversion here would never be invoked. + if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) + return false; #endif - return(*Name!=0 && strpbrk(Name,"?*<>|\"")==NULL); + } + return *Name!=0 && wcspbrk(Name,L"?*<>|\"")==NULL; } -void MakeNameUsable(char *Name,bool Extended) + + +void MakeNameUsable(wchar *Name,bool Extended) { - for (char *s=Name;*s!=0;s=charnext(s)) + for (wchar *s=Name;*s!=0;s++) { - if (strchr(Extended ? "?*<>|\"":"?*",*s)!=NULL || Extended && *s<32) - *s='_'; -#ifdef _EMX - if (*s=='=') + if (wcschr(Extended ? L"?*<>|\"":L"?*",*s)!=NULL || Extended && (uint)*s<32) *s='_'; -#endif -#ifndef _UNIX +#ifdef _UNIX + // We were asked to apply Windows-like conversion in Linux in case + // files are unpacked to Windows share. This code is invoked only + // if file failed to be created, so it doesn't affect extraction + // of Unix compatible names to native Unix drives. + if (Extended) + { + // Windows shares in Unix do not allow the drive letter, + // so unlike Windows version, we check all characters here. + if (*s==':') + *s='_'; + + // No spaces or dots before the path separator are allowed on Windows + // shares. But they are allowed and automatically removed at the end of + // file or folder name, so it is useless to replace them here. + // Since such files or folders are created successfully, a supposed + // conversion here would never be invoked. + if ((*s==' ' || *s=='.') && IsPathDiv(s[1])) + *s='_'; + } +#else if (s-Name>1 && *s==':') *s='_'; - if (*s==' ' && IsPathDiv(s[1])) +#if 0 // We already can create such files. + // Remove ' ' and '.' before path separator, but allow .\ and ..\. + if (IsPathDiv(s[1]) && (*s==' ' || *s=='.' && s>Name && + !IsPathDiv(s[-1]) && (s[-1]!='.' || s>Name+1 && !IsPathDiv(s[-2])))) *s='_'; #endif +#endif } } -char* UnixSlashToDos(char *SrcName,char *DestName,uint MaxLength) +void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength) { - if (DestName!=NULL && DestName!=SrcName) - if (strlen(SrcName)>=MaxLength) - { - *DestName=0; - return(DestName); - } - else - strcpy(DestName,SrcName); - for (char *s=SrcName;*s!=0;s=charnext(s)) - { - if (*s=='/') - if (DestName==NULL) - *s='\\'; - else - DestName[s-SrcName]='\\'; - } - return(DestName==NULL ? SrcName:DestName); + size_t Copied=0; + for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied]; + DestName[Copied]=0; } -char* DosSlashToUnix(char *SrcName,char *DestName,uint MaxLength) +void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength) { - if (DestName!=NULL && DestName!=SrcName) - if (strlen(SrcName)>=MaxLength) - { - *DestName=0; - return(DestName); - } - else - strcpy(DestName,SrcName); - for (char *s=SrcName;*s!=0;s=charnext(s)) - { - if (*s=='\\') - if (DestName==NULL) - *s='/'; - else - DestName[s-SrcName]='/'; - } - return(DestName==NULL ? SrcName:DestName); + size_t Copied=0; + for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied]; + DestName[Copied]=0; +} + + +void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength) +{ + size_t Copied=0; + for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='/' ? '\\':SrcName[Copied]; + DestName[Copied]=0; +} + + +void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength) +{ + size_t Copied=0; + for (;Copied<MaxLength-1 && SrcName[Copied]!=0;Copied++) + DestName[Copied]=SrcName[Copied]=='\\' ? '/':SrcName[Copied]; + DestName[Copied]=0; } -wchar* UnixSlashToDos(wchar *SrcName,wchar *DestName,uint MaxLength) +void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize) { - if (DestName!=NULL && DestName!=SrcName) - if (strlenw(SrcName)>=MaxLength) + if (Src==NULL || *Src==0) + { + if (MaxSize>0) + *Dest=0; + return; + } +#ifdef _WIN_ALL + { + wchar FullName[NM],*NamePtr; + DWORD Code=GetFullPathName(Src,ASIZE(FullName),FullName,&NamePtr); + if (Code==0 || Code>ASIZE(FullName)) { - *DestName=0; - return(DestName); + wchar LongName[NM]; + if (GetWinLongPath(Src,LongName,ASIZE(LongName))) + Code=GetFullPathName(LongName,ASIZE(FullName),FullName,&NamePtr); } + if (Code!=0 && Code<ASIZE(FullName)) + wcsncpyz(Dest,FullName,MaxSize); else - strcpyw(DestName,SrcName); - for (wchar *s=SrcName;*s!=0;s++) + if (Src!=Dest) + wcsncpyz(Dest,Src,MaxSize); + } +#elif defined(_UNIX) + if (IsFullPath(Src)) + *Dest=0; + else { - if (*s=='/') - if (DestName==NULL) - *s='\\'; - else - DestName[s-SrcName]='\\'; + char CurDirA[NM]; + if (getcwd(CurDirA,ASIZE(CurDirA))==NULL) + *CurDirA=0; + CharToWide(CurDirA,Dest,MaxSize); + AddEndSlash(Dest,MaxSize); } - return(DestName==NULL ? SrcName:DestName); -} - - -bool IsFullPath(const char *Path) -{ - char PathOnly[NM]; - GetFilePath(Path,PathOnly,ASIZE(PathOnly)); - if (IsWildcard(PathOnly,NULL)) - return(true); -#if defined(_WIN_32) || defined(_EMX) - return(Path[0]=='\\' && Path[1]=='\\' || - IsDiskLetter(Path) && IsPathDiv(Path[2])); + wcsncatz(Dest,Src,MaxSize); #else - return(IsPathDiv(Path[0])); + wcsncpyz(Dest,Src,MaxSize); #endif } bool IsFullPath(const wchar *Path) { +/* wchar PathOnly[NM]; GetFilePath(Path,PathOnly,ASIZE(PathOnly)); - if (IsWildcard(NULL,PathOnly)) - return(true); -#if defined(_WIN_32) || defined(_EMX) - return(Path[0]=='\\' && Path[1]=='\\' || - IsDiskLetter(Path) && IsPathDiv(Path[2])); + if (IsWildcard(PathOnly)) + return true; +*/ +#if defined(_WIN_ALL) || defined(_EMX) + return Path[0]=='\\' && Path[1]=='\\' || IsDriveLetter(Path) && IsPathDiv(Path[2]); #else - return(IsPathDiv(Path[0])); + return IsPathDiv(Path[0]); #endif } -bool IsDiskLetter(const char *Path) +bool IsFullRootPath(const wchar *Path) { - char Letter=etoupper(Path[0]); - return(Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1])); + return IsFullPath(Path) || IsPathDiv(Path[0]); } -bool IsDiskLetter(const wchar *Path) -{ - int Letter=etoupper(Path[0]); - return(Letter>='A' && Letter<='Z' && IsDriveDiv(Path[1])); -} - - -void GetPathRoot(const char *Path,char *Root) +void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize) { *Root=0; - if (IsDiskLetter(Path)) - sprintf(Root,"%c:\\",*Path); + if (IsDriveLetter(Path)) + swprintf(Root,MaxSize,L"%c:\\",*Path); else if (Path[0]=='\\' && Path[1]=='\\') { - const char *Slash=strchr(Path+2,'\\'); + const wchar *Slash=wcschr(Path+2,'\\'); if (Slash!=NULL) { size_t Length; - if ((Slash=strchr(Slash+1,'\\'))!=NULL) + if ((Slash=wcschr(Slash+1,'\\'))!=NULL) Length=Slash-Path+1; else - Length=strlen(Path); - strncpy(Root,Path,Length); + Length=wcslen(Path); + if (Length>=MaxSize) + Length=0; + wcsncpy(Root,Path,Length); Root[Length]=0; } } } -int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate) +int ParseVersionFileName(wchar *Name,bool Truncate) { int Version=0; - char *VerText=strrchrd(Name,';'); + wchar *VerText=wcsrchr(Name,';'); if (VerText!=NULL) { - Version=atoi(VerText+1); + Version=atoiw(VerText+1); if (Truncate) *VerText=0; } - if (NameW!=NULL) - { - wchar *VerTextW=strrchrw(NameW,';'); - if (VerTextW!=NULL) - { - if (Version==0) - Version=atoiw(VerTextW+1); - if (Truncate) - *VerTextW=0; - } - } - return(Version); + return Version; } -#ifndef SFX_MODULE -char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering) +#if !defined(SFX_MODULE) +// Get the name of first volume. Return the leftmost digit of volume number. +wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering) { if (FirstName!=VolName) - strcpy(FirstName,VolName); - char *VolNumStart=FirstName; + wcsncpyz(FirstName,VolName,MaxSize); + wchar *VolNumStart=FirstName; if (NewNumbering) { - int N='1'; - for (char *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) - if (isdigit(*ChPtr)) + wchar N='1'; + + // From the rightmost digit of volume number to the left. + for (wchar *ChPtr=GetVolNumPart(FirstName);ChPtr>FirstName;ChPtr--) + if (IsDigit(*ChPtr)) { - *ChPtr=N; + *ChPtr=N; // Set the rightmost digit to '1' and others to '0'. N='0'; } else if (N=='0') { - VolNumStart=ChPtr+1; + VolNumStart=ChPtr+1; // Store the position of leftmost digit in volume number. break; } } else { - SetExt(FirstName,"rar"); + // Old volume numbering scheme. Just set the extension to ".rar". + SetExt(FirstName,L"rar",MaxSize); VolNumStart=GetExt(FirstName); } if (!FileExist(FirstName)) { - char Mask[NM]; - strcpy(Mask,FirstName); - SetExt(Mask,"*"); + // If the first volume, which name we just generated, does not exist, + // check if volume with same name and any other extension is available. + // It can help in case of *.exe or *.sfx first volume. + wchar Mask[NM]; + wcsncpyz(Mask,FirstName,ASIZE(Mask)); + SetExt(Mask,L"*",ASIZE(Mask)); FindFile Find; Find.SetMask(Mask); - struct FindData FD; + FindData FD; while (Find.Next(&FD)) { Archive Arc; - if (Arc.Open(FD.Name,FD.NameW) && Arc.IsArchive(true) && !Arc.NotFirstVolume) + if (Arc.Open(FD.Name,0) && Arc.IsArchive(true) && Arc.FirstVolume) { - strcpy(FirstName,FD.Name); + wcsncpyz(FirstName,FD.Name,MaxSize); break; } } } - return(VolNumStart); + return VolNumStart; } #endif +#ifndef SFX_MODULE +static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent) +{ + bool Prefix=false; + if (*GenerateMask=='+') + { + Prefix=true; // Add the time string before the archive name. + GenerateMask++; // Skip '+' in the beginning of time mask. + } + + wchar Mask[MAX_GENERATE_MASK]; + wcsncpyz(Mask,*GenerateMask!=0 ? GenerateMask:L"yyyymmddhhmmss",ASIZE(Mask)); + + bool QuoteMode=false; + uint MAsMinutes=0; // By default we treat 'M' as months. + for (uint I=0;Mask[I]!=0;I++) + { + if (Mask[I]=='{' || Mask[I]=='}') + { + QuoteMode=(Mask[I]=='{'); + continue; + } + if (QuoteMode) + continue; + int CurChar=toupperw(Mask[I]); + if (CurChar=='H') + MAsMinutes=2; // Treat next two 'M' after 'H' as minutes. + if (CurChar=='D' || CurChar=='Y') + MAsMinutes=0; // Treat 'M' in HHDDMMYY and HHYYMMDD as month. + + if (MAsMinutes>0 && CurChar=='M') + { + // Replace minutes with 'I'. We use 'M' both for months and minutes, + // so we treat as minutes only those 'M', which are found after hours. + Mask[I]='I'; + MAsMinutes--; + } + if (CurChar=='N') + { + uint Digits=GetDigits(ArcNumber); + uint NCount=0; + while (toupperw(Mask[I+NCount])=='N') + NCount++; + + // Here we ensure that we have enough 'N' characters to fit all digits + // of archive number. We'll replace them by actual number later + // in this function. + if (NCount<Digits && wcslen(Mask)+Digits-NCount<ASIZE(Mask)) + { + wmemmove(Mask+I+Digits,Mask+I+NCount,wcslen(Mask+I+NCount)+1); + wmemset(Mask+I,'N',Digits); + } + I+=Max(Digits,NCount)-1; + ArcNumPresent=true; + continue; + } + } + + RarTime CurTime; + CurTime.SetCurrentTime(); + RarLocalTime rlt; + CurTime.GetLocal(&rlt); + + wchar Ext[NM],*Dot=GetExt(ArcName); + *Ext=0; + if (Dot==NULL) + wcsncpyz(Ext,*PointToName(ArcName)==0 ? L".rar":L"",ASIZE(Ext)); + else + { + wcsncpyz(Ext,Dot,ASIZE(Ext)); + *Dot=0; + } + + int WeekDay=rlt.wDay==0 ? 6:rlt.wDay-1; + int StartWeekDay=rlt.yDay-WeekDay; + if (StartWeekDay<0) + if (StartWeekDay<=-4) + StartWeekDay+=IsLeapYear(rlt.Year-1) ? 366:365; + else + StartWeekDay=0; + int CurWeek=StartWeekDay/7+1; + if (StartWeekDay%7>=4) + CurWeek++; + + char Field[10][11]; + + sprintf(Field[0],"%04u",rlt.Year); + sprintf(Field[1],"%02u",rlt.Month); + sprintf(Field[2],"%02u",rlt.Day); + sprintf(Field[3],"%02u",rlt.Hour); + sprintf(Field[4],"%02u",rlt.Minute); + sprintf(Field[5],"%02u",rlt.Second); + sprintf(Field[6],"%02u",(uint)CurWeek); + sprintf(Field[7],"%u",(uint)WeekDay+1); + sprintf(Field[8],"%03u",rlt.yDay+1); + sprintf(Field[9],"%05u",ArcNumber); + + const wchar *MaskChars=L"YMDHISWAEN"; + + // How many times every modifier character was encountered in the mask. + int CField[sizeof(Field)/sizeof(Field[0])]; + + memset(CField,0,sizeof(CField)); + QuoteMode=false; + for (uint I=0;Mask[I]!=0;I++) + { + if (Mask[I]=='{' || Mask[I]=='}') + { + QuoteMode=(Mask[I]=='{'); + continue; + } + if (QuoteMode) + continue; + const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I])); + if (ChPtr!=NULL) + CField[ChPtr-MaskChars]++; + } + + wchar DateText[MAX_GENERATE_MASK]; + *DateText=0; + QuoteMode=false; + for (size_t I=0,J=0;Mask[I]!=0 && J<ASIZE(DateText)-1;I++) + { + if (Mask[I]=='{' || Mask[I]=='}') + { + QuoteMode=(Mask[I]=='{'); + continue; + } + const wchar *ChPtr=wcschr(MaskChars,toupperw(Mask[I])); + if (ChPtr==NULL || QuoteMode) + { + DateText[J]=Mask[I]; +#ifdef _WIN_ALL + // We do not allow ':' in Windows because of NTFS streams. + // Users had problems after specifying hh:mm mask. + if (DateText[J]==':') + DateText[J]='_'; +#endif + } + else + { + size_t FieldPos=ChPtr-MaskChars; + int CharPos=(int)strlen(Field[FieldPos])-CField[FieldPos]--; + + // CField[FieldPos] shall have exactly 3 "MMM" symbols, so we do not + // repeat the month name in case "MMMMMMMM" mask. But since we + // decremented CField[FieldPos] above, we compared it with 2. + if (FieldPos==1 && CField[FieldPos]==2 && + toupperw(Mask[I+1])=='M' && toupperw(Mask[I+2])=='M') + { + wcsncpyz(DateText+J,GetMonthName(rlt.Month-1),ASIZE(DateText)-J); + J=wcslen(DateText); + I+=2; + continue; + } + // If CharPos is negative, we have more modifier characters than + // matching time data. We prefer to issue a modifier character + // instead of repeating time data from beginning, so user can notice + // excessive modifiers added by mistake. + if (CharPos<0) + DateText[J]=Mask[I]; + else + DateText[J]=Field[FieldPos][CharPos]; + } + DateText[++J]=0; + } + + if (Prefix) + { + wchar NewName[NM]; + GetFilePath(ArcName,NewName,ASIZE(NewName)); + AddEndSlash(NewName,ASIZE(NewName)); + wcsncatz(NewName,DateText,ASIZE(NewName)); + wcsncatz(NewName,PointToName(ArcName),ASIZE(NewName)); + wcsncpyz(ArcName,NewName,MaxSize); + } + else + wcsncatz(ArcName,DateText,MaxSize); + wcsncatz(ArcName,Ext,MaxSize); +} + + +void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving) +{ + wchar NewName[NM]; + uint ArcNumber=1; + while (true) // Loop for 'N' (archive number) processing. + { + wcsncpyz(NewName,ArcName,ASIZE(NewName)); + + bool ArcNumPresent=false; -wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW) + GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber,ArcNumPresent); + + if (!ArcNumPresent) + break; + if (!FileExist(NewName)) + { + if (!Archiving && ArcNumber>1) + { + // If we perform non-archiving operation, we need to use the last + // existing archive before the first unused name. So we generate + // the name for (ArcNumber-1) below. + wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName)); + GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber-1,ArcNumPresent); + } + break; + } + ArcNumber++; + } + wcsncpyz(ArcName,NewName,MaxSize); +} +#endif + + +wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize) { if (NameW!=NULL && *NameW!=0) { if (DestW!=NameW) - strcpyw(DestW,NameW); + wcsncpy(DestW,NameW,DestSize); + } + else + if (Name!=NULL) + CharToWide(Name,DestW,DestSize); + else + *DestW=0; + + // Ensure that we return a zero terminate string for security reasons. + if (DestSize>0) + DestW[DestSize-1]=0; + + return DestW; +} + + +#ifdef _WIN_ALL +// We should return 'true' even if resulting path is shorter than MAX_PATH, +// because we can also use this function to open files with non-standard +// characters, even if their path length is normal. +bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize) +{ + if (*Src==0) + return false; + const wchar *Prefix=L"\\\\?\\"; + const size_t PrefixLength=4; + bool FullPath=IsDriveLetter(Src) && IsPathDiv(Src[2]); + size_t SrcLength=wcslen(Src); + if (IsFullPath(Src)) // Paths in d:\path\name format. + { + if (IsDriveLetter(Src)) + { + if (MaxSize<=PrefixLength+SrcLength) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,Src,MaxSize); // "\\?\D:\very long path". + return true; + } + else + if (Src[0]=='\\' && Src[1]=='\\') + { + if (MaxSize<=PrefixLength+SrcLength+2) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,L"UNC",MaxSize); + wcsncatz(Dest,Src+1,MaxSize); // "\\?\UNC\server\share". + return true; + } + // We may be here only if we modify IsFullPath in the future. + return false; } else - CharToWide(Name,DestW); - return(DestW); + { + wchar CurDir[NM]; + DWORD DirCode=GetCurrentDirectory(ASIZE(CurDir)-1,CurDir); + if (DirCode==0 || DirCode>ASIZE(CurDir)-1) + return false; + + if (IsPathDiv(Src[0])) // Paths in \path\name format. + { + if (MaxSize<=PrefixLength+SrcLength+2) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + CurDir[2]=0; + wcsncatz(Dest,CurDir,MaxSize); // Copy drive letter 'd:'. + wcsncatz(Dest,Src,MaxSize); + return true; + } + else // Paths in path\name format. + { + AddEndSlash(CurDir,ASIZE(CurDir)); + if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength) + return false; + wcsncpyz(Dest,Prefix,MaxSize); + wcsncatz(Dest,CurDir,MaxSize); + + if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname. + Src+=2; + + wcsncatz(Dest,Src,MaxSize); + return true; + } + } + return false; } +// Convert Unix, OS X and Android decomposed chracters to Windows precomposed. +void ConvertToPrecomposed(wchar *Name,size_t NameSize) +{ + wchar FileName[NM]; + if (WinNT()>=WNT_VISTA && // MAP_PRECOMPOSED is not supported in XP. + FoldString(MAP_PRECOMPOSED,Name,-1,FileName,ASIZE(FileName))!=0) + { + FileName[ASIZE(FileName)-1]=0; + wcsncpyz(Name,FileName,NameSize); + } +} + + +void MakeNameCompatible(wchar *Name,size_t MaxSize) +{ + // Remove trailing spaces and dots in file name and in dir names in path. + int Src=0,Dest=0; + while (true) + { + if (IsPathDiv(Name[Src]) || Name[Src]==0) + for (int I=Dest-1;I>0 && (Name[I]==' ' || Name[I]=='.');I--) + { + // Permit path1/./path2 and ../path1 paths. + if (Name[I]=='.' && (IsPathDiv(Name[I-1]) || Name[I-1]=='.' && I==1)) + break; + Dest--; + } + Name[Dest]=Name[Src]; + if (Name[Src]==0) + break; + Src++; + Dest++; + } + + // Rename reserved device names, such as aux.txt to _aux.txt. + // We check them in path components too, where they are also prohibited. + for (uint I=0;Name[I]!=0;I++) + if (I==0 || I>0 && IsPathDiv(Name[I-1])) + { + static const wchar *Devices[]={L"CON",L"PRN",L"AUX",L"NUL",L"COM#",L"LPT#"}; + wchar *s=Name+I; + bool MatchFound=false; + for (uint J=0;J<ASIZE(Devices);J++) + for (uint K=0;;K++) + if (Devices[J][K]=='#') + { + if (!IsDigit(s[K])) + break; + } + else + if (Devices[J][K]==0) + { + // Names like aux.txt are accessible without \\?\ prefix + // since Windows 11. Pure aux is still prohibited. + MatchFound=s[K]==0 || s[K]=='.' && !IsWindows11OrGreater() || IsPathDiv(s[K]); + break; + } + else + if (Devices[J][K]!=toupperw(s[K])) + break; + if (MatchFound) + { + wchar OrigName[NM]; + wcsncpyz(OrigName,Name,ASIZE(OrigName)); + if (MaxSize>I+1) // I+1, because we do not move the trailing 0. + memmove(s+1,s,(MaxSize-I-1)*sizeof(*s)); + *s='_'; +#ifndef SFX_MODULE + uiMsg(UIMSG_CORRECTINGNAME,nullptr); + uiMsg(UIERROR_RENAMING,nullptr,OrigName,Name); +#endif + } + } +} +#endif diff --git a/unrar/unrar/pathfn.hpp b/unrar/unrar/pathfn.hpp index 6b0ac31..62cae0a 100644 --- a/unrar/unrar/pathfn.hpp +++ b/unrar/unrar/pathfn.hpp @@ -1,49 +1,76 @@ #ifndef _RAR_PATHFN_ #define _RAR_PATHFN_ -char* PointToName(const char *Path); wchar* PointToName(const wchar *Path); -char* PointToLastChar(const char *Path); -char* ConvertPath(const char *SrcPath,char *DestPath); -wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath); -void SetExt(char *Name,const char *NewExt); -void SetExt(wchar *Name,const wchar *NewExt); -void SetSFXExt(char *SFXName); -void SetSFXExt(wchar *SFXName); -char *GetExt(const char *Name); +wchar* PointToLastChar(const wchar *Path); +wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize); +void SetName(wchar *FullName,const wchar *Name,size_t MaxSize); +void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize); +void SetSFXExt(wchar *SFXName,size_t MaxSize); wchar *GetExt(const wchar *Name); -bool CmpExt(const char *Name,const char *Ext); -bool IsWildcard(const char *Str,const wchar *StrW=NULL); +bool CmpExt(const wchar *Name,const wchar *Ext); +bool IsWildcard(const wchar *Str); bool IsPathDiv(int Ch); bool IsDriveDiv(int Ch); -int GetPathDisk(const char *Path); -void AddEndSlash(char *Path); -void AddEndSlash(wchar *Path); -void GetFilePath(const char *FullName,char *Path,int MaxLength); -void GetFilePath(const wchar *FullName,wchar *Path,int MaxLength); -void RemoveNameFromPath(char *Path); +bool IsDriveLetter(const wchar *Path); +int GetPathDisk(const wchar *Path); +void AddEndSlash(wchar *Path,size_t MaxLength); +void MakeName(const wchar *Path,const wchar *Name,wchar *Pathname,size_t MaxSize); +void GetFilePath(const wchar *FullName,wchar *Path,size_t MaxLength); void RemoveNameFromPath(wchar *Path); -void GetAppDataPath(char *Path); -void GetRarDataPath(char *Path); -bool EnumConfigPaths(char *Path,int Number); -void GetConfigName(const char *Name,char *FullName,bool CheckExist); -char* GetVolNumPart(char *ArcName); -void NextVolumeName(char *ArcName,wchar *ArcNameW,uint MaxLength,bool OldNumbering); -bool IsNameUsable(const char *Name); -void MakeNameUsable(char *Name,bool Extended); -char* UnixSlashToDos(char *SrcName,char *DestName=NULL,uint MaxLength=NM); -char* DosSlashToUnix(char *SrcName,char *DestName=NULL,uint MaxLength=NM); -wchar* UnixSlashToDos(wchar *SrcName,wchar *DestName=NULL,uint MaxLength=NM); -bool IsFullPath(const char *Path); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool GetAppDataPath(wchar *Path,size_t MaxSize,bool Create); +void GetRarDataPath(wchar *Path,size_t MaxSize,bool Create); +#endif +#ifndef SFX_MODULE +bool EnumConfigPaths(uint Number,wchar *Path,size_t MaxSize,bool Create); +void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckExist,bool Create); +#endif +wchar* GetVolNumPart(const wchar *ArcName); +void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering); +bool IsNameUsable(const wchar *Name); +void MakeNameUsable(wchar *Name,bool Extended); + +void UnixSlashToDos(const char *SrcName,char *DestName,size_t MaxLength); +void DosSlashToUnix(const char *SrcName,char *DestName,size_t MaxLength); +void UnixSlashToDos(const wchar *SrcName,wchar *DestName,size_t MaxLength); +void DosSlashToUnix(const wchar *SrcName,wchar *DestName,size_t MaxLength); + +inline void SlashToNative(const char *SrcName,char *DestName,size_t MaxLength) +{ +#ifdef _WIN_ALL + UnixSlashToDos(SrcName,DestName,MaxLength); +#else + DosSlashToUnix(SrcName,DestName,MaxLength); +#endif +} + +inline void SlashToNative(const wchar *SrcName,wchar *DestName,size_t MaxLength) +{ +#ifdef _WIN_ALL + UnixSlashToDos(SrcName,DestName,MaxLength); +#else + DosSlashToUnix(SrcName,DestName,MaxLength); +#endif +} + +void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize); bool IsFullPath(const wchar *Path); -bool IsDiskLetter(const char *Path); -bool IsDiskLetter(const wchar *Path); -void GetPathRoot(const char *Path,char *Root); -int ParseVersionFileName(char *Name,wchar *NameW,bool Truncate); -char* VolNameToFirstName(const char *VolName,char *FirstName,bool NewNumbering); -wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW); +bool IsFullRootPath(const wchar *Path); +void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize); +int ParseVersionFileName(wchar *Name,bool Truncate); +wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,bool NewNumbering); +wchar* GetWideName(const char *Name,const wchar *NameW,wchar *DestW,size_t DestSize); +#ifndef SFX_MODULE +void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving); +#endif + +#ifdef _WIN_ALL +bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize); +void ConvertToPrecomposed(wchar *Name,size_t NameSize); +void MakeNameCompatible(wchar *Name,size_t MaxSize); +#endif -inline char* GetOutputName(const char *Name) {return((char *)Name);}; #endif diff --git a/unrar/unrar/qopen.cpp b/unrar/unrar/qopen.cpp new file mode 100644 index 0000000..d906d06 --- /dev/null +++ b/unrar/unrar/qopen.cpp @@ -0,0 +1,300 @@ +#include "rar.hpp" + +QuickOpen::QuickOpen() +{ + Buf=NULL; + Init(NULL,false); +} + + +QuickOpen::~QuickOpen() +{ + Close(); + delete[] Buf; +} + + +void QuickOpen::Init(Archive *Arc,bool WriteMode) +{ + if (Arc!=NULL) // Unless called from constructor. + Close(); + + QuickOpen::Arc=Arc; + QuickOpen::WriteMode=WriteMode; + + ListStart=NULL; + ListEnd=NULL; + + if (Buf==NULL) + Buf=new byte[MaxBufSize]; + + CurBufSize=0; // Current size of buffered data in write mode. + + Loaded=false; +} + + +void QuickOpen::Close() +{ + QuickOpenItem *Item=ListStart; + while (Item!=NULL) + { + QuickOpenItem *Next=Item->Next; + delete[] Item->Header; + delete Item; + Item=Next; + } +} + + + + + + + + + + + + + + +void QuickOpen::Load(uint64 BlockPos) +{ + if (!Loaded) + { + // If loading for the first time, perform additional intialization. + SeekPos=Arc->Tell(); + UnsyncSeekPos=false; + + int64 SavePos=SeekPos; + Arc->Seek(BlockPos,SEEK_SET); + + // If BlockPos points to original main header, we'll have the infinite + // recursion, because ReadHeader() for main header will attempt to load + // QOpen and call QuickOpen::Load again. If BlockPos points to long chain + // of other main headers, we'll have multiple recursive calls of this + // function wasting resources. So we prohibit QOpen temporarily to + // prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator + // and QOpenOffset fields, so we cannot use them to prohibit QOpen. + Arc->SetProhibitQOpen(true); + size_t ReadSize=Arc->ReadHeader(); + Arc->SetProhibitQOpen(false); + + if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE || + !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN)) + { + Arc->Seek(SavePos,SEEK_SET); + return; + } + QOHeaderPos=Arc->CurBlockPos; + RawDataStart=Arc->Tell(); + RawDataSize=Arc->SubHead.UnpSize; + Arc->Seek(SavePos,SEEK_SET); + + Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader. + } + + if (Arc->SubHead.Encrypted) + { + CommandData *Cmd=Arc->GetCommandData(); +#ifndef RAR_NOCRYPT + if (Cmd->Password.IsSet()) + Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt, + Arc->SubHead.InitV,Arc->SubHead.Lg2Count, + Arc->SubHead.HashKey,Arc->SubHead.PswCheck); + else +#endif + { + Loaded=false; + return; + } + } + + RawDataPos=0; + ReadBufSize=0; + ReadBufPos=0; + LastReadHeader.Reset(); + LastReadHeaderPos=0; + + ReadBuffer(); +} + + +bool QuickOpen::Read(void *Data,size_t Size,size_t &Result) +{ + if (!Loaded) + return false; + // Find next suitable cached block. + while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos) + if (!ReadNext()) + break; + if (!Loaded) + { + // If something wrong happened, let's set the correct file pointer + // and stop further quick open processing. + if (UnsyncSeekPos) + Arc->File::Seek(SeekPos,SEEK_SET); + return false; + } + + if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size()) + { + memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size); + Result=Size; + SeekPos+=Size; + UnsyncSeekPos=true; + } + else + { + if (UnsyncSeekPos) + { + Arc->File::Seek(SeekPos,SEEK_SET); + UnsyncSeekPos=false; + } + int ReadSize=Arc->File::Read(Data,Size); + if (ReadSize<0) + { + Loaded=false; + return false; + } + Result=ReadSize; + SeekPos+=ReadSize; + } + + return true; +} + + +bool QuickOpen::Seek(int64 Offset,int Method) +{ + if (!Loaded) + return false; + + // Normally we process an archive sequentially from beginning to end, + // so we read quick open data sequentially. But some operations like + // archive updating involve several passes. So if we detect that file + // pointer is moved back, we reload quick open data from beginning. + if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos) + Load(QOHeaderPos); + + if (Method==SEEK_SET) + SeekPos=Offset; + if (Method==SEEK_CUR) + SeekPos+=Offset; + UnsyncSeekPos=true; + + if (Method==SEEK_END) + { + Arc->File::Seek(Offset,SEEK_END); + SeekPos=Arc->File::Tell(); + UnsyncSeekPos=false; + } + return true; +} + + +bool QuickOpen::Tell(int64 *Pos) +{ + if (!Loaded) + return false; + *Pos=SeekPos; + return true; +} + + +uint QuickOpen::ReadBuffer() +{ + int64 SavePos=Arc->Tell(); + Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET); + size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize); + if (Arc->SubHead.Encrypted) + SizeToRead &= ~CRYPT_BLOCK_MASK; + int ReadSize=0; + if (SizeToRead!=0) + { + ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead); + if (ReadSize<=0) + ReadSize=0; + else + { +#ifndef RAR_NOCRYPT + if (Arc->SubHead.Encrypted) + Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK); +#endif + RawDataPos+=ReadSize; + ReadBufSize+=ReadSize; + } + } + Arc->Seek(SavePos,SEEK_SET); + return ReadSize; +} + + +// Fill RawRead object from buffer. +bool QuickOpen::ReadRaw(RawRead &Raw) +{ + if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer. + { + // Ensure that we have enough data to read CRC and header size. + size_t DataLeft=ReadBufSize-ReadBufPos; + memcpy(Buf,Buf+ReadBufPos,DataLeft); + ReadBufPos=0; + ReadBufSize=DataLeft; + ReadBuffer(); + } + const size_t FirstReadSize=7; + if (ReadBufPos+FirstReadSize>ReadBufSize) + return false; + Raw.Read(Buf+ReadBufPos,FirstReadSize); + ReadBufPos+=FirstReadSize; + + uint SavedCRC=Raw.Get4(); + uint SizeBytes=Raw.GetVSize(4); + uint64 BlockSize=Raw.GetV(); + int SizeToRead=int(BlockSize); + SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any. + if (SizeToRead<0 || SizeBytes==0 || BlockSize==0) + { + Loaded=false; // Invalid data. + return false; + } + + // If rest of block data crosses Buf boundary, read it in loop. + while (SizeToRead>0) + { + size_t DataLeft=ReadBufSize-ReadBufPos; + size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead); + Raw.Read(Buf+ReadBufPos,CurSizeToRead); + ReadBufPos+=CurSizeToRead; + SizeToRead-=int(CurSizeToRead); + if (SizeToRead>0) // We read the entire buffer and still need more data. + { + ReadBufPos=0; + ReadBufSize=0; + if (ReadBuffer()==0) + return false; + } + } + + return SavedCRC==Raw.GetCRC50(); +} + + +// Read next cached header. +bool QuickOpen::ReadNext() +{ + RawRead Raw(NULL); + if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block. + return false; + uint Flags=(uint)Raw.GetV(); + uint64 Offset=Raw.GetV(); + size_t HeaderSize=(size_t)Raw.GetV(); + if (HeaderSize>MAX_HEADER_SIZE_RAR5) + return false; + LastReadHeader.Alloc(HeaderSize); + Raw.GetB(&LastReadHeader[0],HeaderSize); + // Calculate the absolute position as offset from quick open service header. + LastReadHeaderPos=QOHeaderPos-Offset; + return true; +} diff --git a/unrar/unrar/qopen.hpp b/unrar/unrar/qopen.hpp new file mode 100644 index 0000000..d745cea --- /dev/null +++ b/unrar/unrar/qopen.hpp @@ -0,0 +1,61 @@ +#ifndef _RAR_QOPEN_ +#define _RAR_QOPEN_ + +struct QuickOpenItem +{ + byte *Header; + size_t HeaderSize; + uint64 ArcPos; + QuickOpenItem *Next; +}; + + +class Archive; +class RawRead; + +class QuickOpen +{ + private: + void Close(); + + + uint ReadBuffer(); + bool ReadRaw(RawRead &Raw); + bool ReadNext(); + + Archive *Arc; + bool WriteMode; + + QuickOpenItem *ListStart; + QuickOpenItem *ListEnd; + + byte *Buf; // Read quick open data here. + static const size_t MaxBufSize=0x10000; // Buf size, must be multiple of CRYPT_BLOCK_SIZE. + size_t CurBufSize; // Current size of buffered data in write mode. +#ifndef RAR_NOCRYPT // For shell extension. + CryptData Crypt; +#endif + + bool Loaded; + uint64 QOHeaderPos; // Main QO header position. + uint64 RawDataStart; // Start of QO data, just after the main header. + uint64 RawDataSize; // Size of entire QO data. + uint64 RawDataPos; // Current read position in QO data. + size_t ReadBufSize; // Size of Buf data currently read from QO. + size_t ReadBufPos; // Current read position in Buf data. + Array<byte> LastReadHeader; + uint64 LastReadHeaderPos; + uint64 SeekPos; + bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer. + public: + QuickOpen(); + ~QuickOpen(); + void Init(Archive *Arc,bool WriteMode); + void Load(uint64 BlockPos); + void Unload() { Loaded=false; } + bool Read(void *Data,size_t Size,size_t &Result); + bool Seek(int64 Offset,int Method); + bool Tell(int64 *Pos); +}; + +#endif diff --git a/unrar/unrar/rar.cpp b/unrar/unrar/rar.cpp index a55fd55..34b4b27 100644 --- a/unrar/unrar/rar.cpp +++ b/unrar/unrar/rar.cpp @@ -1,143 +1,106 @@ #include "rar.hpp" - -#include "smallfn.cpp" - -#ifdef _DJGPP -extern "C" char **__crt0_glob_function (char *arg) { return 0; } -extern "C" void __crt0_load_environment_file (char *progname) { } -#endif - -#if !defined(GUI) && !defined(RARDLL) +#if !defined(RARDLL) int main(int argc, char *argv[]) { + #ifdef _UNIX setlocale(LC_ALL,""); #endif -#if defined(_EMX) && !defined(_DJGPP) - uni_init(0); -#endif - -#if !defined(_SFX_RTL_) && !defined(_WIN_32) - setbuf(stdout,NULL); -#endif - -#if !defined(SFX_MODULE) && defined(_EMX) - EnumConfigPaths(argv[0],-1); -#endif - + InitConsole(); ErrHandler.SetSignalHandlers(true); - RARInitData(); - #ifdef SFX_MODULE - char ModuleName[NM]; -#ifdef _WIN_32 - GetModuleFileName(NULL,ModuleName,sizeof(ModuleName)); + wchar ModuleName[NM]; +#ifdef _WIN_ALL + GetModuleFileName(NULL,ModuleName,ASIZE(ModuleName)); #else - strcpy(ModuleName,argv[0]); + CharToWide(argv[0],ModuleName,ASIZE(ModuleName)); #endif #endif -#ifdef _WIN_32 +#ifdef _WIN_ALL SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT|SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); #endif -#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT) - bool ShutdownOnClose; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + // Must be initialized, normal initialization can be skipped in case of + // exception. + POWER_MODE ShutdownOnClose=POWERMODE_KEEP; #endif -#ifdef ALLOW_EXCEPTIONS try -#endif { - CommandData Cmd; + CommandData *Cmd=new CommandData; #ifdef SFX_MODULE - strcpy(Cmd.Command,"X"); - char *Switch=NULL; -#ifdef _SFX_RTL_ - char *CmdLine=GetCommandLine(); - if (CmdLine!=NULL && *CmdLine=='\"') - CmdLine=strchr(CmdLine+1,'\"'); - if (CmdLine!=NULL && (CmdLine=strpbrk(CmdLine," /"))!=NULL) - { - while (isspace(*CmdLine)) - CmdLine++; - Switch=CmdLine; - } -#else - Switch=argc>1 ? argv[1]:NULL; -#endif - if (Switch!=NULL && Cmd.IsSwitch(Switch[0])) + wcsncpyz(Cmd->Command,L"X",ASIZE(Cmd->Command)); + char *Switch=argc>1 ? argv[1]:NULL; + if (Switch!=NULL && Cmd->IsSwitch(Switch[0])) { int UpperCmd=etoupper(Switch[1]); switch(UpperCmd) { case 'T': case 'V': - Cmd.Command[0]=UpperCmd; + Cmd->Command[0]=UpperCmd; break; case '?': - Cmd.OutHelp(); + Cmd->OutHelp(RARX_SUCCESS); break; } } - Cmd.AddArcName(ModuleName,NULL); -#else - if (Cmd.IsConfigEnabled(argc,argv)) + Cmd->AddArcName(ModuleName); + Cmd->ParseDone(); + Cmd->AbsoluteLinks=true; // If users runs SFX, he trusts an archive source. +#else // !SFX_MODULE + Cmd->ParseCommandLine(true,argc,argv); + if (!Cmd->ConfigDisabled) { - Cmd.ReadConfig(argc,argv); - Cmd.ParseEnvVar(); + Cmd->ReadConfig(); + Cmd->ParseEnvVar(); } - for (int I=1;I<argc;I++) - Cmd.ParseArg(argv[I],NULL); + Cmd->ParseCommandLine(false,argc,argv); #endif - Cmd.ParseDone(); -#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT) - ShutdownOnClose=Cmd.Shutdown; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + ShutdownOnClose=Cmd->Shutdown; + if (ShutdownOnClose) + ShutdownCheckAnother(true); #endif - InitConsoleOptions(Cmd.MsgStream,Cmd.Sound); - InitLogOptions(Cmd.LogName); - ErrHandler.SetSilent(Cmd.AllYes || Cmd.MsgStream==MSG_NULL); - ErrHandler.SetShutdown(Cmd.Shutdown); + uiInit(Cmd->Sound); + InitLogOptions(Cmd->LogName,Cmd->ErrlogCharset); + ErrHandler.SetSilent(Cmd->AllYes || Cmd->MsgStream==MSG_NULL); - Cmd.OutTitle(); - Cmd.ProcessCommand(); + Cmd->OutTitle(); + Cmd->ProcessCommand(); + delete Cmd; } -#ifdef ALLOW_EXCEPTIONS - catch (int ErrCode) + catch (RAR_EXIT ErrCode) { ErrHandler.SetErrorCode(ErrCode); } -#ifdef ENABLE_BAD_ALLOC - catch (bad_alloc) + catch (std::bad_alloc&) { - ErrHandler.SetErrorCode(MEMORY_ERROR); + ErrHandler.MemoryErrorMsg(); + ErrHandler.SetErrorCode(RARX_MEMORY); } -#endif catch (...) { - ErrHandler.SetErrorCode(FATAL_ERROR); + ErrHandler.SetErrorCode(RARX_FATAL); } + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (ShutdownOnClose!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled() && + !ShutdownCheckAnother(false)) + Shutdown(ShutdownOnClose); #endif - File::RemoveCreated(); -#if defined(SFX_MODULE) && defined(_DJGPP) - _chmod(ModuleName,1,0x20); -#endif -#if defined(_EMX) && !defined(_DJGPP) - uni_done(); -#endif -#if defined(_WIN_32) && !defined(SFX_MODULE) && !defined(SHELL_EXT) - if (ShutdownOnClose) - Shutdown(); -#endif - return(ErrHandler.GetErrorCode()); + ErrHandler.MainExit=true; + return ErrHandler.GetErrorCode(); } #endif diff --git a/unrar/unrar/rar.hpp b/unrar/unrar/rar.hpp index 229ee09..67edb67 100644 --- a/unrar/unrar/rar.hpp +++ b/unrar/unrar/rar.hpp @@ -2,63 +2,70 @@ #define _RAR_RARCOMMON_ #include "raros.hpp" +#include "rartypes.hpp" #include "os.hpp" - #ifdef RARDLL #include "dll.hpp" #endif -#ifndef _WIN_CE #include "version.hpp" -#endif -#include "rartypes.hpp" #include "rardefs.hpp" #include "rarlang.hpp" -#include "int64.hpp" +#include "rawint.hpp" #include "unicode.hpp" #include "errhnd.hpp" +#include "secpassword.hpp" #include "array.hpp" +#include "strlist.hpp" #include "timefn.hpp" +#include "sha1.hpp" +#include "sha256.hpp" +#include "blake2s.hpp" +#include "hash.hpp" #include "options.hpp" +#include "rijndael.hpp" +#include "crypt.hpp" +#include "headers5.hpp" #include "headers.hpp" -#include "rarfn.hpp" #include "pathfn.hpp" #include "strfn.hpp" -#include "strlist.hpp" +#ifdef _WIN_ALL +#include "isnt.hpp" +#endif #include "file.hpp" -#include "sha1.hpp" #include "crc.hpp" -#include "rijndael.hpp" -#include "crypt.hpp" #include "filefn.hpp" #include "filestr.hpp" #include "find.hpp" #include "scantree.hpp" -#include "savepos.hpp" #include "getbits.hpp" #include "rdwrfn.hpp" +#ifdef USE_QOPEN +#include "qopen.hpp" +#endif #include "archive.hpp" #include "match.hpp" #include "cmddata.hpp" +#include "ui.hpp" #include "filcreat.hpp" #include "consio.hpp" #include "system.hpp" -#include "isnt.hpp" #include "log.hpp" #include "rawread.hpp" #include "encname.hpp" #include "resource.hpp" #include "compress.hpp" - #include "rarvm.hpp" #include "model.hpp" +#include "threadpool.hpp" #include "unpack.hpp" + #include "extinfo.hpp" #include "extract.hpp" @@ -67,14 +74,23 @@ #include "list.hpp" - #include "rs.hpp" +#include "rs16.hpp" + + + #include "recvol.hpp" #include "volume.hpp" #include "smallfn.hpp" -#include "ulinks.hpp" #include "global.hpp" +#if 0 +#include "benchmark.hpp" +#endif + + + + #endif diff --git a/unrar/unrar/rardefs.hpp b/unrar/unrar/rardefs.hpp index 24f97b5..6858d39 100644 --- a/unrar/unrar/rardefs.hpp +++ b/unrar/unrar/rardefs.hpp @@ -4,21 +4,32 @@ #define Min(x,y) (((x)<(y)) ? (x):(y)) #define Max(x,y) (((x)>(y)) ? (x):(y)) +// Universal replacement of abs function. +#define Abs(x) (((x)<0) ? -(x):(x)) + #define ASIZE(x) (sizeof(x)/sizeof(x[0])) -#define MAXPASSWORD 128 -#define MAXSFXSIZE 0x80000 +// MAXPASSWORD and MAXPASSWORD_RAR are expected to be multiple of +// CRYPTPROTECTMEMORY_BLOCK_SIZE (16) for CryptProtectMemory in SecPassword. +// We allow a larger MAXPASSWORD to unpack archives with lengthy passwords +// in non-RAR formats in GUI versions. For RAR format we set MAXPASSWORD_RAR +// to 128 for compatibility and because it is enough for AES-256. +#define MAXPASSWORD 512 +#define MAXPASSWORD_RAR 128 + +#define MAXSFXSIZE 0x200000 + +#define MAXCMTSIZE 0x40000 -#define DefSFXName "default.sfx" -#define DefSortListName "rarfiles.lst" +#define DefSFXName L"default.sfx" +#define DefSortListName L"rarfiles.lst" -#ifndef FA_RDONLY - #define FA_RDONLY 0x01 - #define FA_HIDDEN 0x02 - #define FA_SYSTEM 0x04 - #define FA_LABEL 0x08 - #define FA_DIREC 0x10 - #define FA_ARCH 0x20 + +#ifndef SFX_MODULE +#define USE_QOPEN #endif +// Produce the value, which is equal or larger than 'v' and aligned to 'a'. +#define ALIGN_VALUE(v,a) (size_t(v) + ( (~size_t(v) + 1) & (a - 1) ) ) + #endif diff --git a/unrar/unrar/rarfn.hpp b/unrar/unrar/rarfn.hpp deleted file mode 100644 index 05ffdbc..0000000 --- a/unrar/unrar/rarfn.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _RAR_FN_ -#define _RAR_FN_ - -void RARInitData(); - - -#endif diff --git a/unrar/unrar/raros.hpp b/unrar/unrar/raros.hpp index e686798..4f4f2ae 100644 --- a/unrar/unrar/raros.hpp +++ b/unrar/unrar/raros.hpp @@ -11,22 +11,17 @@ #endif #if defined(__WIN32__) || defined(_WIN32) - #define _WIN_32 -#endif - -#ifdef _WIN32_WCE - #define _WIN_32 - #define _WIN_CE - #ifdef WM_FILECHANGEINFO - #define PC2002 + #define _WIN_ALL // Defined for all Windows platforms, 32 and 64 bit, mobile and desktop. + #ifdef _M_X64 + #define _WIN_64 #else - #undef PC2002 + #define _WIN_32 #endif #endif -#ifdef __BEOS__ +#if defined(ANDROID) || defined(__ANDROID__) #define _UNIX - #define _BEOS + #define _ANDROID #endif #ifdef __APPLE__ @@ -34,7 +29,7 @@ #define _APPLE #endif -#if !defined(_EMX) && !defined(_WIN_32) && !defined(_BEOS) && !defined(_APPLE) +#if !defined(_EMX) && !defined(_WIN_ALL) && !defined(_BEOS) && !defined(_APPLE) #define _UNIX #endif diff --git a/unrar/unrar/rarpch.cpp b/unrar/unrar/rarpch.cpp new file mode 100644 index 0000000..c070cf7 --- /dev/null +++ b/unrar/unrar/rarpch.cpp @@ -0,0 +1,2 @@ +// We use rarpch.cpp to create precompiled headers for MS Visual C++. +#include "rar.hpp" diff --git a/unrar/unrar/rartypes.hpp b/unrar/unrar/rartypes.hpp index 56fccf6..3d3111b 100644 --- a/unrar/unrar/rartypes.hpp +++ b/unrar/unrar/rartypes.hpp @@ -1,21 +1,32 @@ #ifndef _RAR_TYPES_ #define _RAR_TYPES_ -typedef unsigned char byte; //8 bits -typedef unsigned short ushort; //preferably 16 bits, but can be more -typedef unsigned int uint; //32 bits or more +#include <stdint.h> -typedef unsigned int uint32; //32 bits exactly -typedef int sint32; //signed 32 bits exactly -#define PRESENT_INT32 +typedef uint8_t byte; // Unsigned 8 bits. +typedef uint16_t ushort; // Preferably 16 bits, but can be more. +typedef unsigned int uint; // 32 bits or more. +typedef uint32_t uint32; // 32 bits exactly. +typedef int32_t int32; // Signed 32 bits exactly. +typedef uint64_t uint64; // 64 bits exactly. +typedef int64_t int64; // Signed 64 bits exactly. +typedef wchar_t wchar; // Unicode character -#if defined(_WIN_32) || defined(__GNUC__) || defined(__sgi) || defined(_AIX) || defined(__sun) || defined(__hpux) || defined(_OSF_SOURCE) -typedef wchar_t wchar; -#else -typedef ushort wchar; -#endif +// Get lowest 16 bits. +#define GET_SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff)) + +// Make 64 bit integer from two 32 bit. +#define INT32TO64(high,low) ((((uint64)(high))<<32)+((uint64)low)) + +// Maximum int64 value. +#define MAX_INT64 int64(INT32TO64(0x7fffffff,0xffffffff)) -#define SHORT16(x) (sizeof(ushort)==2 ? (ushort)(x):((x)&0xffff)) -#define UINT32(x) (sizeof(uint32)==4 ? (uint32)(x):((x)&0xffffffff)) +// Special int64 value, large enough to never be found in real life +// and small enough to fit to both signed and unsigned 64-bit ints. +// We use it in situations, when we need to indicate that parameter +// is not defined and probably should be calculated inside of function. +// Lower part is intentionally 0x7fffffff, not 0xffffffff, to make it +// compatible with 32 bit int64 if 64 bit type is not supported. +#define INT64NDF INT32TO64(0x7fffffff,0x7fffffff) #endif diff --git a/unrar/unrar/rarvm.cpp b/unrar/unrar/rarvm.cpp index 987e3ae..8d8675a 100644 --- a/unrar/unrar/rarvm.cpp +++ b/unrar/unrar/rarvm.cpp @@ -1,7 +1,5 @@ #include "rar.hpp" -#include "rarvmtbl.cpp" - RarVM::RarVM() { Mem=NULL; @@ -20,691 +18,53 @@ void RarVM::Init() Mem=new byte[VM_MEMSIZE+4]; } -/********************************************************************* - IS_VM_MEM macro checks if address belongs to VM memory pool (Mem). - Only Mem data are always low endian regardless of machine architecture, - so we need to convert them to native format when reading or writing. - VM registers have endianness of host machine. -**********************************************************************/ -#define IS_VM_MEM(a) (((byte*)a)>=Mem && ((byte*)a)<Mem+VM_MEMSIZE) - -inline uint RarVM::GetValue(bool ByteMode,uint *Addr) -{ - if (ByteMode) - { -#ifdef BIG_ENDIAN - if (IS_VM_MEM(Addr)) - return(*(byte *)Addr); - else - return(*Addr & 0xff); -#else - return(*(byte *)Addr); -#endif - } - else - { -#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) - if (IS_VM_MEM(Addr)) - { - byte *B=(byte *)Addr; - return UINT32((uint)B[0]|((uint)B[1]<<8)|((uint)B[2]<<16)|((uint)B[3]<<24)); - } - else - return UINT32(*Addr); -#else - return UINT32(*Addr); -#endif - } -} - -#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) - #define GET_VALUE(ByteMode,Addr) GetValue(ByteMode,(uint *)Addr) -#else - #define GET_VALUE(ByteMode,Addr) ((ByteMode) ? (*(byte *)(Addr)):UINT32(*(uint *)(Addr))) -#endif - - -inline void RarVM::SetValue(bool ByteMode,uint *Addr,uint Value) -{ - if (ByteMode) - { -#ifdef BIG_ENDIAN - if (IS_VM_MEM(Addr)) - *(byte *)Addr=Value; - else - *Addr=(*Addr & ~0xff)|(Value & 0xff); -#else - *(byte *)Addr=Value; -#endif - } - else - { -#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) - if (IS_VM_MEM(Addr)) - { - ((byte *)Addr)[0]=(byte)Value; - ((byte *)Addr)[1]=(byte)(Value>>8); - ((byte *)Addr)[2]=(byte)(Value>>16); - ((byte *)Addr)[3]=(byte)(Value>>24); - } - else - *(uint *)Addr=Value; -#else - *(uint32 *)Addr=Value; -#endif - } -} - -#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) - #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint *)Addr,Value) -#else - #define SET_VALUE(ByteMode,Addr,Value) ((ByteMode) ? (*(byte *)(Addr)=(Value)):(*(uint32 *)(Addr)=((uint32)(Value)))) -#endif - - -void RarVM::SetLowEndianValue(uint *Addr,uint Value) -{ -#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) - ((byte *)Addr)[0]=(byte)Value; - ((byte *)Addr)[1]=(byte)(Value>>8); - ((byte *)Addr)[2]=(byte)(Value>>16); - ((byte *)Addr)[3]=(byte)(Value>>24); -#else - *(uint32 *)Addr=Value; -#endif -} - - -inline uint* RarVM::GetOperand(VM_PreparedOperand *CmdOp) -{ - if (CmdOp->Type==VM_OPREGMEM) - return((uint *)&Mem[(*CmdOp->Addr+CmdOp->Base)&VM_MEMMASK]); - else - return(CmdOp->Addr); -} - void RarVM::Execute(VM_PreparedProgram *Prg) { memcpy(R,Prg->InitR,sizeof(Prg->InitR)); - unsigned int GlobalSize=Min(Prg->GlobalData.Size(),VM_GLOBALMEMSIZE); - if (GlobalSize) - memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize); - unsigned int StaticSize=Min(Prg->StaticData.Size(),VM_GLOBALMEMSIZE-GlobalSize); - if (StaticSize) - memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize); - - R[7]=VM_MEMSIZE; - Flags=0; - - VM_PreparedCommand *PreparedCode=Prg->AltCmd ? Prg->AltCmd:&Prg->Cmd[0]; - if (!ExecuteCode(PreparedCode,Prg->CmdCount)) - PreparedCode[0].OpCode=VM_RET; - uint NewBlockPos=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20])&VM_MEMMASK; - uint NewBlockSize=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c])&VM_MEMMASK; - if (NewBlockPos+NewBlockSize>=VM_MEMSIZE) - NewBlockPos=NewBlockSize=0; - Prg->FilteredData=Mem+NewBlockPos; - Prg->FilteredDataSize=NewBlockSize; - - Prg->GlobalData.Reset(); - - uint DataSize=Min(GET_VALUE(false,(uint*)&Mem[VM_GLOBALMEMADDR+0x30]),VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE); - if (DataSize!=0) + Prg->FilteredData=NULL; + if (Prg->Type!=VMSF_NONE) { - Prg->GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE); - memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE); - } -} - - -/* -Note: - Due to performance considerations RAR VM may set VM_FS, VM_FC, VM_FZ - incorrectly for byte operands. These flags are always valid only - for 32-bit operands. Check implementation of concrete VM command - to see if it sets flags right. -*/ - -#define SET_IP(IP) \ - if ((IP)>=CodeSize) \ - return(true); \ - if (--MaxOpCount<=0) \ - return(false); \ - Cmd=PreparedCode+(IP); - -bool RarVM::ExecuteCode(VM_PreparedCommand *PreparedCode,int CodeSize) -{ - int MaxOpCount=25000000; - VM_PreparedCommand *Cmd=PreparedCode; - while (1) - { -#ifndef NORARVM - uint *Op1=GetOperand(&Cmd->Op1); - uint *Op2=GetOperand(&Cmd->Op2); -#endif - switch(Cmd->OpCode) - { -#ifndef NORARVM - case VM_MOV: - SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); - break; -#ifdef VM_OPTIMIZE - case VM_MOVB: - SET_VALUE(true,Op1,GET_VALUE(true,Op2)); - break; - case VM_MOVD: - SET_VALUE(false,Op1,GET_VALUE(false,Op2)); - break; -#endif - case VM_CMP: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - } - break; -#ifdef VM_OPTIMIZE - case VM_CMPB: - { - uint Value1=GET_VALUE(true,Op1); - uint Result=UINT32(Value1-GET_VALUE(true,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - } - break; - case VM_CMPD: - { - uint Value1=GET_VALUE(false,Op1); - uint Result=UINT32(Value1-GET_VALUE(false,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - } - break; -#endif - case VM_ADD: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)); - if (Cmd->ByteMode) - { - Result&=0xff; - Flags=(Result<Value1)|(Result==0 ? VM_FZ:((Result&0x80) ? VM_FS:0)); - } - else - Flags=(Result<Value1)|(Result==0 ? VM_FZ:(Result&VM_FS)); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; -#ifdef VM_OPTIMIZE - case VM_ADDB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)+GET_VALUE(true,Op2)); - break; - case VM_ADDD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)+GET_VALUE(false,Op2)); - break; -#endif - case VM_SUB: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; -#ifdef VM_OPTIMIZE - case VM_SUBB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)-GET_VALUE(true,Op2)); - break; - case VM_SUBD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)-GET_VALUE(false,Op2)); - break; -#endif - case VM_JZ: - if ((Flags & VM_FZ)!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JNZ: - if ((Flags & VM_FZ)==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_INC: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)+1); - if (Cmd->ByteMode) - Result&=0xff; - SET_VALUE(Cmd->ByteMode,Op1,Result); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - } - break; -#ifdef VM_OPTIMIZE - case VM_INCB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)+1); - break; - case VM_INCD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)+1); - break; -#endif - case VM_DEC: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)-1); - SET_VALUE(Cmd->ByteMode,Op1,Result); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - } - break; -#ifdef VM_OPTIMIZE - case VM_DECB: - SET_VALUE(true,Op1,GET_VALUE(true,Op1)-1); - break; - case VM_DECD: - SET_VALUE(false,Op1,GET_VALUE(false,Op1)-1); - break; -#endif - case VM_JMP: - SET_IP(GET_VALUE(false,Op1)); - continue; - case VM_XOR: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)^GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_AND: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_OR: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)|GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_TEST: - { - uint Result=UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); - Flags=Result==0 ? VM_FZ:Result&VM_FS; - } - break; - case VM_JS: - if ((Flags & VM_FS)!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JNS: - if ((Flags & VM_FS)==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JB: - if ((Flags & VM_FC)!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JBE: - if ((Flags & (VM_FC|VM_FZ))!=0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JA: - if ((Flags & (VM_FC|VM_FZ))==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_JAE: - if ((Flags & VM_FC)==0) - { - SET_IP(GET_VALUE(false,Op1)); - continue; - } - break; - case VM_PUSH: - R[7]-=4; - SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],GET_VALUE(false,Op1)); - break; - case VM_POP: - SET_VALUE(false,Op1,GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); - R[7]+=4; - break; - case VM_CALL: - R[7]-=4; - SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Cmd-PreparedCode+1); - SET_IP(GET_VALUE(false,Op1)); - continue; - case VM_NOT: - SET_VALUE(Cmd->ByteMode,Op1,~GET_VALUE(Cmd->ByteMode,Op1)); - break; - case VM_SHL: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Value2=GET_VALUE(Cmd->ByteMode,Op2); - uint Result=UINT32(Value1<<Value2); - Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1<<(Value2-1))&0x80000000 ? VM_FC:0); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_SHR: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Value2=GET_VALUE(Cmd->ByteMode,Op2); - uint Result=UINT32(Value1>>Value2); - Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_SAR: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint Value2=GET_VALUE(Cmd->ByteMode,Op2); - uint Result=UINT32(((int)Value1)>>Value2); - Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_NEG: - { - uint Result=UINT32(-GET_VALUE(Cmd->ByteMode,Op1)); - Flags=Result==0 ? VM_FZ:VM_FC|(Result&VM_FS); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; -#ifdef VM_OPTIMIZE - case VM_NEGB: - SET_VALUE(true,Op1,-GET_VALUE(true,Op1)); - break; - case VM_NEGD: - SET_VALUE(false,Op1,-GET_VALUE(false,Op1)); - break; -#endif - case VM_PUSHA: - { - const int RegCount=sizeof(R)/sizeof(R[0]); - for (int I=0,SP=R[7]-4;I<RegCount;I++,SP-=4) - SET_VALUE(false,(uint *)&Mem[SP & VM_MEMMASK],R[I]); - R[7]-=RegCount*4; - } - break; - case VM_POPA: - { - const int RegCount=sizeof(R)/sizeof(R[0]); - for (uint I=0,SP=R[7];I<RegCount;I++,SP+=4) - R[7-I]=GET_VALUE(false,(uint *)&Mem[SP & VM_MEMMASK]); - } - break; - case VM_PUSHF: - R[7]-=4; - SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Flags); - break; - case VM_POPF: - Flags=GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK]); - R[7]+=4; - break; - case VM_MOVZX: - SET_VALUE(false,Op1,GET_VALUE(true,Op2)); - break; - case VM_MOVSX: - SET_VALUE(false,Op1,(signed char)GET_VALUE(true,Op2)); - break; - case VM_XCHG: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); - SET_VALUE(Cmd->ByteMode,Op2,Value1); - } - break; - case VM_MUL: - { - uint Result=GET_VALUE(Cmd->ByteMode,Op1)*GET_VALUE(Cmd->ByteMode,Op2); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_DIV: - { - uint Divider=GET_VALUE(Cmd->ByteMode,Op2); - if (Divider!=0) - { - uint Result=GET_VALUE(Cmd->ByteMode,Op1)/Divider; - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - } - break; - case VM_ADC: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint FC=(Flags&VM_FC); - uint Result=UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)+FC); - if (Cmd->ByteMode) - Result&=0xff; - Flags=(Result<Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS)); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; - case VM_SBB: - { - uint Value1=GET_VALUE(Cmd->ByteMode,Op1); - uint FC=(Flags&VM_FC); - uint Result=UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)-FC); - if (Cmd->ByteMode) - Result&=0xff; - Flags=(Result>Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS)); - SET_VALUE(Cmd->ByteMode,Op1,Result); - } - break; -#endif // for #ifndef NORARVM - case VM_RET: - if (R[7]>=VM_MEMSIZE) - return(true); - SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); - R[7]+=4; - continue; -#ifdef VM_STANDARDFILTERS - case VM_STANDARD: - ExecuteStandardFilter((VM_StandardFilters)Cmd->Op1.Data); - break; -#endif - case VM_PRINT: - break; - } - Cmd++; - --MaxOpCount; + bool Success=ExecuteStandardFilter(Prg->Type); + uint BlockSize=Prg->InitR[4] & VM_MEMMASK; + Prg->FilteredDataSize=BlockSize; + if (Prg->Type==VMSF_DELTA || Prg->Type==VMSF_RGB || Prg->Type==VMSF_AUDIO) + Prg->FilteredData=2*BlockSize>VM_MEMSIZE || !Success ? Mem:Mem+BlockSize; + else + Prg->FilteredData=Mem; } } - - -void RarVM::Prepare(byte *Code,int CodeSize,VM_PreparedProgram *Prg) +void RarVM::Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg) { - InitBitInput(); - memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); - + // Calculate the single byte XOR checksum to check validity of VM code. byte XorSum=0; - for (int I=1;I<CodeSize;I++) + for (uint I=1;I<CodeSize;I++) XorSum^=Code[I]; - faddbits(8); + if (XorSum!=Code[0]) + return; - Prg->CmdCount=0; - if (XorSum==Code[0]) + struct StandardFilters { -#ifdef VM_STANDARDFILTERS - VM_StandardFilters FilterType=IsStandardFilter(Code,CodeSize); - if (FilterType!=VMSF_NONE) - { - Prg->Cmd.Add(1); - VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; - CurCmd->OpCode=VM_STANDARD; - CurCmd->Op1.Data=FilterType; - CurCmd->Op1.Addr=&CurCmd->Op1.Data; - CurCmd->Op2.Addr=&CurCmd->Op2.Data; - CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; - CodeSize=0; - } -#endif - uint DataFlag=fgetbits(); - faddbits(1); - -/* Read static data contained in DB operators. This data cannot be changed, - it is a part of VM code, not a filter parameter. -*/ - if (DataFlag&0x8000) - { - int DataSize=ReadData(*this)+1; - for (int I=0;InAddr<CodeSize && I<DataSize;I++) - { - Prg->StaticData.Add(1); - Prg->StaticData[I]=fgetbits()>>8; - faddbits(8); - } - } - - while (InAddr<CodeSize) - { - Prg->Cmd.Add(1); - VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount]; - uint Data=fgetbits(); - if ((Data&0x8000)==0) - { - CurCmd->OpCode=(VM_Commands)(Data>>12); - faddbits(4); - } - else - { - CurCmd->OpCode=(VM_Commands)((Data>>10)-24); - faddbits(6); - } - if (VM_CmdFlags[CurCmd->OpCode] & VMCF_BYTEMODE) - { - CurCmd->ByteMode=fgetbits()>>15; - faddbits(1); - } - else - CurCmd->ByteMode=0; - CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; - int OpNum=(VM_CmdFlags[CurCmd->OpCode] & VMCF_OPMASK); - CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; - if (OpNum>0) - { - DecodeArg(CurCmd->Op1,CurCmd->ByteMode); - if (OpNum==2) - DecodeArg(CurCmd->Op2,CurCmd->ByteMode); - else - { - if (CurCmd->Op1.Type==VM_OPINT && (VM_CmdFlags[CurCmd->OpCode]&(VMCF_JUMP|VMCF_PROC))) - { - int Distance=CurCmd->Op1.Data; - if (Distance>=256) - Distance-=256; - else - { - if (Distance>=136) - Distance-=264; - else - if (Distance>=16) - Distance-=8; - else - if (Distance>=8) - Distance-=16; - Distance+=Prg->CmdCount; - } - CurCmd->Op1.Data=Distance; - } - } - } - Prg->CmdCount++; - } - } - Prg->Cmd.Add(1); - VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; - CurCmd->OpCode=VM_RET; - CurCmd->Op1.Addr=&CurCmd->Op1.Data; - CurCmd->Op2.Addr=&CurCmd->Op2.Data; - CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; - - for (int I=0;I<Prg->CmdCount;I++) - { - VM_PreparedCommand *Cmd=&Prg->Cmd[I]; - if (Cmd->Op1.Addr==NULL) - Cmd->Op1.Addr=&Cmd->Op1.Data; - if (Cmd->Op2.Addr==NULL) - Cmd->Op2.Addr=&Cmd->Op2.Data; - } - -#ifdef VM_OPTIMIZE - if (CodeSize!=0) - Optimize(Prg); -#endif -} - - -void RarVM::DecodeArg(VM_PreparedOperand &Op,bool ByteMode) -{ - uint Data=fgetbits(); - if (Data & 0x8000) - { - Op.Type=VM_OPREG; - Op.Data=(Data>>12)&7; - Op.Addr=&R[Op.Data]; - faddbits(4); - } - else - if ((Data & 0xc000)==0) - { - Op.Type=VM_OPINT; - if (ByteMode) - { - Op.Data=(Data>>6) & 0xff; - faddbits(10); - } - else - { - faddbits(2); - Op.Data=ReadData(*this); - } - } - else + uint Length; + uint CRC; + VM_StandardFilters Type; + } static StdList[]={ + 53, 0xad576887, VMSF_E8, + 57, 0x3cd7e57e, VMSF_E8E9, + 120, 0x3769893f, VMSF_ITANIUM, + 29, 0x0e06077d, VMSF_DELTA, + 149, 0x1c2c5dc8, VMSF_RGB, + 216, 0xbc85e701, VMSF_AUDIO + }; + uint CodeCRC=CRC32(0xffffffff,Code,CodeSize)^0xffffffff; + for (uint I=0;I<ASIZE(StdList);I++) + if (StdList[I].CRC==CodeCRC && StdList[I].Length==CodeSize) { - Op.Type=VM_OPREGMEM; - if ((Data & 0x2000)==0) - { - Op.Data=(Data>>10)&7; - Op.Addr=&R[Op.Data]; - Op.Base=0; - faddbits(6); - } - else - { - if ((Data & 0x1000)==0) - { - Op.Data=(Data>>9)&7; - Op.Addr=&R[Op.Data]; - faddbits(7); - } - else - { - Op.Data=0; - faddbits(4); - } - Op.Base=ReadData(*this); - } + Prg->Type=StdList[I].Type; + break; } } @@ -716,7 +76,7 @@ uint RarVM::ReadData(BitInput &Inp) { case 0: Inp.faddbits(6); - return((Data>>10)&0xf); + return (Data>>10)&0xf; case 0x4000: if ((Data&0x3c00)==0) { @@ -728,113 +88,38 @@ uint RarVM::ReadData(BitInput &Inp) Data=(Data>>6)&0xff; Inp.faddbits(10); } - return(Data); + return Data; case 0x8000: Inp.faddbits(2); Data=Inp.fgetbits(); Inp.faddbits(16); - return(Data); + return Data; default: Inp.faddbits(2); Data=(Inp.fgetbits()<<16); Inp.faddbits(16); Data|=Inp.fgetbits(); Inp.faddbits(16); - return(Data); + return Data; } } -void RarVM::SetMemory(unsigned int Pos,byte *Data,unsigned int DataSize) +void RarVM::SetMemory(size_t Pos,byte *Data,size_t DataSize) { if (Pos<VM_MEMSIZE && Data!=Mem+Pos) - memmove(Mem+Pos,Data,Min(DataSize,VM_MEMSIZE-Pos)); -} - - -#ifdef VM_OPTIMIZE -void RarVM::Optimize(VM_PreparedProgram *Prg) -{ - VM_PreparedCommand *Code=&Prg->Cmd[0]; - int CodeSize=Prg->CmdCount; - - for (int I=0;I<CodeSize;I++) { - VM_PreparedCommand *Cmd=Code+I; - switch(Cmd->OpCode) - { - case VM_MOV: - Cmd->OpCode=Cmd->ByteMode ? VM_MOVB:VM_MOVD; - continue; - case VM_CMP: - Cmd->OpCode=Cmd->ByteMode ? VM_CMPB:VM_CMPD; - continue; - } - if ((VM_CmdFlags[Cmd->OpCode] & VMCF_CHFLAGS)==0) - continue; - bool FlagsRequired=false; - for (int J=I+1;J<CodeSize;J++) - { - int Flags=VM_CmdFlags[Code[J].OpCode]; - if (Flags & (VMCF_JUMP|VMCF_PROC|VMCF_USEFLAGS)) - { - FlagsRequired=true; - break; - } - if (Flags & VMCF_CHFLAGS) - break; - } - if (FlagsRequired) - continue; - switch(Cmd->OpCode) - { - case VM_ADD: - Cmd->OpCode=Cmd->ByteMode ? VM_ADDB:VM_ADDD; - continue; - case VM_SUB: - Cmd->OpCode=Cmd->ByteMode ? VM_SUBB:VM_SUBD; - continue; - case VM_INC: - Cmd->OpCode=Cmd->ByteMode ? VM_INCB:VM_INCD; - continue; - case VM_DEC: - Cmd->OpCode=Cmd->ByteMode ? VM_DECB:VM_DECD; - continue; - case VM_NEG: - Cmd->OpCode=Cmd->ByteMode ? VM_NEGB:VM_NEGD; - continue; - } + // We can have NULL Data for invalid filters with DataSize==0. While most + // sensible memmove implementations do not care about data if size is 0, + // let's follow the standard and check the size first. + size_t CopySize=Min(DataSize,VM_MEMSIZE-Pos); + if (CopySize!=0) + memmove(Mem+Pos,Data,CopySize); } } -#endif -#ifdef VM_STANDARDFILTERS -VM_StandardFilters RarVM::IsStandardFilter(byte *Code,int CodeSize) -{ - struct StandardFilterSignature - { - int Length; - uint CRC; - VM_StandardFilters Type; - } StdList[]={ - 53, 0xad576887, VMSF_E8, - 57, 0x3cd7e57e, VMSF_E8E9, - 120, 0x3769893f, VMSF_ITANIUM, - 29, 0x0e06077d, VMSF_DELTA, - 149, 0x1c2c5dc8, VMSF_RGB, - 216, 0xbc85e701, VMSF_AUDIO, - 40, 0x46b9c560, VMSF_UPCASE - }; - uint CodeCRC=CRC(0xffffffff,Code,CodeSize)^0xffffffff; - for (int I=0;I<sizeof(StdList)/sizeof(StdList[0]);I++) - if (StdList[I].CRC==CodeCRC && StdList[I].Length==CodeSize) - return(StdList[I].Type); - return(VMSF_NONE); -} - - -void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) +bool RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) { switch(FilterType) { @@ -842,43 +127,32 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) case VMSF_E8E9: { byte *Data=Mem; - int DataSize=R[4]; - uint FileOffset=R[6]; + uint DataSize=R[4],FileOffset=R[6]; - if (DataSize>=VM_GLOBALMEMADDR || DataSize<4) - break; + if (DataSize>VM_MEMSIZE || DataSize<4) + return false; - const int FileSize=0x1000000; + const uint FileSize=0x1000000; byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8; - for (int CurPos=0;CurPos<DataSize-4;) + for (uint CurPos=0;CurPos<DataSize-4;) { byte CurByte=*(Data++); CurPos++; if (CurByte==0xe8 || CurByte==CmpByte2) { -#ifdef PRESENT_INT32 - sint32 Offset=CurPos+FileOffset; - sint32 Addr=GET_VALUE(false,Data); - if (Addr<0) + uint Offset=CurPos+FileOffset; + uint Addr=RawGet4(Data); + + // We check 0x80000000 bit instead of '< 0' comparison + // not assuming int32 presence or uint size and endianness. + if ((Addr & 0x80000000)!=0) // Addr<0 { - if (Addr+Offset>=0) - SET_VALUE(false,Data,Addr+FileSize); + if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0 + RawPut4(Addr+FileSize,Data); } else - if (Addr<FileSize) - SET_VALUE(false,Data,Addr-Offset); -#else - long Offset=CurPos+FileOffset; - long Addr=GET_VALUE(false,Data); - if ((Addr & 0x80000000)!=0) - { - if (((Addr+Offset) & 0x80000000)==0) - SET_VALUE(false,Data,Addr+FileSize); - } - else - if (((Addr-FileSize) & 0x80000000)!=0) - SET_VALUE(false,Data,Addr-Offset); -#endif + if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize + RawPut4(Addr-Offset,Data); Data+=4; CurPos+=4; } @@ -888,13 +162,12 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) case VMSF_ITANIUM: { byte *Data=Mem; - int DataSize=R[4]; - uint FileOffset=R[6]; + uint DataSize=R[4],FileOffset=R[6]; - if (DataSize>=VM_GLOBALMEMADDR || DataSize<21) - break; + if (DataSize>VM_MEMSIZE || DataSize<21) + return false; - int CurPos=0; + uint CurPos=0; FileOffset>>=4; @@ -906,14 +179,14 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0}; byte CmdMask=Masks[Byte]; if (CmdMask!=0) - for (int I=0;I<=2;I++) + for (uint I=0;I<=2;I++) if (CmdMask & (1<<I)) { - int StartPos=I*41+5; - int OpType=FilterItanium_GetBits(Data,StartPos+37,4); + uint StartPos=I*41+5; + uint OpType=FilterItanium_GetBits(Data,StartPos+37,4); if (OpType==5) { - int Offset=FilterItanium_GetBits(Data,StartPos+13,20); + uint Offset=FilterItanium_GetBits(Data,StartPos+13,20); FilterItanium_SetBits(Data,(Offset-FileOffset)&0xfffff,StartPos+13,20); } } @@ -926,43 +199,39 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) break; case VMSF_DELTA: { - int DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2; - SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - if (DataSize>=VM_GLOBALMEMADDR/2) - break; + uint DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2; + if (DataSize>VM_MEMSIZE/2 || Channels>MAX3_UNPACK_CHANNELS || Channels==0) + return false; -// bytes from same channels are grouped to continual data blocks, -// so we need to place them back to their interleaving positions - - for (int CurChannel=0;CurChannel<Channels;CurChannel++) + // Bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions. + for (uint CurChannel=0;CurChannel<Channels;CurChannel++) { byte PrevByte=0; - for (int DestPos=DataSize+CurChannel;DestPos<Border;DestPos+=Channels) + for (uint DestPos=DataSize+CurChannel;DestPos<Border;DestPos+=Channels) Mem[DestPos]=(PrevByte-=Mem[SrcPos++]); } } break; case VMSF_RGB: { - int DataSize=R[4],Width=R[0]-3,PosR=R[1]; + uint DataSize=R[4],Width=R[0]-3,PosR=R[1]; + if (DataSize>VM_MEMSIZE/2 || DataSize<3 || Width>DataSize || PosR>2) + return false; byte *SrcData=Mem,*DestData=SrcData+DataSize; - const int Channels=3; - SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - if (DataSize>=VM_GLOBALMEMADDR/2 || PosR<0) - break; - for (int CurChannel=0;CurChannel<Channels;CurChannel++) + const uint Channels=3; + for (uint CurChannel=0;CurChannel<Channels;CurChannel++) { - unsigned int PrevByte=0; + uint PrevByte=0; - for (int I=CurChannel;I<DataSize;I+=Channels) + for (uint I=CurChannel;I<DataSize;I+=Channels) { - unsigned int Predicted; - int UpperPos=I-Width; - if (UpperPos>=3) + uint Predicted; + if (I>=Width+3) { - byte *UpperData=DestData+UpperPos; - unsigned int UpperByte=*UpperData; - unsigned int UpperLeftByte=*(UpperData-3); + byte *UpperData=DestData+I-Width; + uint UpperByte=*UpperData; + uint UpperLeftByte=*(UpperData-3); Predicted=PrevByte+UpperByte-UpperLeftByte; int pa=abs((int)(Predicted-PrevByte)); int pb=abs((int)(Predicted-UpperByte)); @@ -980,7 +249,7 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); } } - for (int I=PosR,Border=DataSize-2;I<Border;I+=3) + for (uint I=PosR,Border=DataSize-2;I<Border;I+=3) { byte G=DestData[I+1]; DestData[I]+=G; @@ -990,35 +259,38 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) break; case VMSF_AUDIO: { - int DataSize=R[4],Channels=R[0]; + uint DataSize=R[4],Channels=R[0]; byte *SrcData=Mem,*DestData=SrcData+DataSize; - SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - if (DataSize>=VM_GLOBALMEMADDR/2) - break; - for (int CurChannel=0;CurChannel<Channels;CurChannel++) + // In fact, audio channels never exceed 4. + if (DataSize>VM_MEMSIZE/2 || Channels>128 || Channels==0) + return false; + for (uint CurChannel=0;CurChannel<Channels;CurChannel++) { - unsigned int PrevByte=0,PrevDelta=0,Dif[7]; + uint PrevByte=0,PrevDelta=0,Dif[7]; int D1=0,D2=0,D3; int K1=0,K2=0,K3=0; memset(Dif,0,sizeof(Dif)); - for (int I=CurChannel,ByteCount=0;I<DataSize;I+=Channels,ByteCount++) + for (uint I=CurChannel,ByteCount=0;I<DataSize;I+=Channels,ByteCount++) { D3=D2; D2=PrevDelta-D1; D1=PrevDelta; - unsigned int Predicted=8*PrevByte+K1*D1+K2*D2+K3*D3; + uint Predicted=8*PrevByte+K1*D1+K2*D2+K3*D3; Predicted=(Predicted>>3) & 0xff; - unsigned int CurByte=*(SrcData++); + uint CurByte=*(SrcData++); Predicted-=CurByte; DestData[I]=Predicted; PrevDelta=(signed char)(Predicted-PrevByte); PrevByte=Predicted; - int D=((signed char)CurByte)<<3; + int D=(signed char)CurByte; + // Left shift of negative value is undefined behavior in C++, + // so we cast it to unsigned to follow the standard. + D=(uint)D<<3; Dif[0]+=abs(D); Dif[1]+=abs(D-D1); @@ -1030,9 +302,9 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) if ((ByteCount & 0x1f)==0) { - unsigned int MinDif=Dif[0],NumMinDif=0; + uint MinDif=Dif[0],NumMinDif=0; Dif[0]=0; - for (int J=1;J<sizeof(Dif)/sizeof(Dif[0]);J++) + for (uint J=1;J<ASIZE(Dif);J++) { if (Dif[J]<MinDif) { @@ -1055,50 +327,34 @@ void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) } } break; - case VMSF_UPCASE: - { - int DataSize=R[4],SrcPos=0,DestPos=DataSize; - if (DataSize>=VM_GLOBALMEMADDR/2) - break; - while (SrcPos<DataSize) - { - byte CurByte=Mem[SrcPos++]; - if (CurByte==2 && (CurByte=Mem[SrcPos++])!=2) - CurByte-=32; - Mem[DestPos++]=CurByte; - } - SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c],DestPos-DataSize); - SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); - } - break; } + return true; } -unsigned int RarVM::FilterItanium_GetBits(byte *Data,int BitPos,int BitCount) +uint RarVM::FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount) { - int InAddr=BitPos/8; - int InBit=BitPos&7; - unsigned int BitField=(uint)Data[InAddr++]; + uint InAddr=BitPos/8; + uint InBit=BitPos&7; + uint BitField=(uint)Data[InAddr++]; BitField|=(uint)Data[InAddr++] << 8; BitField|=(uint)Data[InAddr++] << 16; BitField|=(uint)Data[InAddr] << 24; BitField >>= InBit; - return(BitField & (0xffffffff>>(32-BitCount))); + return BitField & (0xffffffff>>(32-BitCount)); } -void RarVM::FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos, - int BitCount) +void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount) { - int InAddr=BitPos/8; - int InBit=BitPos&7; - unsigned int AndMask=0xffffffff>>(32-BitCount); + uint InAddr=BitPos/8; + uint InBit=BitPos&7; + uint AndMask=0xffffffff>>(32-BitCount); AndMask=~(AndMask<<InBit); BitField<<=InBit; - for (int I=0;I<4;I++) + for (uint I=0;I<4;I++) { Data[InAddr+I]&=AndMask; Data[InAddr+I]|=BitField; @@ -1106,4 +362,3 @@ void RarVM::FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos, BitField>>=8; } } -#endif diff --git a/unrar/unrar/rarvm.hpp b/unrar/unrar/rarvm.hpp index 0a6ff8a..e65c4b1 100644 --- a/unrar/unrar/rarvm.hpp +++ b/unrar/unrar/rarvm.hpp @@ -1,108 +1,43 @@ #ifndef _RAR_VM_ #define _RAR_VM_ -#define VM_STANDARDFILTERS - -#ifndef SFX_MODULE -#define VM_OPTIMIZE -#endif - - #define VM_MEMSIZE 0x40000 #define VM_MEMMASK (VM_MEMSIZE-1) -#define VM_GLOBALMEMADDR 0x3C000 -#define VM_GLOBALMEMSIZE 0x2000 -#define VM_FIXEDGLOBALSIZE 64 - -enum VM_Commands -{ - VM_MOV, VM_CMP, VM_ADD, VM_SUB, VM_JZ, VM_JNZ, VM_INC, VM_DEC, - VM_JMP, VM_XOR, VM_AND, VM_OR, VM_TEST, VM_JS, VM_JNS, VM_JB, - VM_JBE, VM_JA, VM_JAE, VM_PUSH, VM_POP, VM_CALL, VM_RET, VM_NOT, - VM_SHL, VM_SHR, VM_SAR, VM_NEG, VM_PUSHA,VM_POPA, VM_PUSHF,VM_POPF, - VM_MOVZX,VM_MOVSX,VM_XCHG, VM_MUL, VM_DIV, VM_ADC, VM_SBB, VM_PRINT, - -#ifdef VM_OPTIMIZE - VM_MOVB, VM_MOVD, VM_CMPB, VM_CMPD, - - VM_ADDB, VM_ADDD, VM_SUBB, VM_SUBD, VM_INCB, VM_INCD, VM_DECB, VM_DECD, - VM_NEGB, VM_NEGD, -#endif - - VM_STANDARD -}; enum VM_StandardFilters { VMSF_NONE, VMSF_E8, VMSF_E8E9, VMSF_ITANIUM, VMSF_RGB, VMSF_AUDIO, - VMSF_DELTA, VMSF_UPCASE + VMSF_DELTA }; -enum VM_Flags {VM_FC=1,VM_FZ=2,VM_FS=0x80000000}; - -enum VM_OpType {VM_OPREG,VM_OPINT,VM_OPREGMEM,VM_OPNONE}; - -struct VM_PreparedOperand -{ - VM_OpType Type; - uint Data; - uint Base; - uint *Addr; -}; - -struct VM_PreparedCommand -{ - VM_Commands OpCode; - bool ByteMode; - VM_PreparedOperand Op1,Op2; -}; - - struct VM_PreparedProgram { - VM_PreparedProgram() {AltCmd=NULL;} - - Array<VM_PreparedCommand> Cmd; - VM_PreparedCommand *AltCmd; - int CmdCount; - - Array<byte> GlobalData; - Array<byte> StaticData; // static data contained in DB operators + VM_PreparedProgram() + { + FilteredDataSize=0; + Type=VMSF_NONE; + } + VM_StandardFilters Type; uint InitR[7]; - byte *FilteredData; - unsigned int FilteredDataSize; + uint FilteredDataSize; }; -class RarVM:private BitInput +class RarVM { private: - inline uint GetValue(bool ByteMode,uint *Addr); - inline void SetValue(bool ByteMode,uint *Addr,uint Value); - inline uint* GetOperand(VM_PreparedOperand *CmdOp); - void DecodeArg(VM_PreparedOperand &Op,bool ByteMode); -#ifdef VM_OPTIMIZE - void Optimize(VM_PreparedProgram *Prg); -#endif - bool ExecuteCode(VM_PreparedCommand *PreparedCode,int CodeSize); -#ifdef VM_STANDARDFILTERS - VM_StandardFilters IsStandardFilter(byte *Code,int CodeSize); - void ExecuteStandardFilter(VM_StandardFilters FilterType); - unsigned int FilterItanium_GetBits(byte *Data,int BitPos,int BitCount); - void FilterItanium_SetBits(byte *Data,unsigned int BitField,int BitPos, - int BitCount); -#endif + bool ExecuteStandardFilter(VM_StandardFilters FilterType); + uint FilterItanium_GetBits(byte *Data,uint BitPos,uint BitCount); + void FilterItanium_SetBits(byte *Data,uint BitField,uint BitPos,uint BitCount); byte *Mem; uint R[8]; - uint Flags; public: RarVM(); ~RarVM(); void Init(); - void Prepare(byte *Code,int CodeSize,VM_PreparedProgram *Prg); + void Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg); void Execute(VM_PreparedProgram *Prg); - void SetLowEndianValue(uint *Addr,uint Value); - void SetMemory(unsigned int Pos,byte *Data,unsigned int DataSize); + void SetMemory(size_t Pos,byte *Data,size_t DataSize); static uint ReadData(BitInput &Inp); }; diff --git a/unrar/unrar/rarvmtbl.cpp b/unrar/unrar/rarvmtbl.cpp deleted file mode 100644 index b5e6c72..0000000 --- a/unrar/unrar/rarvmtbl.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#define VMCF_OP0 0 -#define VMCF_OP1 1 -#define VMCF_OP2 2 -#define VMCF_OPMASK 3 -#define VMCF_BYTEMODE 4 -#define VMCF_JUMP 8 -#define VMCF_PROC 16 -#define VMCF_USEFLAGS 32 -#define VMCF_CHFLAGS 64 - -static byte VM_CmdFlags[]= -{ - /* VM_MOV */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_CMP */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_ADD */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SUB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JNZ */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_INC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_DEC */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JMP */ VMCF_OP1 | VMCF_JUMP , - /* VM_XOR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_AND */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_OR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_TEST */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_JS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JNS */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JB */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JBE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JA */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_JAE */ VMCF_OP1 | VMCF_JUMP | VMCF_USEFLAGS , - /* VM_PUSH */ VMCF_OP1 , - /* VM_POP */ VMCF_OP1 , - /* VM_CALL */ VMCF_OP1 | VMCF_PROC , - /* VM_RET */ VMCF_OP0 | VMCF_PROC , - /* VM_NOT */ VMCF_OP1 | VMCF_BYTEMODE , - /* VM_SHL */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SHR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_SAR */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_NEG */ VMCF_OP1 | VMCF_BYTEMODE | VMCF_CHFLAGS , - /* VM_PUSHA */ VMCF_OP0 , - /* VM_POPA */ VMCF_OP0 , - /* VM_PUSHF */ VMCF_OP0 | VMCF_USEFLAGS , - /* VM_POPF */ VMCF_OP0 | VMCF_CHFLAGS , - /* VM_MOVZX */ VMCF_OP2 , - /* VM_MOVSX */ VMCF_OP2 , - /* VM_XCHG */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_MUL */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_DIV */ VMCF_OP2 | VMCF_BYTEMODE , - /* VM_ADC */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , - /* VM_SBB */ VMCF_OP2 | VMCF_BYTEMODE | VMCF_USEFLAGS | VMCF_CHFLAGS , - /* VM_PRINT */ VMCF_OP0 -}; diff --git a/unrar/unrar/rawint.hpp b/unrar/unrar/rawint.hpp new file mode 100644 index 0000000..c8cd86f --- /dev/null +++ b/unrar/unrar/rawint.hpp @@ -0,0 +1,122 @@ +#ifndef _RAR_RAWINT_ +#define _RAR_RAWINT_ + +#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) +#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) +#define rotl32(x,n) rotls(x,n,32) +#define rotr32(x,n) rotrs(x,n,32) + +inline uint RawGet2(const void *Data) +{ + byte *D=(byte *)Data; + return D[0]+(D[1]<<8); +} + + +inline uint32 RawGet4(const void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + return D[0]+(D[1]<<8)+(D[2]<<16)+(D[3]<<24); +#else + return *(uint32 *)Data; +#endif +} + + +inline uint64 RawGet8(const void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + return INT32TO64(RawGet4(D+4),RawGet4(D)); +#else + return *(uint64 *)Data; +#endif +} + + +inline void RawPut2(uint Field,void *Data) +{ + byte *D=(byte *)Data; + D[0]=(byte)(Field); + D[1]=(byte)(Field>>8); +} + + +inline void RawPut4(uint32 Field,void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + D[0]=(byte)(Field); + D[1]=(byte)(Field>>8); + D[2]=(byte)(Field>>16); + D[3]=(byte)(Field>>24); +#else + *(uint32 *)Data=Field; +#endif +} + + +inline void RawPut8(uint64 Field,void *Data) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) + byte *D=(byte *)Data; + D[0]=(byte)(Field); + D[1]=(byte)(Field>>8); + D[2]=(byte)(Field>>16); + D[3]=(byte)(Field>>24); + D[4]=(byte)(Field>>32); + D[5]=(byte)(Field>>40); + D[6]=(byte)(Field>>48); + D[7]=(byte)(Field>>56); +#else + *(uint64 *)Data=Field; +#endif +} + + +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) +#define USE_MEM_BYTESWAP +#endif + +// Load 4 big endian bytes from memory and return uint32. +inline uint32 RawGetBE4(const byte *m) +{ +#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + return _byteswap_ulong(*(uint32 *)m); +#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) + return __builtin_bswap32(*(uint32 *)m); +#else + return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; +#endif +} + + +// Save integer to memory as big endian. +inline void RawPutBE4(uint32 i,byte *mem) +{ +#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) + *(uint32*)mem = _byteswap_ulong(i); +#elif defined(USE_MEM_BYTESWAP) && (defined(__clang__) || defined(__GNUC__)) + *(uint32*)mem = __builtin_bswap32(i); +#else + mem[0]=byte(i>>24); + mem[1]=byte(i>>16); + mem[2]=byte(i>>8); + mem[3]=byte(i); +#endif +} + + +inline uint32 ByteSwap32(uint32 i) +{ +#ifdef _MSC_VER + return _byteswap_ulong(i); +#elif defined(__clang__) || defined(__GNUC__) + return __builtin_bswap32(i); +#else + return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); +#endif +} + +#endif diff --git a/unrar/unrar/rawread.cpp b/unrar/unrar/rawread.cpp index bbba948..d99bac8 100644 --- a/unrar/unrar/rawread.cpp +++ b/unrar/unrar/rawread.cpp @@ -1,45 +1,69 @@ #include "rar.hpp" +RawRead::RawRead() +{ + RawRead::SrcFile=NULL; + Reset(); +} + + RawRead::RawRead(File *SrcFile) { RawRead::SrcFile=SrcFile; + Reset(); +} + + +void RawRead::Reset() +{ + Data.SoftReset(); ReadPos=0; DataSize=0; -#ifndef SHELL_EXT Crypt=NULL; -#endif } -void RawRead::Read(int Size) +size_t RawRead::Read(size_t Size) { -#if !defined(SHELL_EXT) && !defined(NOCRYPT) + size_t ReadSize=0; +#if !defined(RAR_NOCRYPT) if (Crypt!=NULL) { - int CurSize=Data.Size(); - int SizeToRead=Size-(CurSize-DataSize); - if (SizeToRead>0) + // Full size of buffer with already read data including data read + // for encryption block alignment. + size_t FullSize=Data.Size(); + + // Data read for alignment and not processed yet. + size_t DataLeft=FullSize-DataSize; + + if (Size>DataLeft) // Need to read more than we already have. { - int AlignedReadSize=SizeToRead+((~SizeToRead+1)&0xf); + size_t SizeToRead=Size-DataLeft; + size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK); Data.Add(AlignedReadSize); - int ReadSize=SrcFile->Read(&Data[CurSize],AlignedReadSize); - Crypt->DecryptBlock(&Data[CurSize],AlignedReadSize); + ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize); + Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize); DataSize+=ReadSize==0 ? 0:Size; } - else + else // Use buffered data, no real read. + { + ReadSize=Size; DataSize+=Size; + } } else #endif if (Size!=0) { Data.Add(Size); - DataSize+=SrcFile->Read(&Data[DataSize],Size); + ReadSize=SrcFile->Read(&Data[DataSize],Size); + DataSize+=ReadSize; } + return ReadSize; } -void RawRead::Read(byte *SrcData,int Size) +void RawRead::Read(byte *SrcData,size_t Size) { if (Size!=0) { @@ -50,77 +74,124 @@ void RawRead::Read(byte *SrcData,int Size) } -void RawRead::Get(byte &Field) +byte RawRead::Get1() { - if (ReadPos<DataSize) - { - Field=Data[ReadPos]; - ReadPos++; - } - else - Field=0; + return ReadPos<DataSize ? Data[ReadPos++]:0; } -void RawRead::Get(ushort &Field) +ushort RawRead::Get2() { if (ReadPos+1<DataSize) { - Field=Data[ReadPos]+(Data[ReadPos+1]<<8); + ushort Result=Data[ReadPos]+(Data[ReadPos+1]<<8); ReadPos+=2; + return Result; } - else - Field=0; + return 0; } -void RawRead::Get(uint &Field) +uint RawRead::Get4() { if (ReadPos+3<DataSize) { - Field=Data[ReadPos]+(Data[ReadPos+1]<<8)+(Data[ReadPos+2]<<16)+ - (Data[ReadPos+3]<<24); + uint Result=Data[ReadPos]+(Data[ReadPos+1]<<8)+(Data[ReadPos+2]<<16)+ + (Data[ReadPos+3]<<24); ReadPos+=4; + return Result; } - else - Field=0; + return 0; } -void RawRead::Get8(Int64 &Field) +uint64 RawRead::Get8() { - uint Low,High; - Get(Low); - Get(High); - Field=int32to64(High,Low); + uint Low=Get4(),High=Get4(); + return INT32TO64(High,Low); } -void RawRead::Get(byte *Field,int Size) +uint64 RawRead::GetV() { - if (ReadPos+Size-1<DataSize) + uint64 Result=0; + // Need to check Shift<64, because for shift greater than or equal to + // the width of the promoted left operand, the behavior is undefined. + for (uint Shift=0;ReadPos<DataSize && Shift<64;Shift+=7) { - memcpy(Field,&Data[ReadPos],Size); - ReadPos+=Size; + byte CurByte=Data[ReadPos++]; + Result+=uint64(CurByte & 0x7f)<<Shift; + if ((CurByte & 0x80)==0) + return Result; // Decoded successfully. } - else - memset(Field,0,Size); + return 0; // Out of buffer border. } -void RawRead::Get(wchar *Field,int Size) +// Return a number of bytes in current variable length integer. +uint RawRead::GetVSize(size_t Pos) +{ + for (size_t CurPos=Pos;CurPos<DataSize;CurPos++) + if ((Data[CurPos] & 0x80)==0) + return int(CurPos-Pos+1); + return 0; // Buffer overflow. +} + + +size_t RawRead::GetB(void *Field,size_t Size) +{ + byte *F=(byte *)Field; + size_t CopySize=Min(DataSize-ReadPos,Size); + if (CopySize>0) + memcpy(F,&Data[ReadPos],CopySize); + if (Size>CopySize) + memset(F+CopySize,0,Size-CopySize); + ReadPos+=CopySize; + return CopySize; +} + + +void RawRead::GetW(wchar *Field,size_t Size) { if (ReadPos+2*Size-1<DataSize) { RawToWide(&Data[ReadPos],Field,Size); - ReadPos+=2*Size; + ReadPos+=sizeof(wchar)*Size; } else - memset(Field,0,2*Size); + memset(Field,0,sizeof(wchar)*Size); +} + + +uint RawRead::GetCRC15(bool ProcessedOnly) // RAR 1.5 block CRC. +{ + if (DataSize<=2) + return 0; + uint HeaderCRC=CRC32(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2); + return ~HeaderCRC & 0xffff; } -uint RawRead::GetCRC(bool ProcessedOnly) +uint RawRead::GetCRC50() // RAR 5.0 block CRC. { - return(DataSize>2 ? CRC(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2):0xffffffff); + if (DataSize<=4) + return 0xffffffff; + return CRC32(0xffffffff,&Data[4],DataSize-4) ^ 0xffffffff; +} + + +// Read vint from arbitrary byte array. +uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow) +{ + Overflow=false; + uint64 Result=0; + for (uint Shift=0;ReadPos<DataSize;Shift+=7) + { + byte CurByte=Data[ReadPos++]; + Result+=uint64(CurByte & 0x7f)<<Shift; + if ((CurByte & 0x80)==0) + return Result; // Decoded successfully. + } + Overflow=true; + return 0; // Out of buffer border. } diff --git a/unrar/unrar/rawread.hpp b/unrar/unrar/rawread.hpp index 47c2bbc..b319898 100644 --- a/unrar/unrar/rawread.hpp +++ b/unrar/unrar/rawread.hpp @@ -6,27 +6,36 @@ class RawRead private: Array<byte> Data; File *SrcFile; - int DataSize; - int ReadPos; -#ifndef SHELL_EXT + size_t DataSize; + size_t ReadPos; CryptData *Crypt; -#endif public: + RawRead(); RawRead(File *SrcFile); - void Read(int Size); - void Read(byte *SrcData,int Size); - void Get(byte &Field); - void Get(ushort &Field); - void Get(uint &Field); - void Get8(Int64 &Field); - void Get(byte *Field,int Size); - void Get(wchar *Field,int Size); - uint GetCRC(bool ProcessedOnly); - int Size() {return DataSize;} - int PaddedSize() {return Data.Size()-DataSize;} -#ifndef SHELL_EXT + void Reset(); + size_t Read(size_t Size); + void Read(byte *SrcData,size_t Size); + byte Get1(); + ushort Get2(); + uint Get4(); + uint64 Get8(); + uint64 GetV(); + uint GetVSize(size_t Pos); + size_t GetB(void *Field,size_t Size); + void GetW(wchar *Field,size_t Size); + uint GetCRC15(bool ProcessedOnly); + uint GetCRC50(); + byte* GetDataPtr() {return &Data[0];} + size_t Size() {return DataSize;} + size_t PaddedSize() {return Data.Size()-DataSize;} + size_t DataLeft() {return DataSize-ReadPos;} + size_t GetPos() {return ReadPos;} + void SetPos(size_t Pos) {ReadPos=Pos;} + void Skip(size_t Size) {ReadPos+=Size;} + void Rewind() {SetPos(0);} void SetCrypt(CryptData *Crypt) {RawRead::Crypt=Crypt;} -#endif }; +uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow); + #endif diff --git a/unrar/unrar/rdwrfn.cpp b/unrar/unrar/rdwrfn.cpp index 2662bdb..fa71376 100644 --- a/unrar/unrar/rdwrfn.cpp +++ b/unrar/unrar/rdwrfn.cpp @@ -2,6 +2,11 @@ ComprDataIO::ComprDataIO() { +#ifndef RAR_NOCRYPT + Crypt=new CryptData; + Decrypt=new CryptData; +#endif + Init(); } @@ -11,73 +16,120 @@ void ComprDataIO::Init() UnpackFromMemory=false; UnpackToMemory=false; UnpPackedSize=0; + UnpPackedLeft=0; ShowProgress=true; TestMode=false; SkipUnpCRC=false; + NoFileHeader=false; PackVolume=false; UnpVolume=false; NextVolumeMissing=false; SrcFile=NULL; DestFile=NULL; + UnpWrAddr=NULL; UnpWrSize=0; Command=NULL; - Encryption=0; - Decryption=0; - TotalPackRead=0; + Encryption=false; + Decryption=false; CurPackRead=CurPackWrite=CurUnpRead=CurUnpWrite=0; - PackFileCRC=UnpFileCRC=PackedCRC=0xffffffff; LastPercent=-1; SubHead=NULL; SubHeadPos=NULL; CurrentCommand=0; - ProcessedArcSize=TotalArcSize=0; + ProcessedArcSize=0; + LastArcSize=0; + TotalArcSize=0; +} + + +ComprDataIO::~ComprDataIO() +{ +#ifndef RAR_NOCRYPT + delete Crypt; + delete Decrypt; +#endif } -int ComprDataIO::UnpRead(byte *Addr,uint Count) +int ComprDataIO::UnpRead(byte *Addr,size_t Count) { - int RetCode=0,TotalRead=0; +#ifndef RAR_NOCRYPT + // In case of encryption we need to align read size to encryption + // block size. We can do it by simple masking, because unpack read code + // always reads more than CRYPT_BLOCK_SIZE, so we do not risk to make it 0. + if (Decryption) + Count &= ~CRYPT_BLOCK_MASK; +#endif + + int ReadSize=0,TotalRead=0; byte *ReadAddr; ReadAddr=Addr; while (Count > 0) { Archive *SrcArc=(Archive *)SrcFile; - uint ReadSize=(Count>UnpPackedSize) ? int64to32(UnpPackedSize):Count; if (UnpackFromMemory) { memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize); - RetCode=UnpackFromMemorySize; + ReadSize=(int)UnpackFromMemorySize; UnpackFromMemorySize=0; } else { - if (!SrcFile->IsOpened()) - return(-1); - RetCode=SrcFile->Read(ReadAddr,ReadSize); - FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->NewLhd; - if (hd->Flags & LHD_SPLIT_AFTER) - PackedCRC=CRC(PackedCRC,ReadAddr,ReadSize); + size_t SizeToRead=((int64)Count>UnpPackedLeft) ? (size_t)UnpPackedLeft:Count; + if (SizeToRead > 0) + { + if (UnpVolume && Decryption && (int64)Count>UnpPackedLeft) + { + // We need aligned blocks for decryption and we want "Keep broken + // files" to work efficiently with missing encrypted volumes. + // So for last data block in volume we adjust the size to read to + // next equal or smaller block producing aligned total block size. + // So we'll ask for next volume only when processing few unaligned + // bytes left in the end, when most of data is already extracted. + size_t NewTotalRead = TotalRead + SizeToRead; + size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK); + size_t NewSizeToRead = SizeToRead - Adjust; + if ((int)NewSizeToRead > 0) + SizeToRead = NewSizeToRead; + } + + if (!SrcFile->IsOpened()) + return -1; + ReadSize=SrcFile->Read(ReadAddr,SizeToRead); + FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead; + if (!NoFileHeader && hd->SplitAfter) + PackedDataHash.Update(ReadAddr,ReadSize); + } } - CurUnpRead+=RetCode; - TotalRead+=RetCode; + CurUnpRead+=ReadSize; + TotalRead+=ReadSize; #ifndef NOVOLUME - // these variable are not used below in NOVOLUME mode, so it is better - // to exclude these commands to avoid compiler warnings - ReadAddr+=RetCode; - Count-=RetCode; + // These variable are not used in NOVOLUME mode, so it is better + // to exclude commands below to avoid compiler warnings. + ReadAddr+=ReadSize; + Count-=ReadSize; #endif - UnpPackedSize-=RetCode; - if (UnpPackedSize == 0 && UnpVolume) + UnpPackedLeft-=ReadSize; + + // Do not ask for next volume if we read something from current volume. + // If next volume is missing, we need to process all data from current + // volume before aborting. It helps to recover all possible data + // in "Keep broken files" mode. But if we process encrypted data, + // we ask for next volume also if we have non-aligned encryption block. + // Since we adjust data size for decryption earlier above, + // it does not hurt "Keep broken files" mode efficiency. + if (UnpVolume && UnpPackedLeft == 0 && + (ReadSize==0 || Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0) ) { #ifndef NOVOLUME if (!MergeArchive(*SrcArc,this,true,CurrentCommand)) #endif { NextVolumeMissing=true; - return(-1); + return -1; } } else @@ -85,55 +137,39 @@ int ComprDataIO::UnpRead(byte *Addr,uint Count) } Archive *SrcArc=(Archive *)SrcFile; if (SrcArc!=NULL) - ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize); - if (RetCode!=-1) + ShowUnpRead(SrcArc->NextBlockPos-UnpPackedSize+CurUnpRead,TotalArcSize); + if (ReadSize!=-1) { - RetCode=TotalRead; -#ifndef NOCRYPT + ReadSize=TotalRead; +#ifndef RAR_NOCRYPT if (Decryption) -#ifndef SFX_MODULE - if (Decryption<20) - Decrypt.Crypt(Addr,RetCode,(Decryption==15) ? NEW_CRYPT : OLD_DECODE); - else - if (Decryption==20) - for (uint I=0;I<RetCode;I+=16) - Decrypt.DecryptBlock20(&Addr[I]); - else -#endif - { - int CryptSize=(RetCode & 0xf)==0 ? RetCode:((RetCode & ~0xf)+16); - Decrypt.DecryptBlock(Addr,CryptSize); - } + Decrypt->DecryptBlock(Addr,ReadSize); #endif } Wait(); - return(RetCode); + return ReadSize; } -void ComprDataIO::UnpWrite(byte *Addr,uint Count) +void ComprDataIO::UnpWrite(byte *Addr,size_t Count) { + #ifdef RARDLL - RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions(); + CommandData *Cmd=((Archive *)SrcFile)->GetCommandData(); if (Cmd->DllOpMode!=RAR_SKIP) { if (Cmd->Callback!=NULL && Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1) - ErrHandler.Exit(USER_BREAK); + ErrHandler.Exit(RARX_USERBREAK); if (Cmd->ProcessDataProc!=NULL) { -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _EBX=_ESP; -#endif - int RetCode=Cmd->ProcessDataProc(Addr,Count); -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _ESP=_EBX; -#endif + int RetCode=Cmd->ProcessDataProc(Addr,(int)Count); if (RetCode==0) - ErrHandler.Exit(USER_BREAK); + ErrHandler.Exit(RARX_USERBREAK); } } -#endif +#endif // RARDLL + UnpWrAddr=Addr; UnpWrSize=Count; if (UnpackToMemory) @@ -150,12 +186,7 @@ void ComprDataIO::UnpWrite(byte *Addr,uint Count) DestFile->Write(Addr,Count); CurUnpWrite+=Count; if (!SkipUnpCRC) -#ifndef SFX_MODULE - if (((Archive *)SrcFile)->OldFormat) - UnpFileCRC=OldCRC((ushort)UnpFileCRC,Addr,Count); - else -#endif - UnpFileCRC=CRC(UnpFileCRC,Addr,Count); + UnpHash.Update(Addr,Count); ShowUnpWrite(); Wait(); } @@ -165,24 +196,20 @@ void ComprDataIO::UnpWrite(byte *Addr,uint Count) -void ComprDataIO::ShowUnpRead(Int64 ArcPos,Int64 ArcSize) +void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize) { if (ShowProgress && SrcFile!=NULL) { - if (TotalArcSize!=0) - { - // important when processing several archives or multivolume archive - ArcSize=TotalArcSize; - ArcPos+=ProcessedArcSize; - } + // Important when processing several archives or multivolume archive. + ArcPos+=ProcessedArcSize; Archive *SrcArc=(Archive *)SrcFile; - RAROptions *Cmd=SrcArc->GetRAROptions(); + CommandData *Cmd=SrcArc->GetCommandData(); int CurPercent=ToPercent(ArcPos,ArcSize); if (!Cmd->DisablePercentage && CurPercent!=LastPercent) { - mprintf("\b\b\b\b%3d%%",CurPercent); + uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize); LastPercent=CurPercent; } } @@ -200,6 +227,8 @@ void ComprDataIO::ShowUnpWrite() + + void ComprDataIO::SetFiles(File *SrcFile,File *DestFile) { if (SrcFile!=NULL) @@ -210,46 +239,40 @@ void ComprDataIO::SetFiles(File *SrcFile,File *DestFile) } -void ComprDataIO::GetUnpackedData(byte **Data,uint *Size) +void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size) { *Data=UnpWrAddr; *Size=UnpWrSize; } -void ComprDataIO::SetEncryption(int Method,char *Password,byte *Salt,bool Encrypt,bool HandsOffHash) +void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method, + SecPassword *Password,const byte *Salt,const byte *InitV, + uint Lg2Cnt,byte *HashKey,byte *PswCheck) { +#ifndef RAR_NOCRYPT if (Encrypt) - { - Encryption=*Password ? Method:0; -#ifndef NOCRYPT - Crypt.SetCryptKeys(Password,Salt,Encrypt,false,HandsOffHash); -#endif - } + Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); else - { - Decryption=*Password ? Method:0; -#ifndef NOCRYPT - Decrypt.SetCryptKeys(Password,Salt,Encrypt,Method<29,HandsOffHash); + Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck); #endif - } } -#if !defined(SFX_MODULE) && !defined(NOCRYPT) +#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) void ComprDataIO::SetAV15Encryption() { - Decryption=15; - Decrypt.SetAV15Encryption(); + Decryption=true; + Decrypt->SetAV15Encryption(); } #endif -#if !defined(SFX_MODULE) && !defined(NOCRYPT) +#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT) void ComprDataIO::SetCmt13Encryption() { - Decryption=13; - Decrypt.SetCmt13Encryption(); + Decryption=true; + Decrypt->SetCmt13Encryption(); } #endif @@ -264,3 +287,36 @@ void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size) } +// Extraction progress is based on the position in archive and we adjust +// the total archives size here, so trailing blocks do not prevent progress +// reaching 100% at the end of extraction. Alternatively we could print "100%" +// after completing the entire archive extraction, but then we would need +// to take into account possible messages like the checksum error after +// last file percent progress. +void ComprDataIO::AdjustTotalArcSize(Archive *Arc) +{ + // If we know a position of QO or RR blocks, use them to adjust the total + // packed size to beginning of these blocks. Earlier we already calculated + // the total size based on entire archive sizes. We also set LastArcSize + // to start of first trailing block, to add it later to ProcessedArcSize. + int64 ArcLength=Arc->IsSeekable() ? Arc->FileLength() : 0; + if (Arc->MainHead.QOpenOffset!=0) // QO is always preceding RR record. + LastArcSize=Arc->MainHead.QOpenOffset; + else + if (Arc->MainHead.RROffset!=0) + LastArcSize=Arc->MainHead.RROffset; + else + { + // If neither QO nor RR are found, exclude the approximate size of + // end of archive block. + // We select EndBlock to be larger than typical 8 bytes HEAD_ENDARC, + // but to not exceed the smallest 22 bytes HEAD_FILE with 1 byte file + // name, so we do not have two files with 100% at the end of archive. + const uint EndBlock=23; + + if (ArcLength>EndBlock) + LastArcSize=ArcLength-EndBlock; + } + + TotalArcSize-=ArcLength-LastArcSize; +} diff --git a/unrar/unrar/rdwrfn.hpp b/unrar/unrar/rdwrfn.hpp index b80ae28..3060a0f 100644 --- a/unrar/unrar/rdwrfn.hpp +++ b/unrar/unrar/rdwrfn.hpp @@ -1,33 +1,41 @@ #ifndef _RAR_DATAIO_ #define _RAR_DATAIO_ +class Archive; class CmdAdd; class Unpack; +class ArcFileSearch; +#if 0 +// We use external i/o calls for Benchmark command. +#define COMPRDATAIO_EXTIO +#endif class ComprDataIO { private: - void ShowUnpRead(Int64 ArcPos,Int64 ArcSize); + void ShowUnpRead(int64 ArcPos,int64 ArcSize); void ShowUnpWrite(); bool UnpackFromMemory; - uint UnpackFromMemorySize; + size_t UnpackFromMemorySize; byte *UnpackFromMemoryAddr; bool UnpackToMemory; - uint UnpackToMemorySize; + size_t UnpackToMemorySize; byte *UnpackToMemoryAddr; - uint UnpWrSize; + size_t UnpWrSize; byte *UnpWrAddr; - Int64 UnpPackedSize; + int64 UnpPackedSize; + int64 UnpPackedLeft; bool ShowProgress; bool TestMode; bool SkipUnpCRC; + bool NoFileHeader; File *SrcFile; File *DestFile; @@ -35,54 +43,63 @@ class ComprDataIO CmdAdd *Command; FileHeader *SubHead; - Int64 *SubHeadPos; + int64 *SubHeadPos; -#ifndef NOCRYPT - CryptData Crypt; - CryptData Decrypt; +#ifndef RAR_NOCRYPT + CryptData *Crypt; + CryptData *Decrypt; #endif int LastPercent; - char CurrentCommand; + wchar CurrentCommand; public: ComprDataIO(); + ~ComprDataIO(); void Init(); - int UnpRead(byte *Addr,uint Count); - void UnpWrite(byte *Addr,uint Count); + int UnpRead(byte *Addr,size_t Count); + void UnpWrite(byte *Addr,size_t Count); void EnableShowProgress(bool Show) {ShowProgress=Show;} - void GetUnpackedData(byte **Data,uint *Size); - void SetPackedSizeToRead(Int64 Size) {UnpPackedSize=Size;} + void GetUnpackedData(byte **Data,size_t *Size); + void SetPackedSizeToRead(int64 Size) {UnpPackedSize=UnpPackedLeft=Size;} void SetTestMode(bool Mode) {TestMode=Mode;} void SetSkipUnpCRC(bool Skip) {SkipUnpCRC=Skip;} + void SetNoFileHeader(bool Mode) {NoFileHeader=Mode;} void SetFiles(File *SrcFile,File *DestFile); void SetCommand(CmdAdd *Cmd) {Command=Cmd;} - void SetSubHeader(FileHeader *hd,Int64 *Pos) {SubHead=hd;SubHeadPos=Pos;} - void SetEncryption(int Method,char *Password,byte *Salt,bool Encrypt,bool HandsOffHash); + void SetSubHeader(FileHeader *hd,int64 *Pos) {SubHead=hd;SubHeadPos=Pos;} + void SetEncryption(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, + const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); void SetAV15Encryption(); void SetCmt13Encryption(); void SetUnpackToMemory(byte *Addr,uint Size); - void SetCurrentCommand(char Cmd) {CurrentCommand=Cmd;} + void SetCurrentCommand(wchar Cmd) {CurrentCommand=Cmd;} + void AdjustTotalArcSize(Archive *Arc); + bool PackVolume; bool UnpVolume; bool NextVolumeMissing; - Int64 TotalPackRead; - Int64 UnpArcSize; - Int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite; + int64 CurPackRead,CurPackWrite,CurUnpRead,CurUnpWrite; + // Size of already processed archives. // Used to calculate the total operation progress. - Int64 ProcessedArcSize; + int64 ProcessedArcSize; + + // Last extracted archive size up to QO or RR block. + int64 LastArcSize; - Int64 TotalArcSize; + int64 TotalArcSize; - uint PackFileCRC,UnpFileCRC,PackedCRC; + DataHash PackedDataHash; // Packed write and unpack read hash. + DataHash PackHash; // Pack read hash. + DataHash UnpHash; // Unpack write hash. - int Encryption; - int Decryption; + bool Encryption; + bool Decryption; }; #endif diff --git a/unrar/unrar/readme.txt b/unrar/unrar/readme.txt index 41ee05c..a1f820a 100644 --- a/unrar/unrar/readme.txt +++ b/unrar/unrar/readme.txt @@ -4,12 +4,13 @@ 1. General - This package includes freeware Unrar C++ source and a few makefiles - (makefile.bcc, makefile.msc+msc.dep, makefile.unix). Unrar source - is subset of RAR and generated from RAR source automatically, + This package includes freeware Unrar C++ source and makefile for + several Unix compilers. + + Unrar source is subset of RAR and generated from RAR source automatically, by a small program removing blocks like '#ifndef UNRAR ... #endif'. - Such method is not perfect and you may find some RAR related - stuff unnecessary in Unrar, especially in header files. + Such method is not perfect and you may find some RAR related stuff + unnecessary in Unrar, especially in header files. If you wish to port Unrar to a new platform, you may need to edit '#define LITTLE_ENDIAN' in os.hpp and data type definitions @@ -17,16 +18,10 @@ if computer architecture does not allow not aligned data access, you need to undefine ALLOW_NOT_ALIGNED_INT and define - STRICT_ALIGNMENT_REQUIRED in os.h. Note that it will increase memory - requirements. - - If you use Borland C++ makefile (makefile.bcc), you need to define - BASEPATHCC environment (or makefile) variable containing - the path to Borland C++ installation. + STRICT_ALIGNMENT_REQUIRED in os.h. - Makefile.unix contains numerous compiler option sets. - GCC Linux is selected by default. If you need to compile Unrar - for other platforms, uncomment corresponding lines. + UnRAR.vcproj and UnRARDll.vcproj are projects for Microsoft Visual C++. + UnRARDll.vcproj lets to build unrar.dll library. 2. Unrar binaries @@ -38,16 +33,8 @@ 3. Acknowledgements - This source includes parts of code written by the following authors: - - Dmitry Shkarin PPMII v.H text compression - Dmitry Subbotin Carryless rangecoder - Szymon Stefanek AES encryption - Brian Gladman AES encryption - Steve Reid SHA-1 hash function - Marcus Herbert makefile.unix file - Tomasz Klim fixes for libunrar.so - Robert Riebisch makefile.dj and patches for DJGPP + This source includes parts of code written by other authors. + Please see acknow.txt file for details. 4. Legal stuff diff --git a/unrar/unrar/recvol.cpp b/unrar/unrar/recvol.cpp index 6d6f6af..b178207 100644 --- a/unrar/unrar/recvol.cpp +++ b/unrar/unrar/recvol.cpp @@ -1,374 +1,111 @@ #include "rar.hpp" -#define RECVOL_BUFSIZE 0x800 - -RecVolumes::RecVolumes() -{ - Buf.Alloc(RECVOL_BUFSIZE*256); - memset(SrcFile,0,sizeof(SrcFile)); -} - - -RecVolumes::~RecVolumes() -{ - for (int I=0;I<sizeof(SrcFile)/sizeof(SrcFile[0]);I++) - delete SrcFile[I]; -} - +#include "recvol3.cpp" +#include "recvol5.cpp" -bool RecVolumes::Restore(RAROptions *Cmd,const char *Name, - const wchar *NameW,bool Silent) +bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent) { - char ArcName[NM]; - wchar ArcNameW[NM]; - strcpy(ArcName,Name); - strcpyw(ArcNameW,NameW); - char *Ext=GetExt(ArcName); - bool NewStyle=false; - bool RevName=Ext!=NULL && stricomp(Ext,".rev")==0; - if (RevName) + Archive Arc(Cmd); + if (!Arc.Open(Name)) { - for (int DigitGroup=0;Ext>ArcName && DigitGroup<3;Ext--) - if (!isdigit(*Ext)) - if (isdigit(*(Ext-1)) && (*Ext=='_' || DigitGroup<2)) - DigitGroup++; - else - if (DigitGroup<2) - { - NewStyle=true; - break; - } - while (isdigit(*Ext) && Ext>ArcName+1) - Ext--; - strcpy(Ext,"*.*"); - FindFile Find; - Find.SetMask(ArcName); - struct FindData FD; - while (Find.Next(&FD)) - { - Archive Arc(Cmd); - if (Arc.WOpen(FD.Name,FD.NameW) && Arc.IsArchive(true)) - { - strcpy(ArcName,FD.Name); - *ArcNameW=0; - break; - } - } + if (!Silent) + ErrHandler.OpenErrorMsg(Name); + return false; } - Archive Arc(Cmd); - if (!Arc.WCheckOpen(ArcName,ArcNameW)) - return(false); - if (!Arc.Volume) + RARFORMAT Fmt=RARFMT15; + if (Arc.IsArchive(true)) + Fmt=Arc.Format; + else { -#ifndef SILENT - Log(ArcName,St(MNotVolume),ArcName); -#endif - return(false); + byte Sign[REV5_SIGN_SIZE]; + Arc.Seek(0,SEEK_SET); + if (Arc.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0) + Fmt=RARFMT50; } - bool NewNumbering=(Arc.NewMhd.Flags & MHD_NEWNUMBERING); Arc.Close(); - char *VolNumStart=VolNameToFirstName(ArcName,ArcName,NewNumbering); - char RecVolMask[NM]; - strcpy(RecVolMask,ArcName); - int BaseNamePartLength=VolNumStart-ArcName; - strcpy(RecVolMask+BaseNamePartLength,"*.rev"); - -#ifndef SILENT - Int64 RecFileSize=0; -#endif - -#ifndef SILENT - mprintf(St(MCalcCRCAllVol)); -#endif - FindFile Find; - Find.SetMask(RecVolMask); - struct FindData RecData; - int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0; - char PrevName[NM]; - while (Find.Next(&RecData)) + // We define RecVol as local variable for proper stack unwinding when + // handling exceptions. So it can close and delete files on Cancel. + if (Fmt==RARFMT15) { - char *Name=RecData.Name; - int P[3]; - if (!RevName && !NewStyle) - { - NewStyle=true; - char *Dot=GetExt(Name); - if (Dot!=NULL) - { - int LineCount=0; - Dot--; - while (Dot>Name && *Dot!='.') - { - if (*Dot=='_') - LineCount++; - Dot--; - } - if (LineCount==2) - NewStyle=false; - } - } - if (NewStyle) - { - File CurFile; - CurFile.TOpen(Name); - CurFile.Seek(0,SEEK_END); - Int64 Length=CurFile.Tell(); - CurFile.Seek(Length-7,SEEK_SET); - for (int I=0;I<3;I++) - P[2-I]=CurFile.GetByte()+1; - uint FileCRC=0; - for (int I=0;I<4;I++) - FileCRC|=CurFile.GetByte()<<(I*8); - if (FileCRC!=CalcFileCRC(&CurFile,Length-4)) - { -#ifndef SILENT - mprintf(St(MCRCFailed),Name); -#endif - continue; - } - } - else - { - char *Dot=GetExt(Name); - if (Dot==NULL) - continue; - bool WrongParam=false; - for (int I=0;I<sizeof(P)/sizeof(P[0]);I++) - { - do - { - Dot--; - } while (isdigit(*Dot) && Dot>=Name+BaseNamePartLength); - P[I]=atoi(Dot+1); - if (P[I]==0 || P[I]>255) - WrongParam=true; - } - if (WrongParam) - continue; - } - if (P[1]+P[2]>255) - continue; - if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2]) - { -#ifndef SILENT - Log(NULL,St(MRecVolDiffSets),Name,PrevName); -#endif - return(false); - } - RecVolNumber=P[1]; - FileNumber=P[2]; - strcpy(PrevName,Name); - File *NewFile=new File; - NewFile->TOpen(Name); - SrcFile[FileNumber+P[0]-1]=NewFile; - FoundRecVolumes++; -#ifndef SILENT - if (RecFileSize==0) - RecFileSize=NewFile->FileLength(); -#endif + RecVolumes3 RecVol(Cmd,false); + return RecVol.Restore(Cmd,Name,Silent); } -#ifndef SILENT - if (!Silent || FoundRecVolumes!=0) + else { - mprintf(St(MRecVolFound),FoundRecVolumes); + RecVolumes5 RecVol(Cmd,false); + return RecVol.Restore(Cmd,Name,Silent); } -#endif - if (FoundRecVolumes==0) - return(false); - - bool WriteFlags[256]; - memset(WriteFlags,0,sizeof(WriteFlags)); +} - char LastVolName[NM]; - *LastVolName=0; - for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++) +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name) +{ + wchar RevName[NM]; + *RevName=0; + if (Arc!=NULL) { - Archive *NewFile=new Archive; - bool ValidVolume=FileExist(ArcName); - if (ValidVolume) + // We received .rar or .exe volume as a parameter, trying to find + // the matching .rev file number 1. + bool NewNumbering=Arc->NewNumbering; + + wchar ArcName[NM]; + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + + wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); + wchar RecVolMask[NM]; + wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); + size_t BaseNamePartLength=VolNumStart-ArcName; + wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); + + FindFile Find; + Find.SetMask(RecVolMask); + FindData RecData; + + while (Find.Next(&RecData)) { - NewFile->TOpen(ArcName); - ValidVolume=NewFile->IsArchive(false); - if (ValidVolume) - { - while (NewFile->ReadHeader()!=0) + wchar *Num=GetVolNumPart(RecData.Name); + if (*Num!='1') // Name must have "0...01" numeric part. + continue; + bool FirstVol=true; + while (--Num>=RecData.Name && IsDigit(*Num)) + if (*Num!='0') { - if (NewFile->GetHeaderType()==ENDARC_HEAD) - { - if ((NewFile->EndArcHead.Flags&EARC_DATACRC)!=0 && - NewFile->EndArcHead.ArcDataCRC!=CalcFileCRC(NewFile,NewFile->CurBlockPos)) - { - ValidVolume=false; -#ifndef SILENT - mprintf(St(MCRCFailed),ArcName); -#endif - } - break; - } - NewFile->SeekToNext(); + FirstVol=false; + break; } - } - if (!ValidVolume) + if (FirstVol) { - NewFile->Close(); - char NewName[NM]; - strcpy(NewName,ArcName); - strcat(NewName,".bad"); -#ifndef SILENT - mprintf(St(MBadArc),ArcName); - mprintf(St(MRenaming),ArcName,NewName); -#endif - rename(ArcName,NewName); + wcsncpyz(RevName,RecData.Name,ASIZE(RevName)); + Name=RevName; + break; } - NewFile->Seek(0,SEEK_SET); } - if (!ValidVolume) - { - NewFile->TCreate(ArcName); - WriteFlags[CurArcNum]=true; - MissingVolumes++; - - if (CurArcNum==FileNumber-1) - strcpy(LastVolName,ArcName); - -#ifndef SILENT - mprintf(St(MAbsNextVol),ArcName); -#endif - } - SrcFile[CurArcNum]=(File*)NewFile; - NextVolumeName(ArcName,ArcNameW,ASIZE(ArcName),!NewNumbering); + if (*RevName==0) // First .rev file not found. + return; } - -#ifndef SILENT - mprintf(St(MRecVolMissing),MissingVolumes); -#endif - - if (MissingVolumes==0) + + File RevFile; + if (!RevFile.Open(Name)) { -#ifndef SILENT - mprintf(St(MRecVolAllExist)); -#endif - return(false); + ErrHandler.OpenErrorMsg(Name); // It also sets RARX_OPEN. + return; } - - if (MissingVolumes>FoundRecVolumes) + mprintf(L"\n"); + byte Sign[REV5_SIGN_SIZE]; + bool Rev5=RevFile.Read(Sign,REV5_SIGN_SIZE)==REV5_SIGN_SIZE && memcmp(Sign,REV5_SIGN,REV5_SIGN_SIZE)==0; + RevFile.Close(); + if (Rev5) { -#ifndef SILENT - mprintf(St(MRecVolCannotFix)); -#endif - return(false); + RecVolumes5 RecVol(Cmd,true); + RecVol.Test(Cmd,Name); } -#ifndef SILENT - mprintf(St(MReconstructing)); -#endif - - RSCoder RSC(RecVolNumber); - - int TotalFiles=FileNumber+RecVolNumber; - int Erasures[256],EraSize=0; - - for (int I=0;I<TotalFiles;I++) - if (WriteFlags[I] || SrcFile[I]==NULL) - Erasures[EraSize++]=I; - -#ifndef SILENT - Int64 ProcessedSize=0; - -/* Modified by Tomas Bzatek - bug in defines */ - int LastPercent=-1; -#ifndef GUI - mprintf(" "); -#endif -#endif - int RecCount=0; - - while (true) + else { - if ((++RecCount & 15)==0) - Wait(); - int MaxRead=0; - for (int I=0;I<TotalFiles;I++) - if (WriteFlags[I] || SrcFile[I]==NULL) - memset(&Buf[I*RECVOL_BUFSIZE],0,RECVOL_BUFSIZE); - else - { - int ReadSize=SrcFile[I]->Read(&Buf[I*RECVOL_BUFSIZE],RECVOL_BUFSIZE); - if (ReadSize!=RECVOL_BUFSIZE) - memset(&Buf[I*RECVOL_BUFSIZE+ReadSize],0,RECVOL_BUFSIZE-ReadSize); - if (ReadSize>MaxRead) - MaxRead=ReadSize; - } - if (MaxRead==0) - break; -#ifndef SILENT - int CurPercent=ToPercent(ProcessedSize,RecFileSize); - if (!Cmd->DisablePercentage && CurPercent!=LastPercent) - { - mprintf("\b\b\b\b%3d%%",CurPercent); - LastPercent=CurPercent; - } - ProcessedSize+=MaxRead; -#endif - for (int BufPos=0;BufPos<MaxRead;BufPos++) - { - byte Data[256]; - for (int I=0;I<TotalFiles;I++) - Data[I]=Buf[I*RECVOL_BUFSIZE+BufPos]; - RSC.Decode(Data,TotalFiles,Erasures,EraSize); - for (int I=0;I<EraSize;I++) - Buf[Erasures[I]*RECVOL_BUFSIZE+BufPos]=Data[Erasures[I]]; -/* - for (int I=0;I<FileNumber;I++) - Buf[I*RECVOL_BUFSIZE+BufPos]=Data[I]; -*/ - } - for (int I=0;I<FileNumber;I++) - if (WriteFlags[I]) - SrcFile[I]->Write(&Buf[I*RECVOL_BUFSIZE],MaxRead); - } - for (int I=0;I<RecVolNumber+FileNumber;I++) - if (SrcFile[I]!=NULL) - { - File *CurFile=SrcFile[I]; - if (NewStyle && WriteFlags[I]) - { - Int64 Length=CurFile->Tell(); - CurFile->Seek(Length-7,SEEK_SET); - for (int J=0;J<7;J++) - CurFile->PutByte(0); - } - CurFile->Close(); - SrcFile[I]=NULL; - } - if (*LastVolName) - { - Archive Arc(Cmd); - if (Arc.Open(LastVolName,NULL,false,true) && Arc.IsArchive(true) && - Arc.SearchBlock(ENDARC_HEAD)) - { - Arc.Seek(Arc.NextBlockPos,SEEK_SET); - char Buf[8192]; - int ReadSize=Arc.Read(Buf,sizeof(Buf)); - int ZeroCount=0; - while (ZeroCount<ReadSize && Buf[ZeroCount]==0) - ZeroCount++; - if (ZeroCount==ReadSize) - { - Arc.Seek(Arc.NextBlockPos,SEEK_SET); - Arc.Truncate(); - } - } + RecVolumes3 RecVol(Cmd,true); + RecVol.Test(Cmd,Name); } -#if !defined(GUI) && !defined(SILENT) - if (!Cmd->DisablePercentage) - mprintf("\b\b\b\b100%%"); - if (!Silent && !Cmd->DisableDone) - mprintf(St(MDone)); -#endif - return(true); } diff --git a/unrar/unrar/recvol.hpp b/unrar/unrar/recvol.hpp index 5a0abe5..4a6d663 100644 --- a/unrar/unrar/recvol.hpp +++ b/unrar/unrar/recvol.hpp @@ -1,16 +1,88 @@ #ifndef _RAR_RECVOL_ #define _RAR_RECVOL_ -class RecVolumes +#define REV5_SIGN "Rar!\x1aRev" +#define REV5_SIGN_SIZE 8 + +class RecVolumes3 { private: File *SrcFile[256]; Array<byte> Buf; + +#ifdef RAR_SMP + ThreadPool *RSThreadPool; +#endif + public: + RecVolumes3(CommandData *Cmd,bool TestOnly); + ~RecVolumes3(); + void Make(CommandData *Cmd,wchar *ArcName); + bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); + void Test(CommandData *Cmd,const wchar *Name); +}; + + +struct RecVolItem +{ + File *f; + wchar Name[NM]; + uint CRC; + uint64 FileSize; + bool New; // Newly created RAR volume. + bool Valid; // If existing RAR volume is valid. +}; + + +class RecVolumes5; +struct RecRSThreadData +{ + RecVolumes5 *RecRSPtr; + RSCoder16 *RS; + bool Encode; + uint DataNum; + const byte *Data; + size_t StartPos; + size_t Size; +}; + +class RecVolumes5 +{ + private: + void ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode); + void ProcessRS(CommandData *Cmd,uint MaxRead,bool Encode); + uint ReadHeader(File *RecFile,bool FirstRev); + + Array<RecVolItem> RecItems; + + byte *RealReadBuffer; // Real pointer returned by 'new'. + byte *ReadBuffer; // Pointer aligned for SSE instructions. + + byte *RealBuf; // Real pointer returned by 'new'. + byte *Buf; // Store ECC or recovered data here, aligned for SSE. + size_t RecBufferSize; // Buffer area allocated for single volume. + + uint DataCount; // Number of archives. + uint RecCount; // Number of recovery volumes. + uint TotalCount; // Total number of archives and recovery volumes. + + bool *ValidFlags; // Volume validity flags for recovering. + uint MissingVolumes; // Number of missing or bad RAR volumes. + +#ifdef RAR_SMP + ThreadPool *RecThreadPool; +#endif + uint MaxUserThreads; // Maximum number of threads defined by user. + RecRSThreadData *ThreadData; // Array to store thread parameters. + public: // 'public' only because called from thread functions. + void ProcessAreaRS(RecRSThreadData *td); public: - RecVolumes(); - ~RecVolumes(); - void Make(RAROptions *Cmd,char *ArcName,wchar *ArcNameW); - bool Restore(RAROptions *Cmd,const char *Name,const wchar *NameW,bool Silent); + RecVolumes5(CommandData *Cmd,bool TestOnly); + ~RecVolumes5(); + bool Restore(CommandData *Cmd,const wchar *Name,bool Silent); + void Test(CommandData *Cmd,const wchar *Name); }; +bool RecVolumesRestore(CommandData *Cmd,const wchar *Name,bool Silent); +void RecVolumesTest(CommandData *Cmd,Archive *Arc,const wchar *Name); + #endif diff --git a/unrar/unrar/recvol3.cpp b/unrar/unrar/recvol3.cpp new file mode 100644 index 0000000..0138d0f --- /dev/null +++ b/unrar/unrar/recvol3.cpp @@ -0,0 +1,551 @@ +// Buffer size for all volumes involved. +static const size_t TotalBufferSize=0x4000000; + +class RSEncode // Encode or decode data area, one object per one thread. +{ + private: + RSCoder RSC; + public: + void EncodeBuf(); + void DecodeBuf(); + + void Init(int RecVolNumber) {RSC.Init(RecVolNumber);} + byte *Buf; + byte *OutBuf; + int BufStart; + int BufEnd; + int FileNumber; + int RecVolNumber; + size_t RecBufferSize; + int *Erasures; + int EraSize; +}; + + +#ifdef RAR_SMP +THREAD_PROC(RSEncodeThread) +{ + RSEncode *rs=(RSEncode *)Data; + rs->EncodeBuf(); +} + +THREAD_PROC(RSDecodeThread) +{ + RSEncode *rs=(RSEncode *)Data; + rs->DecodeBuf(); +} +#endif + +RecVolumes3::RecVolumes3(CommandData *Cmd,bool TestOnly) +{ + memset(SrcFile,0,sizeof(SrcFile)); + if (TestOnly) + { +#ifdef RAR_SMP + RSThreadPool=NULL; +#endif + } + else + { + Buf.Alloc(TotalBufferSize); + memset(SrcFile,0,sizeof(SrcFile)); +#ifdef RAR_SMP + RSThreadPool=new ThreadPool(Cmd->Threads); +#endif + } +} + + +RecVolumes3::~RecVolumes3() +{ + for (size_t I=0;I<ASIZE(SrcFile);I++) + delete SrcFile[I]; +#ifdef RAR_SMP + delete RSThreadPool; +#endif +} + + + + +void RSEncode::EncodeBuf() +{ + for (int BufPos=BufStart;BufPos<BufEnd;BufPos++) + { + byte Data[256],Code[256]; + for (int I=0;I<FileNumber;I++) + Data[I]=Buf[I*RecBufferSize+BufPos]; + RSC.Encode(Data,FileNumber,Code); + for (int I=0;I<RecVolNumber;I++) + OutBuf[I*RecBufferSize+BufPos]=Code[I]; + } +} + + +// Check for names like arc5_3_1.rev created by RAR 3.0. +static bool IsNewStyleRev(const wchar *Name) +{ + wchar *Ext=GetExt(Name); + if (Ext==NULL) + return true; + int DigitGroup=0; + for (Ext--;Ext>Name;Ext--) + if (!IsDigit(*Ext)) + if (*Ext=='_' && IsDigit(*(Ext-1))) + DigitGroup++; + else + break; + return DigitGroup<2; +} + + +bool RecVolumes3::Restore(CommandData *Cmd,const wchar *Name,bool Silent) +{ + wchar ArcName[NM]; + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + wchar *Ext=GetExt(ArcName); + bool NewStyle=false; // New style .rev volumes are supported since RAR 3.10. + bool RevName=Ext!=NULL && wcsicomp(Ext,L".rev")==0; + if (RevName) + { + NewStyle=IsNewStyleRev(ArcName); + while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_')) + Ext--; + wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName)); + + FindFile Find; + Find.SetMask(ArcName); + FindData fd; + while (Find.Next(&fd)) + { + Archive Arc(Cmd); + if (Arc.WOpen(fd.Name) && Arc.IsArchive(true)) + { + wcsncpyz(ArcName,fd.Name,ASIZE(ArcName)); + break; + } + } + } + + Archive Arc(Cmd); + if (!Arc.WCheckOpen(ArcName)) + return false; + if (!Arc.Volume) + { + uiMsg(UIERROR_NOTVOLUME,ArcName); + return false; + } + bool NewNumbering=Arc.NewNumbering; + Arc.Close(); + + wchar *VolNumStart=VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),NewNumbering); + wchar RecVolMask[NM]; + wcsncpyz(RecVolMask,ArcName,ASIZE(RecVolMask)); + size_t BaseNamePartLength=VolNumStart-ArcName; + wcsncpyz(RecVolMask+BaseNamePartLength,L"*.rev",ASIZE(RecVolMask)-BaseNamePartLength); + + int64 RecFileSize=0; + + // We cannot display "Calculating CRC..." message here, because we do not + // know if we'll find any recovery volumes. We'll display it after finding + // the first recovery volume. + bool CalcCRCMessageDone=false; + + FindFile Find; + Find.SetMask(RecVolMask); + FindData RecData; + int FileNumber=0,RecVolNumber=0,FoundRecVolumes=0,MissingVolumes=0; + wchar PrevName[NM]; + while (Find.Next(&RecData)) + { + wchar *CurName=RecData.Name; + int P[3]; + if (!RevName && !NewStyle) + { + NewStyle=true; + + wchar *Dot=GetExt(CurName); + if (Dot!=NULL) + { + int LineCount=0; + Dot--; + while (Dot>CurName && *Dot!='.') + { + if (*Dot=='_') + LineCount++; + Dot--; + } + if (LineCount==2) + NewStyle=false; + } + } + if (NewStyle) + { + if (!CalcCRCMessageDone) + { + uiMsg(UIMSG_RECVOLCALCCHECKSUM); + CalcCRCMessageDone=true; + } + + uiMsg(UIMSG_STRING,CurName); + + File CurFile; + CurFile.TOpen(CurName); + CurFile.Seek(0,SEEK_END); + int64 Length=CurFile.Tell(); + CurFile.Seek(Length-7,SEEK_SET); + for (int I=0;I<3;I++) + P[2-I]=CurFile.GetByte()+1; + uint FileCRC=0; + for (int I=0;I<4;I++) + FileCRC|=CurFile.GetByte()<<(I*8); + uint CalcCRC; + CalcFileSum(&CurFile,&CalcCRC,NULL,Cmd->Threads,Length-4); + if (FileCRC!=CalcCRC) + { + uiMsg(UIMSG_CHECKSUM,CurName); + continue; + } + } + else + { + wchar *Dot=GetExt(CurName); + if (Dot==NULL) + continue; + bool WrongParam=false; + for (size_t I=0;I<ASIZE(P);I++) + { + do + { + Dot--; + } while (IsDigit(*Dot) && Dot>=CurName+BaseNamePartLength); + P[I]=atoiw(Dot+1); + if (P[I]==0 || P[I]>255) + WrongParam=true; + } + if (WrongParam) + continue; + } + if (P[0]<=0 || P[1]<=0 || P[2]<=0 || P[1]+P[2]>255 || P[0]+P[2]-1>255) + continue; + if (RecVolNumber!=0 && RecVolNumber!=P[1] || FileNumber!=0 && FileNumber!=P[2]) + { + uiMsg(UIERROR_RECVOLDIFFSETS,CurName,PrevName); + return false; + } + RecVolNumber=P[1]; + FileNumber=P[2]; + wcsncpyz(PrevName,CurName,ASIZE(PrevName)); + File *NewFile=new File; + NewFile->TOpen(CurName); + + // This check is redundant taking into account P[I]>255 and P[0]+P[2]-1>255 + // checks above. Still we keep it here for better clarity and security. + int SrcPos=FileNumber+P[0]-1; + if (SrcPos<0 || SrcPos>=ASIZE(SrcFile)) + continue; + SrcFile[SrcPos]=NewFile; + + FoundRecVolumes++; + + if (RecFileSize==0) + RecFileSize=NewFile->FileLength(); + } + if (!Silent || FoundRecVolumes!=0) + uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); + if (FoundRecVolumes==0) + return false; + + bool WriteFlags[256]; + memset(WriteFlags,0,sizeof(WriteFlags)); + + wchar LastVolName[NM]; + *LastVolName=0; + + for (int CurArcNum=0;CurArcNum<FileNumber;CurArcNum++) + { + Archive *NewFile=new Archive(Cmd); + bool ValidVolume=FileExist(ArcName); + if (ValidVolume) + { + NewFile->TOpen(ArcName); + ValidVolume=NewFile->IsArchive(false); + if (ValidVolume) + { + while (NewFile->ReadHeader()!=0) + { + if (NewFile->GetHeaderType()==HEAD_ENDARC) + { + uiMsg(UIMSG_STRING,ArcName); + + if (NewFile->EndArcHead.DataCRC) + { + uint CalcCRC; + CalcFileSum(NewFile,&CalcCRC,NULL,Cmd->Threads,NewFile->CurBlockPos); + if (NewFile->EndArcHead.ArcDataCRC!=CalcCRC) + { + ValidVolume=false; + uiMsg(UIMSG_CHECKSUM,ArcName); + } + } + break; + } + NewFile->SeekToNext(); + } + } + if (!ValidVolume) + { + NewFile->Close(); + wchar NewName[NM]; + wcsncpyz(NewName,ArcName,ASIZE(NewName)); + wcsncatz(NewName,L".bad",ASIZE(NewName)); + + uiMsg(UIMSG_BADARCHIVE,ArcName); + uiMsg(UIMSG_RENAMING,ArcName,NewName); + RenameFile(ArcName,NewName); + } + NewFile->Seek(0,SEEK_SET); + } + if (!ValidVolume) + { + // It is important to return 'false' instead of aborting here, + // so if we are called from extraction, we will be able to continue + // extracting. It may happen if .rar and .rev are on read-only disks + // like CDs. + if (!NewFile->Create(ArcName,FMF_WRITE|FMF_SHAREREAD)) + { + // We need to display the title of operation before the error message, + // to make clear for user that create error is related to recovery + // volumes. This is why we cannot use WCreate call here. Title must be + // before create error, not after that. + + uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. + uiMsg(UIERROR_RECONSTRUCTING); + ErrHandler.CreateErrorMsg(ArcName); + return false; + } + + WriteFlags[CurArcNum]=true; + MissingVolumes++; + + if (CurArcNum==FileNumber-1) + wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName)); + + uiMsg(UIMSG_MISSINGVOL,ArcName); + uiMsg(UIEVENT_NEWARCHIVE,ArcName); + } + SrcFile[CurArcNum]=(File*)NewFile; + NextVolumeName(ArcName,ASIZE(ArcName),!NewNumbering); + } + + uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); + + if (MissingVolumes==0) + { + uiMsg(UIERROR_RECVOLALLEXIST); + return false; + } + + if (MissingVolumes>FoundRecVolumes) + { + uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. + uiMsg(UIERROR_RECVOLCANNOTFIX); + return false; + } + + uiMsg(UIMSG_RECONSTRUCTING); + + int TotalFiles=FileNumber+RecVolNumber; + int Erasures[256],EraSize=0; + + for (int I=0;I<TotalFiles;I++) + if (WriteFlags[I] || SrcFile[I]==NULL) + Erasures[EraSize++]=I; + + int64 ProcessedSize=0; + int LastPercent=-1; + mprintf(L" "); + // Size of per file buffer. + size_t RecBufferSize=TotalBufferSize/TotalFiles; + +#ifdef RAR_SMP + uint ThreadNumber=Cmd->Threads; +#else + uint ThreadNumber=1; +#endif + RSEncode *rse=new RSEncode[ThreadNumber]; + for (uint I=0;I<ThreadNumber;I++) + rse[I].Init(RecVolNumber); + + while (true) + { + Wait(); + int MaxRead=0; + for (int I=0;I<TotalFiles;I++) + if (WriteFlags[I] || SrcFile[I]==NULL) + memset(&Buf[I*RecBufferSize],0,RecBufferSize); + else + { + int ReadSize=SrcFile[I]->Read(&Buf[I*RecBufferSize],RecBufferSize); + if ((size_t)ReadSize!=RecBufferSize) + memset(&Buf[I*RecBufferSize+ReadSize],0,RecBufferSize-ReadSize); + if (ReadSize>MaxRead) + MaxRead=ReadSize; + } + if (MaxRead==0) + break; + + int CurPercent=ToPercent(ProcessedSize,RecFileSize); + if (!Cmd->DisablePercentage && CurPercent!=LastPercent) + { + uiProcessProgress("RC",ProcessedSize,RecFileSize); + LastPercent=CurPercent; + } + ProcessedSize+=MaxRead; + + int BlockStart=0; + int BlockSize=MaxRead/ThreadNumber; + if (BlockSize<0x100) + BlockSize=MaxRead; + + for (uint CurThread=0;BlockStart<MaxRead;CurThread++) + { + // Last thread processes all left data including increasement + // from rounding error. + if (CurThread==ThreadNumber-1) + BlockSize=MaxRead-BlockStart; + + RSEncode *curenc=rse+CurThread; + curenc->Buf=&Buf[0]; + curenc->BufStart=BlockStart; + curenc->BufEnd=BlockStart+BlockSize; + curenc->FileNumber=TotalFiles; + curenc->RecBufferSize=RecBufferSize; + curenc->Erasures=Erasures; + curenc->EraSize=EraSize; + +#ifdef RAR_SMP + if (ThreadNumber>1) + RSThreadPool->AddTask(RSDecodeThread,(void*)curenc); + else + curenc->DecodeBuf(); +#else + curenc->DecodeBuf(); +#endif + + BlockStart+=BlockSize; + } + +#ifdef RAR_SMP + RSThreadPool->WaitDone(); +#endif // RAR_SMP + + for (int I=0;I<FileNumber;I++) + if (WriteFlags[I]) + SrcFile[I]->Write(&Buf[I*RecBufferSize],MaxRead); + } + delete[] rse; + + for (int I=0;I<RecVolNumber+FileNumber;I++) + if (SrcFile[I]!=NULL) + { + File *CurFile=SrcFile[I]; + if (NewStyle && WriteFlags[I]) + { + int64 Length=CurFile->Tell(); + CurFile->Seek(Length-7,SEEK_SET); + for (int J=0;J<7;J++) + CurFile->PutByte(0); + } + CurFile->Close(); + SrcFile[I]=NULL; + } + if (*LastVolName!=0) + { + // Truncate the last volume to its real size. + Archive Arc(Cmd); + if (Arc.Open(LastVolName,FMF_UPDATE) && Arc.IsArchive(true) && + Arc.SearchBlock(HEAD_ENDARC)) + { + Arc.Seek(Arc.NextBlockPos,SEEK_SET); + char Buf[8192]; + int ReadSize=Arc.Read(Buf,sizeof(Buf)); + int ZeroCount=0; + while (ZeroCount<ReadSize && Buf[ZeroCount]==0) + ZeroCount++; + if (ZeroCount==ReadSize) + { + Arc.Seek(Arc.NextBlockPos,SEEK_SET); + Arc.Truncate(); + } + } + } +#if !defined(SILENT) + if (!Cmd->DisablePercentage) + mprintf(L"\b\b\b\b100%%"); + if (!Silent && !Cmd->DisableDone) + mprintf(St(MDone)); +#endif + return true; +} + + +void RSEncode::DecodeBuf() +{ + for (int BufPos=BufStart;BufPos<BufEnd;BufPos++) + { + byte Data[256]; + for (int I=0;I<FileNumber;I++) + Data[I]=Buf[I*RecBufferSize+BufPos]; + RSC.Decode(Data,FileNumber,Erasures,EraSize); + for (int I=0;I<EraSize;I++) + Buf[Erasures[I]*RecBufferSize+BufPos]=Data[Erasures[I]]; + } +} + + +void RecVolumes3::Test(CommandData *Cmd,const wchar *Name) +{ + if (!IsNewStyleRev(Name)) // RAR 3.0 name#_#_#.rev do not include CRC32. + { + ErrHandler.UnknownMethodMsg(Name,Name); + return; + } + + wchar VolName[NM]; + wcsncpyz(VolName,Name,ASIZE(VolName)); + + while (FileExist(VolName)) + { + File CurFile; + if (!CurFile.Open(VolName)) + { + ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN. + continue; + } + if (!uiStartFileExtract(VolName,false,true,false)) + return; + mprintf(St(MExtrTestFile),VolName); + mprintf(L" "); + CurFile.Seek(0,SEEK_END); + int64 Length=CurFile.Tell(); + CurFile.Seek(Length-4,SEEK_SET); + uint FileCRC=0; + for (int I=0;I<4;I++) + FileCRC|=CurFile.GetByte()<<(I*8); + + uint CalcCRC; + CalcFileSum(&CurFile,&CalcCRC,NULL,1,Length-4,Cmd->DisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS); + if (FileCRC==CalcCRC) + { + mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk)); + } + else + { + uiMsg(UIERROR_CHECKSUM,VolName,VolName); + ErrHandler.SetErrorCode(RARX_CRC); + } + + NextVolumeName(VolName,ASIZE(VolName),false); + } +} diff --git a/unrar/unrar/recvol5.cpp b/unrar/unrar/recvol5.cpp new file mode 100644 index 0000000..2d9c947 --- /dev/null +++ b/unrar/unrar/recvol5.cpp @@ -0,0 +1,538 @@ +static const uint MaxVolumes=65535; + +// We select this limit arbitrarily, to prevent user creating too many +// rev files by mistake. +#define MAX_REV_TO_DATA_RATIO 10 // 1000% of rev files. + +RecVolumes5::RecVolumes5(CommandData *Cmd,bool TestOnly) +{ + RealBuf=NULL; + RealReadBuffer=NULL; + + DataCount=0; + RecCount=0; + TotalCount=0; + RecBufferSize=0; + +#ifdef RAR_SMP + MaxUserThreads=Cmd->Threads; +#else + MaxUserThreads=1; +#endif + + ThreadData=new RecRSThreadData[MaxUserThreads]; + for (uint I=0;I<MaxUserThreads;I++) + { + ThreadData[I].RecRSPtr=this; + ThreadData[I].RS=NULL; + } + + if (TestOnly) + { +#ifdef RAR_SMP + RecThreadPool=NULL; +#endif + } + else + { +#ifdef RAR_SMP + RecThreadPool=new ThreadPool(MaxUserThreads); +#endif + RealBuf=new byte[TotalBufferSize+SSE_ALIGNMENT]; + Buf=(byte *)ALIGN_VALUE(RealBuf,SSE_ALIGNMENT); + } +} + + +RecVolumes5::~RecVolumes5() +{ + delete[] RealBuf; + delete[] RealReadBuffer; + for (uint I=0;I<RecItems.Size();I++) + delete RecItems[I].f; + for (uint I=0;I<MaxUserThreads;I++) + delete ThreadData[I].RS; + delete[] ThreadData; +#ifdef RAR_SMP + delete RecThreadPool; +#endif +} + + + + +#ifdef RAR_SMP +THREAD_PROC(RecThreadRS) +{ + RecRSThreadData *td=(RecRSThreadData *)Data; + td->RecRSPtr->ProcessAreaRS(td); +} +#endif + + +void RecVolumes5::ProcessRS(CommandData *Cmd,uint DataNum,const byte *Data,uint MaxRead,bool Encode) +{ +/* + RSCoder16 RS; + RS.Init(DataCount,RecCount,Encode ? NULL:ValidFlags); + uint Count=Encode ? RecCount : MissingVolumes; + for (uint I=0;I<Count;I++) + RS.UpdateECC(DataNum, I, Data, Buf+I*RecBufferSize, MaxRead); +*/ + + uint ThreadNumber=MaxUserThreads; + + const uint MinThreadBlock=0x1000; + ThreadNumber=Min(ThreadNumber,MaxRead/MinThreadBlock); + + if (ThreadNumber<1) + ThreadNumber=1; + uint ThreadDataSize=MaxRead/ThreadNumber; + ThreadDataSize+=(ThreadDataSize&1); // Must be even for 16-bit RS coder. +#ifdef USE_SSE + ThreadDataSize=ALIGN_VALUE(ThreadDataSize,SSE_ALIGNMENT); // Alignment for SSE operations. +#endif + if (ThreadDataSize<MinThreadBlock) + ThreadDataSize=MinThreadBlock; + + for (size_t I=0,CurPos=0;I<ThreadNumber && CurPos<MaxRead;I++) + { + RecRSThreadData *td=ThreadData+I; + if (td->RS==NULL) + { + td->RS=new RSCoder16; + td->RS->Init(DataCount,RecCount,Encode ? NULL:ValidFlags); + } + td->DataNum=DataNum; + td->Data=Data; + td->Encode=Encode; + td->StartPos=CurPos; + + size_t EndPos=CurPos+ThreadDataSize; + if (EndPos>MaxRead || I==ThreadNumber-1) + EndPos=MaxRead; + + td->Size=EndPos-CurPos; + + CurPos=EndPos; + +#ifdef RAR_SMP + if (ThreadNumber>1) + RecThreadPool->AddTask(RecThreadRS,(void*)td); + else + ProcessAreaRS(td); +#else + ProcessAreaRS(td); +#endif + } +#ifdef RAR_SMP + RecThreadPool->WaitDone(); +#endif // RAR_SMP +} + + +void RecVolumes5::ProcessAreaRS(RecRSThreadData *td) +{ + uint Count=td->Encode ? RecCount : MissingVolumes; + for (uint I=0;I<Count;I++) + td->RS->UpdateECC(td->DataNum, I, td->Data+td->StartPos, Buf+I*RecBufferSize+td->StartPos, td->Size); +} + + + + +bool RecVolumes5::Restore(CommandData *Cmd,const wchar *Name,bool Silent) +{ + wchar ArcName[NM]; + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + + wchar *Num=GetVolNumPart(ArcName); + while (Num>ArcName && IsDigit(*(Num-1))) + Num--; + if (Num<=PointToName(ArcName)) + return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume. + wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName)); + + wchar FirstVolName[NM]; + *FirstVolName=0; + + wchar LongestRevName[NM]; + *LongestRevName=0; + + int64 RecFileSize=0; + + FindFile VolFind; + VolFind.SetMask(ArcName); + FindData fd; + uint FoundRecVolumes=0; + while (VolFind.Next(&fd)) + { + Wait(); + + Archive *Vol=new Archive(Cmd); + int ItemPos=-1; + if (!fd.IsDir && Vol->WOpen(fd.Name)) + { + if (CmpExt(fd.Name,L"rev")) + { + uint RecNum=ReadHeader(Vol,FoundRecVolumes==0); + if (RecNum!=0) + { + if (FoundRecVolumes==0) + RecFileSize=Vol->FileLength(); + + ItemPos=RecNum; + FoundRecVolumes++; + + if (wcslen(fd.Name)>wcslen(LongestRevName)) + wcsncpyz(LongestRevName,fd.Name,ASIZE(LongestRevName)); + } + } + else + if (Vol->IsArchive(true) && (Vol->SFXSize>0 || CmpExt(fd.Name,L"rar"))) + { + if (!Vol->Volume && !Vol->BrokenHeader) + { + uiMsg(UIERROR_NOTVOLUME,ArcName); + return false; + } + // We work with archive as with raw data file, so we do not want + // to spend time to QOpen I/O redirection. + Vol->QOpenUnload(); + + Vol->Seek(0,SEEK_SET); + + // RAR volume found. Get its number, store the handle in appropriate + // array slot, clean slots in between if we had to grow the array. + wchar *Num=GetVolNumPart(fd.Name); + uint VolNum=0; + for (uint K=1;Num>=fd.Name && IsDigit(*Num);K*=10,Num--) + VolNum+=(*Num-'0')*K; + if (VolNum==0 || VolNum>MaxVolumes) + continue; + size_t CurSize=RecItems.Size(); + if (VolNum>CurSize) + { + RecItems.Alloc(VolNum); + for (size_t I=CurSize;I<VolNum;I++) + RecItems[I].f=NULL; + } + ItemPos=VolNum-1; + + if (*FirstVolName==0) + VolNameToFirstName(fd.Name,FirstVolName,ASIZE(FirstVolName),true); + } + } + if (ItemPos==-1) + delete Vol; // Skip found file, it is not RAR or REV volume. + else + if ((uint)ItemPos<RecItems.Size()) // Check if found more REV than needed. + { + // Store found RAR or REV volume. + RecVolItem *Item=RecItems+ItemPos; + Item->f=Vol; + Item->New=false; + wcsncpyz(Item->Name,fd.Name,ASIZE(Item->Name)); + } + } + + if (!Silent || FoundRecVolumes!=0) + uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes); + if (FoundRecVolumes==0) + return false; + + // If we did not find even a single .rar volume, create .rar volume name + // based on the longest .rev file name. Use longest .rev, so we have + // enough space for volume number. + if (*FirstVolName==0) + { + SetExt(LongestRevName,L"rar",ASIZE(LongestRevName)); + VolNameToFirstName(LongestRevName,FirstVolName,ASIZE(FirstVolName),true); + } + + uiMsg(UIMSG_RECVOLCALCCHECKSUM); + + MissingVolumes=0; + for (uint I=0;I<TotalCount;I++) + { + RecVolItem *Item=&RecItems[I]; + if (Item->f!=NULL) + { + uiMsg(UIMSG_STRING,Item->Name); + + uint RevCRC; + CalcFileSum(Item->f,&RevCRC,NULL,MaxUserThreads,INT64NDF,CALCFSUM_CURPOS); + Item->Valid=RevCRC==Item->CRC; + if (!Item->Valid) + { + uiMsg(UIMSG_CHECKSUM,Item->Name); + + // Close only corrupt REV volumes here. We'll close and rename corrupt + // RAR volumes later, if we'll know that recovery is possible. + if (I>=DataCount) + { + Item->f->Close(); + Item->f=NULL; + FoundRecVolumes--; + } + } + } + if (I<DataCount && (Item->f==NULL || !Item->Valid)) + MissingVolumes++; + } + + uiMsg(UIMSG_RECVOLMISSING,MissingVolumes); + + if (MissingVolumes==0) + { + uiMsg(UIERROR_RECVOLALLEXIST); + return false; + } + + if (MissingVolumes>FoundRecVolumes) + { + uiMsg(UIERROR_RECVOLFOUND,FoundRecVolumes); // Intentionally not displayed in console mode. + uiMsg(UIERROR_RECVOLCANNOTFIX); + return false; + } + + uiMsg(UIMSG_RECONSTRUCTING); + + // Create missing and rename bad volumes. + uint64 MaxVolSize=0; + for (uint I=0;I<DataCount;I++) + { + RecVolItem *Item=&RecItems[I]; + if (Item->FileSize>MaxVolSize) + MaxVolSize=Item->FileSize; + if (Item->f!=NULL && !Item->Valid) + { + Item->f->Close(); + + wchar NewName[NM]; + wcsncpyz(NewName,Item->Name,ASIZE(NewName)); + wcsncatz(NewName,L".bad",ASIZE(NewName)); + + uiMsg(UIMSG_BADARCHIVE,Item->Name); + uiMsg(UIMSG_RENAMING,Item->Name,NewName); + RenameFile(Item->Name,NewName); + delete Item->f; + Item->f=NULL; + } + + if ((Item->New=(Item->f==NULL))==true) + { + wcsncpyz(Item->Name,FirstVolName,ASIZE(Item->Name)); + uiMsg(UIMSG_CREATING,Item->Name); + uiMsg(UIEVENT_NEWARCHIVE,Item->Name); + File *NewVol=new File; + bool UserReject; + if (!FileCreate(Cmd,NewVol,Item->Name,ASIZE(Item->Name),&UserReject)) + { + if (!UserReject) + ErrHandler.CreateErrorMsg(Item->Name); + ErrHandler.Exit(UserReject ? RARX_USERBREAK:RARX_CREATE); + } + NewVol->Prealloc(Item->FileSize); + Item->f=NewVol; + } + NextVolumeName(FirstVolName,ASIZE(FirstVolName),false); + } + + + int64 ProcessedSize=0; + int LastPercent=-1; + mprintf(L" "); + + // Even though we already preliminary calculated missing volume number, + // let's do it again now, when we have the final and exact information. + MissingVolumes=0; + + ValidFlags=new bool[TotalCount]; + for (uint I=0;I<TotalCount;I++) + { + ValidFlags[I]=RecItems[I].f!=NULL && !RecItems[I].New; + if (I<DataCount && !ValidFlags[I]) + MissingVolumes++; + } + + // Size of per file buffer. + RecBufferSize=TotalBufferSize/MissingVolumes; + if ((RecBufferSize&1)==1) // Must be even for our RS16 codec. + RecBufferSize--; +#ifdef USE_SSE + RecBufferSize&=~(SSE_ALIGNMENT-1); // Align for SSE. +#endif + + RSCoder16 RS; + if (!RS.Init(DataCount,RecCount,ValidFlags)) + { + uiMsg(UIERROR_OPFAILED); + delete[] ValidFlags; + return false; // Should not happen, we check parameter validity above. + } + + RealReadBuffer=new byte[RecBufferSize+SSE_ALIGNMENT]; + byte *ReadBuf=(byte *)ALIGN_VALUE(RealReadBuffer,SSE_ALIGNMENT); + + while (true) + { + Wait(); + + int MaxRead=0; + for (uint I=0,J=DataCount;I<DataCount;I++) + { + uint VolNum=I; + if (!ValidFlags[I]) // If next RAR volume is missing or invalid. + { + while (!ValidFlags[J]) // Find next valid REV volume. + J++; + VolNum=J++; // Use next valid REV volume data instead of RAR. + } + RecVolItem *Item=RecItems+VolNum; + + byte *B=&ReadBuf[0]; + int ReadSize=0; + if (Item->f!=NULL && !Item->New) + ReadSize=Item->f->Read(B,RecBufferSize); + if (ReadSize!=RecBufferSize) + memset(B+ReadSize,0,RecBufferSize-ReadSize); + if (ReadSize>MaxRead) + MaxRead=ReadSize; + + // We can have volumes of different size. Let's use data chunk + // for largest volume size. + uint DataToProcess=(uint)Min(RecBufferSize,MaxVolSize-ProcessedSize); + ProcessRS(Cmd,I,B,DataToProcess,false); + } + if (MaxRead==0) + break; + + for (uint I=0,J=0;I<DataCount;I++) + if (!ValidFlags[I]) + { + RecVolItem *Item=RecItems+I; + size_t WriteSize=(size_t)Min(MaxRead,Item->FileSize); + Item->f->Write(Buf+(J++)*RecBufferSize,WriteSize); + Item->FileSize-=WriteSize; + } + + int CurPercent=ToPercent(ProcessedSize,RecFileSize); + if (!Cmd->DisablePercentage && CurPercent!=LastPercent) + { + uiProcessProgress("RV",ProcessedSize,RecFileSize); + LastPercent=CurPercent; + } + ProcessedSize+=MaxRead; + } + + for (uint I=0;I<TotalCount;I++) + if (RecItems[I].f!=NULL) + RecItems[I].f->Close(); + + delete[] ValidFlags; +#if !defined(SILENT) + if (!Cmd->DisablePercentage) + mprintf(L"\b\b\b\b100%%"); + if (!Silent && !Cmd->DisableDone) + mprintf(St(MDone)); +#endif + return true; +} + + +uint RecVolumes5::ReadHeader(File *RecFile,bool FirstRev) +{ + const size_t FirstReadSize=REV5_SIGN_SIZE+8; + byte ShortBuf[FirstReadSize]; + if (RecFile->Read(ShortBuf,FirstReadSize)!=FirstReadSize) + return 0; + if (memcmp(ShortBuf,REV5_SIGN,REV5_SIGN_SIZE)!=0) + return 0; + uint HeaderSize=RawGet4(ShortBuf+REV5_SIGN_SIZE+4); + if (HeaderSize>0x100000 || HeaderSize<=5) + return 0; + uint BlockCRC=RawGet4(ShortBuf+REV5_SIGN_SIZE); + + RawRead Raw(RecFile); + if (Raw.Read(HeaderSize)!=HeaderSize) + return 0; + + // Calculate CRC32 of entire header including 4 byte size field. + uint CalcCRC=CRC32(0xffffffff,ShortBuf+REV5_SIGN_SIZE+4,4); + if ((CRC32(CalcCRC,Raw.GetDataPtr(),HeaderSize)^0xffffffff)!=BlockCRC) + return 0; + + if (Raw.Get1()!=1) // Version check. + return 0; + DataCount=Raw.Get2(); + RecCount=Raw.Get2(); + TotalCount=DataCount+RecCount; + uint RecNum=Raw.Get2(); // Number of recovery volume. + if (RecNum>=TotalCount || TotalCount>MaxVolumes) + return 0; + uint RevCRC=Raw.Get4(); // CRC of current REV volume. + + if (FirstRev) + { + // If we have read the first valid REV file, init data structures + // using information from REV header. + size_t CurSize=RecItems.Size(); + RecItems.Alloc(TotalCount); + for (size_t I=CurSize;I<TotalCount;I++) + RecItems[I].f=NULL; + for (uint I=0;I<DataCount;I++) + { + RecItems[I].FileSize=Raw.Get8(); + RecItems[I].CRC=Raw.Get4(); + } + } + + RecItems[RecNum].CRC=RevCRC; // Assign it here, after allocating RecItems. + + return RecNum; +} + + +void RecVolumes5::Test(CommandData *Cmd,const wchar *Name) +{ + wchar VolName[NM]; + wcsncpyz(VolName,Name,ASIZE(VolName)); + + uint FoundRecVolumes=0; + while (FileExist(VolName)) + { + File CurFile; + if (!CurFile.Open(VolName)) + { + ErrHandler.OpenErrorMsg(VolName); // It also sets RARX_OPEN. + continue; + } + if (!uiStartFileExtract(VolName,false,true,false)) + return; + mprintf(St(MExtrTestFile),VolName); + mprintf(L" "); + bool Valid=false; + uint RecNum=ReadHeader(&CurFile,FoundRecVolumes==0); + if (RecNum!=0) + { + FoundRecVolumes++; + + uint RevCRC; + CalcFileSum(&CurFile,&RevCRC,NULL,1,INT64NDF,CALCFSUM_CURPOS|(Cmd->DisablePercentage ? 0 : CALCFSUM_SHOWPROGRESS)); + Valid=RevCRC==RecItems[RecNum].CRC; + } + + if (Valid) + { + mprintf(L"%s%s ",L"\b\b\b\b\b ",St(MOk)); + } + else + { + uiMsg(UIERROR_CHECKSUM,VolName,VolName); + ErrHandler.SetErrorCode(RARX_CRC); + } + + NextVolumeName(VolName,ASIZE(VolName),false); + } +} diff --git a/unrar/unrar/resource.cpp b/unrar/unrar/resource.cpp index f2a4657..dadd072 100644 --- a/unrar/unrar/resource.cpp +++ b/unrar/unrar/resource.cpp @@ -2,11 +2,21 @@ -#if !defined(SILENT) || !defined(RARDLL) -const char *St(MSGID StringId) + + +#ifndef RARDLL +const wchar* St(MSGID StringId) { - return(StringId); + return StringId; } -#endif +// Needed for Unix swprintf to convert %s to %ls in legacy language resources. +const wchar *StF(MSGID StringId) +{ + static wchar FormattedStr[512]; + PrintfPrepareFmt(St(StringId),FormattedStr,ASIZE(FormattedStr)); + return FormattedStr; +} +#endif + diff --git a/unrar/unrar/resource.hpp b/unrar/unrar/resource.hpp index 581b34b..62c5bf4 100644 --- a/unrar/unrar/resource.hpp +++ b/unrar/unrar/resource.hpp @@ -1,14 +1,13 @@ #ifndef _RAR_RESOURCE_ #define _RAR_RESOURCE_ -#if defined(SILENT) && defined(RARDLL) -#define St(x) ("") +#ifdef RARDLL +#define St(x) (L"") +#define StF(x) (L"") #else -const char *St(MSGID StringId); +const wchar *St(MSGID StringId); +const wchar *StF(MSGID StringId); #endif -inline const char *StT(MSGID StringId) {return(St(StringId));} - - #endif diff --git a/unrar/unrar/rijndael.cpp b/unrar/unrar/rijndael.cpp index d0d71b9..02c4d14 100644 --- a/unrar/unrar/rijndael.cpp +++ b/unrar/unrar/rijndael.cpp @@ -1,30 +1,51 @@ /************************************************************************** - * This code is based on Szymon Stefanek AES implementation: * - * http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael-cpplib.tar.gz * - * * - * Dynamic tables generation is based on the Brian Gladman work: * - * http://fp.gladman.plus.com/cryptography_technology/rijndael * + * This code is based on Szymon Stefanek public domain AES implementation * **************************************************************************/ #include "rar.hpp" -const int uKeyLenInBytes=16, m_uRounds=10; +#ifdef USE_SSE +#include <wmmintrin.h> +#endif + +static byte S[256]= +{ + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 +}; + +static byte S5[256]; + +// Round constants. 10 items are used by AES-128, 8 by AES-192, 7 by AES-256. +static byte rcon[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36}; -static byte S[256],S5[256],rcon[30]; static byte T1[256][4],T2[256][4],T3[256][4],T4[256][4]; static byte T5[256][4],T6[256][4],T7[256][4],T8[256][4]; static byte U1[256][4],U2[256][4],U3[256][4],U4[256][4]; - -inline void Xor128(byte *dest,const byte *arg1,const byte *arg2) +inline void Xor128(void *dest,const void *arg1,const void *arg2) { -#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) +#ifdef ALLOW_MISALIGNED ((uint32*)dest)[0]=((uint32*)arg1)[0]^((uint32*)arg2)[0]; ((uint32*)dest)[1]=((uint32*)arg1)[1]^((uint32*)arg2)[1]; ((uint32*)dest)[2]=((uint32*)arg1)[2]^((uint32*)arg2)[2]; ((uint32*)dest)[3]=((uint32*)arg1)[3]^((uint32*)arg2)[3]; #else for (int I=0;I<16;I++) - dest[I]=arg1[I]^arg2[I]; + ((byte*)dest)[I]=((byte*)arg1)[I]^((byte*)arg2)[I]; #endif } @@ -32,7 +53,7 @@ inline void Xor128(byte *dest,const byte *arg1,const byte *arg2) inline void Xor128(byte *dest,const byte *arg1,const byte *arg2, const byte *arg3,const byte *arg4) { -#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) +#ifdef ALLOW_MISALIGNED (*(uint32*)dest)=(*(uint32*)arg1)^(*(uint32*)arg2)^(*(uint32*)arg3)^(*(uint32*)arg4); #else for (int I=0;I<4;I++) @@ -43,7 +64,7 @@ inline void Xor128(byte *dest,const byte *arg1,const byte *arg2, inline void Copy128(byte *dest,const byte *src) { -#if defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT) +#ifdef ALLOW_MISALIGNED ((uint32*)dest)[0]=((uint32*)src)[0]; ((uint32*)dest)[1]=((uint32*)src)[1]; ((uint32*)dest)[2]=((uint32*)src)[2]; @@ -61,59 +82,349 @@ inline void Copy128(byte *dest,const byte *src) Rijndael::Rijndael() { - if (S[0]==0) + if (S5[0]==0) GenerateTables(); + CBCMode = true; // Always true for RAR. } -void Rijndael::init(Direction dir,const byte * key,byte * initVector) +void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVector) { - m_direction = dir; + // Check SIMD here instead of constructor, so if object is a part of some + // structure memset'ed before use, these variables are not lost. +#if defined(USE_SSE) + int CPUInfo[4]; + __cpuid(CPUInfo, 0); + if (CPUInfo[0]>=1) // Check the maximum supported cpuid function. + { + __cpuid(CPUInfo, 1); + AES_NI=(CPUInfo[2] & 0x2000000)!=0; + } + else + AES_NI=false; +#elif defined(USE_NEON) + AES_Neon=(getauxval(AT_HWCAP) & HWCAP_AES)!=0; +#endif + + // Other developers asked us to initialize it to suppress "may be used + // uninitialized" warning in code below in some compilers. + uint uKeyLenInBytes=0; + + switch(keyLen) + { + case 128: + uKeyLenInBytes = 16; + m_uRounds = 10; + break; + case 192: + uKeyLenInBytes = 24; + m_uRounds = 12; + break; + case 256: + uKeyLenInBytes = 32; + m_uRounds = 14; + break; + } byte keyMatrix[_MAX_KEY_COLUMNS][4]; - for(uint i = 0;i < uKeyLenInBytes;i++) + for(uint i = 0; i < uKeyLenInBytes; i++) keyMatrix[i >> 2][i & 3] = key[i]; - for(int i = 0;i < MAX_IV_SIZE;i++) - m_initVector[i] = initVector[i]; + if (initVector==NULL) + memset(m_initVector, 0, sizeof(m_initVector)); + else + for(int i = 0; i < MAX_IV_SIZE; i++) + m_initVector[i] = initVector[i]; keySched(keyMatrix); - if(m_direction == Decrypt) + if(!Encrypt) keyEncToDec(); } +void Rijndael::blockEncrypt(const byte *input,size_t inputLen,byte *outBuffer) +{ + if (inputLen <= 0) + return; + + size_t numBlocks = inputLen/16; +#if defined(USE_SSE) + if (AES_NI) + { + blockEncryptSSE(input,numBlocks,outBuffer); + return; + } +#elif defined(USE_NEON) + if (AES_Neon) + { + blockEncryptNeon(input,numBlocks,outBuffer); + return; + } +#endif + + byte *prevBlock = m_initVector; + for(size_t i = numBlocks;i > 0;i--) + { + byte block[16]; + if (CBCMode) + Xor128(block,prevBlock,input); + else + Copy128(block,input); + + byte temp[4][4]; + + Xor128(temp,block,m_expandedKey[0]); + Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]); + Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]); + Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]); + Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]); + + for(int r = 1; r < m_uRounds-1; r++) + { + Xor128(temp,outBuffer,m_expandedKey[r]); + Xor128(outBuffer, T1[temp[0][0]],T2[temp[1][1]],T3[temp[2][2]],T4[temp[3][3]]); + Xor128(outBuffer+4, T1[temp[1][0]],T2[temp[2][1]],T3[temp[3][2]],T4[temp[0][3]]); + Xor128(outBuffer+8, T1[temp[2][0]],T2[temp[3][1]],T3[temp[0][2]],T4[temp[1][3]]); + Xor128(outBuffer+12,T1[temp[3][0]],T2[temp[0][1]],T3[temp[1][2]],T4[temp[2][3]]); + } + Xor128(temp,outBuffer,m_expandedKey[m_uRounds-1]); + outBuffer[ 0] = T1[temp[0][0]][1]; + outBuffer[ 1] = T1[temp[1][1]][1]; + outBuffer[ 2] = T1[temp[2][2]][1]; + outBuffer[ 3] = T1[temp[3][3]][1]; + outBuffer[ 4] = T1[temp[1][0]][1]; + outBuffer[ 5] = T1[temp[2][1]][1]; + outBuffer[ 6] = T1[temp[3][2]][1]; + outBuffer[ 7] = T1[temp[0][3]][1]; + outBuffer[ 8] = T1[temp[2][0]][1]; + outBuffer[ 9] = T1[temp[3][1]][1]; + outBuffer[10] = T1[temp[0][2]][1]; + outBuffer[11] = T1[temp[1][3]][1]; + outBuffer[12] = T1[temp[3][0]][1]; + outBuffer[13] = T1[temp[0][1]][1]; + outBuffer[14] = T1[temp[1][2]][1]; + outBuffer[15] = T1[temp[2][3]][1]; + Xor128(outBuffer,outBuffer,m_expandedKey[m_uRounds]); + prevBlock=outBuffer; + + outBuffer += 16; + input += 16; + } + Copy128(m_initVector,prevBlock); +} + + +#ifdef USE_SSE +void Rijndael::blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer) +{ + __m128i v = _mm_loadu_si128((__m128i*)m_initVector); + __m128i *src=(__m128i*)input; + __m128i *dest=(__m128i*)outBuffer; + __m128i *rkey=(__m128i*)m_expandedKey; + while (numBlocks > 0) + { + __m128i d = _mm_loadu_si128(src++); + if (CBCMode) + v = _mm_xor_si128(v, d); + else + v = d; + __m128i r0 = _mm_loadu_si128(rkey); + v = _mm_xor_si128(v, r0); + + for (int i=1; i<m_uRounds; i++) + { + __m128i ri = _mm_loadu_si128(rkey + i); + v = _mm_aesenc_si128(v, ri); + } + + __m128i rl = _mm_loadu_si128(rkey + m_uRounds); + v = _mm_aesenclast_si128(v, rl); + _mm_storeu_si128(dest++,v); + numBlocks--; + } + _mm_storeu_si128((__m128i*)m_initVector,v); +} +#endif + + +#ifdef USE_NEON +void Rijndael::blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer) +{ + byte *prevBlock = m_initVector; + while (numBlocks > 0) + { + byte block[16]; + if (CBCMode) + vst1q_u8(block, veorq_u8(vld1q_u8(prevBlock), vld1q_u8(input))); + else + vst1q_u8(block, vld1q_u8(input)); + + uint8x16_t data = vld1q_u8(block); + for (uint i = 0; i < m_uRounds-1; i++) + { + data = vaeseq_u8(data, vld1q_u8((byte *)m_expandedKey[i])); + data = vaesmcq_u8(data); + } + data = vaeseq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds-1]))); + data = veorq_u8(data, vld1q_u8((byte *)(m_expandedKey[m_uRounds]))); + vst1q_u8(outBuffer, data); + + prevBlock=outBuffer; + + outBuffer += 16; + input += 16; + numBlocks--; + } + vst1q_u8(m_initVector, vld1q_u8(prevBlock)); + return; +} +#endif + -int Rijndael::blockDecrypt(const byte *input, int inputLen, byte *outBuffer) +void Rijndael::blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer) { - if (input == 0 || inputLen <= 0) - return 0; + if (inputLen <= 0) + return; + + size_t numBlocks=inputLen/16; +#if defined(USE_SSE) + if (AES_NI) + { + blockDecryptSSE(input,numBlocks,outBuffer); + return; + } +#elif defined(USE_NEON) + if (AES_Neon) + { + blockDecryptNeon(input,numBlocks,outBuffer); + return; + } +#endif byte block[16], iv[4][4]; memcpy(iv,m_initVector,16); - int numBlocks=inputLen/16; - for (int i = numBlocks; i > 0; i--) + for (size_t i = numBlocks; i > 0; i--) { - decrypt(input, block); - Xor128(block,block,(byte*)iv); -#if STRICT_ALIGN - memcpy(iv, input, 16); - memcpy(outBuf, block, 16); -#else + byte temp[4][4]; + + Xor128(temp,input,m_expandedKey[m_uRounds]); + + Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); + Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); + Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); + Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); + + for(int r = m_uRounds-1; r > 1; r--) + { + Xor128(temp,block,m_expandedKey[r]); + Xor128(block, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); + Xor128(block+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); + Xor128(block+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); + Xor128(block+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); + } + + Xor128(temp,block,m_expandedKey[1]); + block[ 0] = S5[temp[0][0]]; + block[ 1] = S5[temp[3][1]]; + block[ 2] = S5[temp[2][2]]; + block[ 3] = S5[temp[1][3]]; + block[ 4] = S5[temp[1][0]]; + block[ 5] = S5[temp[0][1]]; + block[ 6] = S5[temp[3][2]]; + block[ 7] = S5[temp[2][3]]; + block[ 8] = S5[temp[2][0]]; + block[ 9] = S5[temp[1][1]]; + block[10] = S5[temp[0][2]]; + block[11] = S5[temp[3][3]]; + block[12] = S5[temp[3][0]]; + block[13] = S5[temp[2][1]]; + block[14] = S5[temp[1][2]]; + block[15] = S5[temp[0][3]]; + Xor128(block,block,m_expandedKey[0]); + + if (CBCMode) + Xor128(block,block,iv); + Copy128((byte*)iv,input); Copy128(outBuffer,block); + + input += 16; + outBuffer += 16; + } + + memcpy(m_initVector,iv,16); +} + + +#ifdef USE_SSE +void Rijndael::blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer) +{ + __m128i initVector = _mm_loadu_si128((__m128i*)m_initVector); + __m128i *src=(__m128i*)input; + __m128i *dest=(__m128i*)outBuffer; + __m128i *rkey=(__m128i*)m_expandedKey; + while (numBlocks > 0) + { + __m128i rl = _mm_loadu_si128(rkey + m_uRounds); + __m128i d = _mm_loadu_si128(src++); + __m128i v = _mm_xor_si128(rl, d); + + for (int i=m_uRounds-1; i>0; i--) + { + __m128i ri = _mm_loadu_si128(rkey + i); + v = _mm_aesdec_si128(v, ri); + } + + __m128i r0 = _mm_loadu_si128(rkey); + v = _mm_aesdeclast_si128(v, r0); + + if (CBCMode) + v = _mm_xor_si128(v, initVector); + initVector = d; + _mm_storeu_si128(dest++,v); + numBlocks--; + } + _mm_storeu_si128((__m128i*)m_initVector,initVector); +} #endif + + +#ifdef USE_NEON +void Rijndael::blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer) +{ + byte iv[16]; + memcpy(iv,m_initVector,16); + + while (numBlocks > 0) + { + uint8x16_t data = vld1q_u8(input); + + for (int i=m_uRounds-1; i>0; i--) + { + data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[i+1])); + data = vaesimcq_u8(data); + } + + data = vaesdq_u8(data, vld1q_u8((byte *)m_expandedKey[1])); + data = veorq_u8(data, vld1q_u8((byte *)m_expandedKey[0])); + + if (CBCMode) + data = veorq_u8(data, vld1q_u8(iv)); + + vst1q_u8(iv, vld1q_u8(input)); + vst1q_u8(outBuffer, data); + input += 16; outBuffer += 16; + numBlocks--; } memcpy(m_initVector,iv,16); - - return 16*numBlocks; } +#endif ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -197,8 +508,8 @@ void Rijndael::keyEncToDec() for(int r = 1; r < m_uRounds; r++) { byte n_expandedKey[4][4]; - for (int i=0;i<4;i++) - for (int j=0;j<4;j++) + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) { byte *w=m_expandedKey[r][j]; n_expandedKey[j][i]=U1[w[0]][i]^U2[w[1]][i]^U3[w[2]][i]^U4[w[3]][i]; @@ -208,91 +519,89 @@ void Rijndael::keyEncToDec() } -void Rijndael::decrypt(const byte a[16], byte b[16]) +static byte gmul(byte a, byte b) // Galois field "peasant's algorithm" multiplication. { - int r; - byte temp[4][4]; - - Xor128((byte*)temp,(byte*)a,(byte*)m_expandedKey[m_uRounds]); - - Xor128(b, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); - Xor128(b+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); - Xor128(b+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); - Xor128(b+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); - - for(r = m_uRounds-1; r > 1; r--) + const byte poly=0x1b; // Lower byte of AES 0x11b irreducible polynomial. + byte result = 0; + while (b>0) { - Xor128((byte*)temp,(byte*)b,(byte*)m_expandedKey[r]); - Xor128(b, T5[temp[0][0]],T6[temp[3][1]],T7[temp[2][2]],T8[temp[1][3]]); - Xor128(b+4, T5[temp[1][0]],T6[temp[0][1]],T7[temp[3][2]],T8[temp[2][3]]); - Xor128(b+8, T5[temp[2][0]],T6[temp[1][1]],T7[temp[0][2]],T8[temp[3][3]]); - Xor128(b+12,T5[temp[3][0]],T6[temp[2][1]],T7[temp[1][2]],T8[temp[0][3]]); + if ((b & 1) != 0) + result ^= a; + a = (a & 0x80) ? (a<<1)^poly : a<<1; + b >>= 1; } - - Xor128((byte*)temp,(byte*)b,(byte*)m_expandedKey[1]); - b[ 0] = S5[temp[0][0]]; - b[ 1] = S5[temp[3][1]]; - b[ 2] = S5[temp[2][2]]; - b[ 3] = S5[temp[1][3]]; - b[ 4] = S5[temp[1][0]]; - b[ 5] = S5[temp[0][1]]; - b[ 6] = S5[temp[3][2]]; - b[ 7] = S5[temp[2][3]]; - b[ 8] = S5[temp[2][0]]; - b[ 9] = S5[temp[1][1]]; - b[10] = S5[temp[0][2]]; - b[11] = S5[temp[3][3]]; - b[12] = S5[temp[3][0]]; - b[13] = S5[temp[2][1]]; - b[14] = S5[temp[1][2]]; - b[15] = S5[temp[0][3]]; - Xor128((byte*)b,(byte*)b,(byte*)m_expandedKey[0]); + return result; } -#define ff_poly 0x011b -#define ff_hi 0x80 -#define FFinv(x) ((x) ? pow[255 - log[x]]: 0) +// 2021-09-24: changed to slower and simpler code without interim tables. +// It is still fast enough for our purpose. +void Rijndael::GenerateTables() +{ + for (int I=0;I<256;I++) + S5[S[I]]=I; + + for (int I=0;I<256;I++) + { + byte s=S[I]; + T1[I][1]=T1[I][2]=T2[I][2]=T2[I][3]=T3[I][0]=T3[I][3]=T4[I][0]=T4[I][1]=s; + T1[I][0]=T2[I][1]=T3[I][2]=T4[I][3]=gmul(s,2); + T1[I][3]=T2[I][0]=T3[I][1]=T4[I][2]=gmul(s,3); + + byte b=S5[I]; + U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[I][3]=T6[I][0]=T7[I][1]=T8[I][2]=gmul(b,0xb); + U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[I][1]=T6[I][2]=T7[I][3]=T8[I][0]=gmul(b,0x9); + U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[I][2]=T6[I][3]=T7[I][0]=T8[I][1]=gmul(b,0xd); + U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[I][0]=T6[I][1]=T7[I][2]=T8[I][3]=gmul(b,0xe); + } +} -#define FFmul02(x) (x ? pow[log[x] + 0x19] : 0) -#define FFmul03(x) (x ? pow[log[x] + 0x01] : 0) -#define FFmul09(x) (x ? pow[log[x] + 0xc7] : 0) -#define FFmul0b(x) (x ? pow[log[x] + 0x68] : 0) -#define FFmul0d(x) (x ? pow[log[x] + 0xee] : 0) -#define FFmul0e(x) (x ? pow[log[x] + 0xdf] : 0) -#define fwd_affine(x) \ - (w = (uint)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), (byte)(0x63^(w^(w>>8)))) -#define inv_affine(x) \ - (w = (uint)x, w = (w<<1)^(w<<3)^(w<<6), (byte)(0x05^(w^(w>>8)))) +#if 0 +static void TestRijndael(); +struct TestRij {TestRij() {TestRijndael();exit(0);}} GlobalTestRij; -void Rijndael::GenerateTables() +// Test CBC encryption according to NIST 800-38A. +void TestRijndael() { - unsigned char pow[512],log[256]; - int i = 0, w = 1; - do - { - pow[i] = (byte)w; - pow[i + 255] = (byte)w; - log[w] = (byte)i++; - w ^= (w << 1) ^ (w & ff_hi ? ff_poly : 0); - } while (w != 1); - - for (int i = 0,w = 1; i < sizeof(rcon)/sizeof(rcon[0]); i++) + byte IV[16]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f}; + byte PT[64]={ + 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a, + 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51, + 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11,0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef, + 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17,0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10, + }; + + byte Key128[16]={0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c}; + byte Chk128[16]={0x3f,0xf1,0xca,0xa1,0x68,0x1f,0xac,0x09,0x12,0x0e,0xca,0x30,0x75,0x86,0xe1,0xa7}; + byte Key192[24]={0x8e,0x73,0xb0,0xf7,0xda,0x0e,0x64,0x52,0xc8,0x10,0xf3,0x2b,0x80,0x90,0x79,0xe5,0x62,0xf8,0xea,0xd2,0x52,0x2c,0x6b,0x7b}; + byte Chk192[16]={0x08,0xb0,0xe2,0x79,0x88,0x59,0x88,0x81,0xd9,0x20,0xa9,0xe6,0x4f,0x56,0x15,0xcd}; + byte Key256[32]={0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4}; + byte Chk256[16]={0xb2,0xeb,0x05,0xe2,0xc3,0x9b,0xe9,0xfc,0xda,0x6c,0x19,0x07,0x8c,0x6a,0x9d,0x1b}; + byte *Key[3]={Key128,Key192,Key256}; + byte *Chk[3]={Chk128,Chk192,Chk256}; + + Rijndael rij; // Declare outside of loop to test re-initialization. + for (uint L=0;L<3;L++) { - rcon[i] = w; - w = (w << 1) ^ (w & ff_hi ? ff_poly : 0); - } - for(int i = 0; i < 256; ++i) - { - unsigned char b=S[i]=fwd_affine(FFinv((byte)i)); - T1[i][1]=T1[i][2]=T2[i][2]=T2[i][3]=T3[i][0]=T3[i][3]=T4[i][0]=T4[i][1]=b; - T1[i][0]=T2[i][1]=T3[i][2]=T4[i][3]=FFmul02(b); - T1[i][3]=T2[i][0]=T3[i][1]=T4[i][2]=FFmul03(b); - S5[i] = b = FFinv(inv_affine((byte)i)); - U1[b][3]=U2[b][0]=U3[b][1]=U4[b][2]=T5[i][3]=T6[i][0]=T7[i][1]=T8[i][2]=FFmul0b(b); - U1[b][1]=U2[b][2]=U3[b][3]=U4[b][0]=T5[i][1]=T6[i][2]=T7[i][3]=T8[i][0]=FFmul09(b); - U1[b][2]=U2[b][3]=U3[b][0]=U4[b][1]=T5[i][2]=T6[i][3]=T7[i][0]=T8[i][1]=FFmul0d(b); - U1[b][0]=U2[b][1]=U3[b][2]=U4[b][3]=T5[i][0]=T6[i][1]=T7[i][2]=T8[i][3]=FFmul0e(b); + byte Out[16]; + wchar Str[sizeof(Out)*2+1]; + + uint KeyLength=128+L*64; + rij.Init(true,Key[L],KeyLength,IV); + for (uint I=0;I<sizeof(PT);I+=16) + rij.blockEncrypt(PT+I,16,Out); + BinToHex(Chk[L],16,NULL,Str,ASIZE(Str)); + mprintf(L"\nAES-%d expected: %s",KeyLength,Str); + BinToHex(Out,sizeof(Out),NULL,Str,ASIZE(Str)); + mprintf(L"\nAES-%d result: %s",KeyLength,Str); + if (memcmp(Out,Chk[L],16)==0) + mprintf(L" OK"); + else + { + mprintf(L" FAILED"); + getchar(); + } } } +#endif diff --git a/unrar/unrar/rijndael.hpp b/unrar/unrar/rijndael.hpp index 6a8edde..96e1d0d 100644 --- a/unrar/unrar/rijndael.hpp +++ b/unrar/unrar/rijndael.hpp @@ -2,11 +2,7 @@ #define _RIJNDAEL_H_ /************************************************************************** - * This code is based on Szymon Stefanek AES implementation: * - * http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael-cpplib.tar.gz * - * * - * Dynamic tables generation is based on the Brian Gladman's work: * - * http://fp.gladman.plus.com/cryptography_technology/rijndael * + * This code is based on Szymon Stefanek public domain AES implementation * **************************************************************************/ #define _MAX_KEY_COLUMNS (256/32) @@ -14,24 +10,41 @@ #define MAX_IV_SIZE 16 class Rijndael -{ - public: - enum Direction { Encrypt , Decrypt }; +{ private: +#ifdef USE_SSE + void blockEncryptSSE(const byte *input,size_t numBlocks,byte *outBuffer); + void blockDecryptSSE(const byte *input, size_t numBlocks, byte *outBuffer); + + bool AES_NI; +#endif +#ifdef USE_NEON + // Set "crypto" attribute as replacement of -march=armv8-a+crypto switch. + __attribute__((target("crypto"))) + void blockEncryptNeon(const byte *input,size_t numBlocks,byte *outBuffer); + __attribute__((target("crypto"))) + void blockDecryptNeon(const byte *input, size_t numBlocks, byte *outBuffer); + + bool AES_Neon; +#endif + void keySched(byte key[_MAX_KEY_COLUMNS][4]); void keyEncToDec(); - void encrypt(const byte a[16], byte b[16]); - void decrypt(const byte a[16], byte b[16]); void GenerateTables(); - Direction m_direction; + // RAR always uses CBC, but we may need to turn it off when calling + // this code from other archive formats with CTR and other modes. + bool CBCMode; + + int m_uRounds; byte m_initVector[MAX_IV_SIZE]; byte m_expandedKey[_MAX_ROUNDS+1][4][4]; public: Rijndael(); - void init(Direction dir,const byte *key,byte *initVector); - int blockEncrypt(const byte *input, int inputLen, byte *outBuffer); - int blockDecrypt(const byte *input, int inputLen, byte *outBuffer); + void Init(bool Encrypt,const byte *key,uint keyLen,const byte *initVector); + void blockEncrypt(const byte *input, size_t inputLen, byte *outBuffer); + void blockDecrypt(const byte *input, size_t inputLen, byte *outBuffer); + void SetCBCMode(bool Mode) {CBCMode=Mode;} }; - + #endif // _RIJNDAEL_H_ diff --git a/unrar/unrar/rs.cpp b/unrar/unrar/rs.cpp index 9eed7a2..10ccc6d 100644 --- a/unrar/unrar/rs.cpp +++ b/unrar/unrar/rs.cpp @@ -2,53 +2,65 @@ #define Clean(D,S) {for (int I=0;I<(S);I++) (D)[I]=0;} -RSCoder::RSCoder(int ParSize) +void RSCoder::Init(int ParSize) { - RSCoder::ParSize=ParSize; + RSCoder::ParSize=ParSize; // Store the number of recovery volumes. FirstBlockDone=false; gfInit(); pnInit(); } +// Initialize logarithms and exponents Galois field tables. void RSCoder::gfInit() { for (int I=0,J=1;I<MAXPAR;I++) { gfLog[J]=I; gfExp[I]=J; - if ((J<<=1)&256) - J^=285; + J<<=1; + if (J > MAXPAR) + J^=0x11D; // 0x11D field-generator polynomial (x^8+x^4+x^3+x^2+1). } - for (int I=MAXPAR;I<MAXPOL;I++) + for (int I=MAXPAR;I<MAXPOL;I++) // Avoid gfExp overflow check. gfExp[I]=gfExp[I-MAXPAR]; } +// Multiplication over Galois field. inline int RSCoder::gfMult(int a,int b) { return(a==0 || b == 0 ? 0:gfExp[gfLog[a]+gfLog[b]]); } +// Create the generator polynomial g(x). +// g(x)=(x-a)(x-a^2)(x-a^3)..(x-a^N) void RSCoder::pnInit() { - int p1[MAXPAR+1],p2[MAXPAR+1]; + int p2[MAXPAR+1]; // Currently calculated part of g(x). Clean(p2,ParSize); - p2[0]=1; + p2[0]=1; // Set p2 polynomial to 1. + for (int I=1;I<=ParSize;I++) { + int p1[MAXPAR+1]; // We use p1 as current (x+a^i) expression. Clean(p1,ParSize); p1[0]=gfExp[I]; - p1[1]=1; + p1[1]=1; // Set p1 polynomial to x+a^i. + + // Multiply the already calucated part of g(x) to next (x+a^i). pnMult(p1,p2,GXPol); + + // p2=g(x). for (int J=0;J<ParSize;J++) p2[J]=GXPol[J]; } } +// Multiply polynomial 'p1' to 'p2' and store the result in 'r'. void RSCoder::pnMult(int *p1,int *p2,int *r) { Clean(r,ParSize); @@ -61,12 +73,14 @@ void RSCoder::pnMult(int *p1,int *p2,int *r) void RSCoder::Encode(byte *Data,int DataSize,byte *DestData) { - int ShiftReg[MAXPAR+1]; + int ShiftReg[MAXPAR+1]; // Linear Feedback Shift Register. Clean(ShiftReg,ParSize+1); for (int I=0;I<DataSize;I++) { int D=Data[I]^ShiftReg[ParSize-1]; + + // Use g(x) to define feedback taps. for (int J=ParSize-1;J>0;J--) ShiftReg[J]=ShiftReg[J-1]^gfMult(GXPol[J],D); ShiftReg[0]=gfMult(GXPol[0],D); @@ -78,66 +92,69 @@ void RSCoder::Encode(byte *Data,int DataSize,byte *DestData) bool RSCoder::Decode(byte *Data,int DataSize,int *EraLoc,int EraSize) { - int SynData[MAXPOL]; + int SynData[MAXPOL]; // Syndrome data. + bool AllZeroes=true; for (int I=0;I<ParSize;I++) { - int Sum=Data[0],J=1,Exp=gfExp[I+1]; - for (;J+8<=DataSize;J+=8) - { - Sum=Data[J]^gfMult(Exp,Sum); - Sum=Data[J+1]^gfMult(Exp,Sum); - Sum=Data[J+2]^gfMult(Exp,Sum); - Sum=Data[J+3]^gfMult(Exp,Sum); - Sum=Data[J+4]^gfMult(Exp,Sum); - Sum=Data[J+5]^gfMult(Exp,Sum); - Sum=Data[J+6]^gfMult(Exp,Sum); - Sum=Data[J+7]^gfMult(Exp,Sum); - } - for (;J<DataSize;J++) - Sum=Data[J]^gfMult(Exp,Sum); + int Sum=0; + for (int J=0;J<DataSize;J++) + Sum=Data[J]^gfMult(gfExp[I+1],Sum); if ((SynData[I]=Sum)!=0) AllZeroes=false; } + + // If all syndrome numbers are zero, message does not have errors. if (AllZeroes) return(true); - if (!FirstBlockDone) + if (!FirstBlockDone) // Do things which we need to do once for all data. { FirstBlockDone=true; - Clean(PolB,ParSize+1); - PolB[0]=1; + + // Calculate the error locator polynomial. + Clean(ELPol,ParSize+1); + ELPol[0]=1; + for (int EraPos=0;EraPos<EraSize;EraPos++) for (int I=ParSize,M=gfExp[DataSize-EraLoc[EraPos]-1];I>0;I--) - PolB[I]^=gfMult(M,PolB[I-1]); + ELPol[I]^=gfMult(M,ELPol[I-1]); ErrCount=0; + + // Find roots of error locator polynomial. for (int Root=MAXPAR-DataSize;Root<MAXPAR+1;Root++) { int Sum=0; for (int B=0;B<ParSize+1;B++) - Sum^=gfMult(gfExp[(B*Root)%MAXPAR],PolB[B]); - if (Sum==0) + Sum^=gfMult(gfExp[(B*Root)%MAXPAR],ELPol[B]); + if (Sum==0) // Root found. { - Dn[ErrCount]=0; + ErrorLocs[ErrCount]=MAXPAR-Root; // Location of error. + + // Calculate the denominator for every error location. + Dnm[ErrCount]=0; for (int I=1;I<ParSize+1;I+=2) - Dn[ErrCount]^= gfMult(PolB[I],gfExp[Root*(I-1)%MAXPAR]); - ErrorLocs[ErrCount++]=MAXPAR-Root; + Dnm[ErrCount]^= gfMult(ELPol[I],gfExp[Root*(I-1)%MAXPAR]); + + ErrCount++; } } } - int PolD[MAXPOL]; - pnMult(PolB,SynData,PolD); + int EEPol[MAXPOL]; // Error Evaluator Polynomial. + pnMult(ELPol,SynData,EEPol); + // If errors are present and their number is correctable. if ((ErrCount<=ParSize) && ErrCount>0) for (int I=0;I<ErrCount;I++) { int Loc=ErrorLocs[I],DLoc=MAXPAR-Loc,N=0; for (int J=0;J<ParSize;J++) - N^=gfMult(PolD[J],gfExp[DLoc*J%MAXPAR]); + N^=gfMult(EEPol[J],gfExp[DLoc*J%MAXPAR]); int DataPos=DataSize-Loc-1; + // Perform bounds check and correct the data error. if (DataPos>=0 && DataPos<DataSize) - Data[DataPos]^=gfMult(N,gfExp[MAXPAR-gfLog[Dn[I]]]); + Data[DataPos]^=gfMult(N,gfExp[MAXPAR-gfLog[Dnm[I]]]); } - return(ErrCount<=ParSize); + return(ErrCount<=ParSize); // Return true if success. } diff --git a/unrar/unrar/rs.hpp b/unrar/unrar/rs.hpp index 2f099f0..6ac8094 100644 --- a/unrar/unrar/rs.hpp +++ b/unrar/unrar/rs.hpp @@ -1,8 +1,8 @@ #ifndef _RAR_RS_ #define _RAR_RS_ -#define MAXPAR 255 -#define MAXPOL 512 +#define MAXPAR 255 // Maximum parity data size. +#define MAXPOL 512 // Maximum polynomial degree. class RSCoder { @@ -12,19 +12,19 @@ class RSCoder void pnInit(); void pnMult(int *p1,int *p2,int *r); - int gfExp[MAXPOL]; - int gfLog[MAXPAR+1]; + int gfExp[MAXPOL]; // Galois field exponents. + int gfLog[MAXPAR+1]; // Galois field logarithms. - int GXPol[MAXPOL*2]; + int GXPol[MAXPOL*2]; // Generator polynomial g(x). int ErrorLocs[MAXPAR+1],ErrCount; - int Dn[MAXPAR+1]; + int Dnm[MAXPAR+1]; - int ParSize; - int PolB[MAXPOL]; + int ParSize; // Parity bytes size and so the number of recovery volumes. + int ELPol[MAXPOL]; // Error locator polynomial. bool FirstBlockDone; public: - RSCoder(int ParSize); + void Init(int ParSize); void Encode(byte *Data,int DataSize,byte *DestData); bool Decode(byte *Data,int DataSize,int *EraLoc,int EraSize); }; diff --git a/unrar/unrar/rs16.cpp b/unrar/unrar/rs16.cpp new file mode 100644 index 0000000..f5c7cca --- /dev/null +++ b/unrar/unrar/rs16.cpp @@ -0,0 +1,421 @@ +#include "rar.hpp" + +// We used "Screaming Fast Galois Field Arithmetic Using Intel SIMD +// Instructions" paper by James S. Plank, Kevin M. Greenan +// and Ethan L. Miller for fast SSE based multiplication. +// Also we are grateful to Artem Drobanov and Bulat Ziganshin +// for samples and ideas allowed to make Reed-Solomon codec more efficient. + +RSCoder16::RSCoder16() +{ + Decoding=false; + ND=NR=NE=0; + ValidFlags=NULL; + MX=NULL; + DataLog=NULL; + DataLogSize=0; + + gfInit(); +} + + +RSCoder16::~RSCoder16() +{ + delete[] gfExp; + delete[] gfLog; + delete[] DataLog; + delete[] MX; + delete[] ValidFlags; +} + + +// Initialize logarithms and exponents Galois field tables. +void RSCoder16::gfInit() +{ + gfExp=new uint[4*gfSize+1]; + gfLog=new uint[gfSize+1]; + + for (uint L=0,E=1;L<gfSize;L++) + { + gfLog[E]=L; + gfExp[L]=E; + gfExp[L+gfSize]=E; // Duplicate the table to avoid gfExp overflow check. + E<<=1; + if (E>gfSize) + E^=0x1100B; // Irreducible field-generator polynomial. + } + + // log(0)+log(x) must be outside of usual log table, so we can set it + // to 0 and avoid check for 0 in multiplication parameters. + gfLog[0]= 2*gfSize; + for (uint I=2*gfSize;I<=4*gfSize;I++) // Results for log(0)+log(x). + gfExp[I]=0; +} + + +uint RSCoder16::gfAdd(uint a,uint b) // Addition in Galois field. +{ + return a^b; +} + + +uint RSCoder16::gfMul(uint a,uint b) // Multiplication in Galois field. +{ + return gfExp[gfLog[a]+gfLog[b]]; +} + + +uint RSCoder16::gfInv(uint a) // Inverse element in Galois field. +{ + return a==0 ? 0:gfExp[gfSize-gfLog[a]]; +} + + +bool RSCoder16::Init(uint DataCount, uint RecCount, bool *ValidityFlags) +{ + ND = DataCount; + NR = RecCount; + NE = 0; + + Decoding=ValidityFlags!=NULL; + if (Decoding) + { + delete[] ValidFlags; + ValidFlags=new bool[ND + NR]; + + for (uint I = 0; I < ND + NR; I++) + ValidFlags[I]=ValidityFlags[I]; + for (uint I = 0; I < ND; I++) + if (!ValidFlags[I]) + NE++; + uint ValidECC=0; + for (uint I = ND; I < ND + NR; I++) + if (ValidFlags[I]) + ValidECC++; + if (NE > ValidECC || NE == 0 || ValidECC == 0) + return false; + } + + // 2021.09.01 - we allowed RR and REV >100%, so no more NR > ND check. + if (ND + NR > gfSize || /*NR > ND ||*/ ND == 0 || NR == 0) + return false; + + delete[] MX; + if (Decoding) + { + MX=new uint[NE * ND]; + MakeDecoderMatrix(); + InvertDecoderMatrix(); + } + else + { + MX=new uint[NR * ND]; + MakeEncoderMatrix(); + } + return true; +} + + +void RSCoder16::MakeEncoderMatrix() +{ + // Create Cauchy encoder generator matrix. Skip trivial "1" diagonal rows, + // which would just copy source data to destination. + for (uint I = 0; I < NR; I++) + for (uint J = 0; J < ND; J++) + MX[I * ND + J] = gfInv( gfAdd( (I+ND), J) ); +} + + +void RSCoder16::MakeDecoderMatrix() +{ + // Create Cauchy decoder matrix. Skip trivial rows matching valid data + // units and containing "1" on main diagonal. Such rows would just copy + // source data to destination and they have no real value for us. + // Include rows only for broken data units and replace them by first + // available valid recovery code rows. + for (uint Flag=0, R=ND, Dest=0; Flag < ND; Flag++) + if (!ValidFlags[Flag]) // For every broken data unit. + { + while (!ValidFlags[R]) // Find a valid recovery unit. + R++; + for (uint J = 0; J < ND; J++) // And place its row to matrix. + MX[Dest*ND + J] = gfInv( gfAdd(R,J) ); + Dest++; + R++; + } +} + + +// Apply Gauss–Jordan elimination to find inverse of decoder matrix. +// We have the square NDxND matrix, but we do not store its trivial +// diagonal "1" rows matching valid data, so we work with NExND matrix. +// Our original Cauchy matrix does not contain 0, so we skip search +// for non-zero pivot. +void RSCoder16::InvertDecoderMatrix() +{ + uint *MI=new uint[NE * ND]; // We'll create inverse matrix here. + memset(MI, 0, ND * NE * sizeof(*MI)); // Initialize to identity matrix. + for (uint Kr = 0, Kf = 0; Kr < NE; Kr++, Kf++) + { + while (ValidFlags[Kf]) // Skip trivial rows. + Kf++; + MI[Kr * ND + Kf] = 1; // Set diagonal 1. + } + + // Kr is the number of row in our actual reduced NE x ND matrix, + // which does not contain trivial diagonal 1 rows. + // Kf is the number of row in full ND x ND matrix with all trivial rows + // included. + for (uint Kr = 0, Kf = 0; Kf < ND; Kr++, Kf++) // Select pivot row. + { + while (ValidFlags[Kf] && Kf < ND) + { + // Here we process trivial diagonal 1 rows matching valid data units. + // Their processing can be simplified comparing to usual rows. + // In full version of elimination we would set MX[I * ND + Kf] to zero + // after MI[..]^=, but we do not need it for matrix inversion. + for (uint I = 0; I < NE; I++) + MI[I * ND + Kf] ^= MX[I * ND + Kf]; + Kf++; + } + + if (Kf == ND) + break; + + uint *MXk = MX + Kr * ND; // k-th row of main matrix. + uint *MIk = MI + Kr * ND; // k-th row of inversion matrix. + + uint PInv = gfInv( MXk[Kf] ); // Pivot inverse. + // Divide the pivot row by pivot, so pivot cell contains 1. + for (uint I = 0; I < ND; I++) + { + MXk[I] = gfMul( MXk[I], PInv ); + MIk[I] = gfMul( MIk[I], PInv ); + } + + for (uint I = 0; I < NE; I++) + if (I != Kr) // For all rows except containing the pivot cell. + { + // Apply Gaussian elimination Mij -= Mkj * Mik / pivot. + // Since pivot is already 1, it is reduced to Mij -= Mkj * Mik. + uint *MXi = MX + I * ND; // i-th row of main matrix. + uint *MIi = MI + I * ND; // i-th row of inversion matrix. + uint Mik = MXi[Kf]; // Cell in pivot position. + for (uint J = 0; J < ND; J++) + { + MXi[J] ^= gfMul(MXk[J] , Mik); + MIi[J] ^= gfMul(MIk[J] , Mik); + } + } + } + + // Copy data to main matrix. + for (uint I = 0; I < NE * ND; I++) + MX[I] = MI[I]; + + delete[] MI; +} + + +#if 0 +// Multiply matrix to data vector. When encoding, it contains data in Data +// and stores error correction codes in Out. When decoding it contains +// broken data followed by ECC in Data and stores recovered data to Out. +// We do not use this function now, everything is moved to UpdateECC. +void RSCoder16::Process(const uint *Data, uint *Out) +{ + uint ProcData[gfSize]; + + for (uint I = 0; I < ND; I++) + ProcData[I]=Data[I]; + + if (Decoding) + { + // Replace broken data units with first available valid recovery codes. + // 'Data' array must contain recovery codes after data. + for (uint I=0, R=ND, Dest=0; I < ND; I++) + if (!ValidFlags[I]) // For every broken data unit. + { + while (!ValidFlags[R]) // Find a valid recovery unit. + R++; + ProcData[I]=Data[R]; + R++; + } + } + + uint H=Decoding ? NE : NR; + for (uint I = 0; I < H; I++) + { + uint R = 0; // Result of matrix row multiplication to data. + + uint *MXi=MX + I * ND; + for (uint J = 0; J < ND; J++) + R ^= gfMul(MXi[J], ProcData[J]); + + Out[I] = R; + } +} +#endif + + +// We update ECC in blocks by applying every data block to all ECC blocks. +// This function applies one data block to one ECC block. +void RSCoder16::UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize) +{ + if (DataNum==0) // Init ECC data. + memset(ECC, 0, BlockSize); + + bool DirectAccess; +#ifdef LITTLE_ENDIAN + // We can access data and ECC directly if we have little endian 16 bit uint. + DirectAccess=sizeof(ushort)==2; +#else + DirectAccess=false; +#endif + +#ifdef USE_SSE + if (DirectAccess && SSE_UpdateECC(DataNum,ECCNum,Data,ECC,BlockSize)) + return; +#endif + + if (ECCNum==0) + { + if (DataLogSize!=BlockSize) + { + delete[] DataLog; + DataLog=new uint[BlockSize]; + DataLogSize=BlockSize; + + } + if (DirectAccess) + for (size_t I=0; I<BlockSize; I+=2) + DataLog[I] = gfLog[ *(ushort*)(Data+I) ]; + else + for (size_t I=0; I<BlockSize; I+=2) + { + uint D=Data[I]+Data[I+1]*256; + DataLog[I] = gfLog[ D ]; + } + } + + uint ML = gfLog[ MX[ECCNum * ND + DataNum] ]; + + if (DirectAccess) + for (size_t I=0; I<BlockSize; I+=2) + *(ushort*)(ECC+I) ^= gfExp[ ML + DataLog[I] ]; + else + for (size_t I=0; I<BlockSize; I+=2) + { + uint R=gfExp[ ML + DataLog[I] ]; + ECC[I]^=byte(R); + ECC[I+1]^=byte(R/256); + } +} + + +#ifdef USE_SSE +// Data and ECC addresses must be properly aligned for SSE. +// AVX2 did not provide a noticeable speed gain on i7-6700K here. +bool RSCoder16::SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize) +{ + // Check data alignment and SSSE3 support. + if ((size_t(Data) & (SSE_ALIGNMENT-1))!=0 || (size_t(ECC) & (SSE_ALIGNMENT-1))!=0 || + _SSE_Version<SSE_SSSE3) + return false; + + uint M=MX[ECCNum * ND + DataNum]; + + // Prepare tables containing products of M and 4, 8, 12, 16 bit length + // numbers, which have 4 high bits in 0..15 range and other bits set to 0. + // Store high and low bytes of resulting 16 bit product in separate tables. + __m128i T0L,T1L,T2L,T3L; // Low byte tables. + __m128i T0H,T1H,T2H,T3H; // High byte tables. + + for (uint I=0;I<16;I++) + { + ((byte *)&T0L)[I]=gfMul(I,M); + ((byte *)&T0H)[I]=gfMul(I,M)>>8; + ((byte *)&T1L)[I]=gfMul(I<<4,M); + ((byte *)&T1H)[I]=gfMul(I<<4,M)>>8; + ((byte *)&T2L)[I]=gfMul(I<<8,M); + ((byte *)&T2H)[I]=gfMul(I<<8,M)>>8; + ((byte *)&T3L)[I]=gfMul(I<<12,M); + ((byte *)&T3H)[I]=gfMul(I<<12,M)>>8; + } + + size_t Pos=0; + + __m128i LowByteMask=_mm_set1_epi16(0xff); // 00ff00ff...00ff + __m128i Low4Mask=_mm_set1_epi8(0xf); // 0f0f0f0f...0f0f + __m128i High4Mask=_mm_slli_epi16(Low4Mask,4); // f0f0f0f0...f0f0 + + for (; Pos+2*sizeof(__m128i)<=BlockSize; Pos+=2*sizeof(__m128i)) + { + // We process two 128 bit chunks of source data at once. + __m128i *D=(__m128i *)(Data+Pos); + + // Place high bytes of both chunks to one variable and low bytes to + // another, so we can use the table lookup multiplication for 16 values + // 4 bit length each at once. + __m128i HighBytes0=_mm_srli_epi16(D[0],8); + __m128i LowBytes0=_mm_and_si128(D[0],LowByteMask); + __m128i HighBytes1=_mm_srli_epi16(D[1],8); + __m128i LowBytes1=_mm_and_si128(D[1],LowByteMask); + __m128i HighBytes=_mm_packus_epi16(HighBytes0,HighBytes1); + __m128i LowBytes=_mm_packus_epi16(LowBytes0,LowBytes1); + + // Multiply bits 0..3 of low bytes. Store low and high product bytes + // separately in cumulative sum variables. + __m128i LowBytesLow4=_mm_and_si128(LowBytes,Low4Mask); + __m128i LowBytesMultSum=_mm_shuffle_epi8(T0L,LowBytesLow4); + __m128i HighBytesMultSum=_mm_shuffle_epi8(T0H,LowBytesLow4); + + // Multiply bits 4..7 of low bytes. Store low and high product bytes separately. + __m128i LowBytesHigh4=_mm_and_si128(LowBytes,High4Mask); + LowBytesHigh4=_mm_srli_epi16(LowBytesHigh4,4); + __m128i LowBytesHigh4MultLow=_mm_shuffle_epi8(T1L,LowBytesHigh4); + __m128i LowBytesHigh4MultHigh=_mm_shuffle_epi8(T1H,LowBytesHigh4); + + // Add new product to existing sum, low and high bytes separately. + LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,LowBytesHigh4MultLow); + HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,LowBytesHigh4MultHigh); + + // Multiply bits 0..3 of high bytes. Store low and high product bytes separately. + __m128i HighBytesLow4=_mm_and_si128(HighBytes,Low4Mask); + __m128i HighBytesLow4MultLow=_mm_shuffle_epi8(T2L,HighBytesLow4); + __m128i HighBytesLow4MultHigh=_mm_shuffle_epi8(T2H,HighBytesLow4); + + // Add new product to existing sum, low and high bytes separately. + LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesLow4MultLow); + HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesLow4MultHigh); + + // Multiply bits 4..7 of high bytes. Store low and high product bytes separately. + __m128i HighBytesHigh4=_mm_and_si128(HighBytes,High4Mask); + HighBytesHigh4=_mm_srli_epi16(HighBytesHigh4,4); + __m128i HighBytesHigh4MultLow=_mm_shuffle_epi8(T3L,HighBytesHigh4); + __m128i HighBytesHigh4MultHigh=_mm_shuffle_epi8(T3H,HighBytesHigh4); + + // Add new product to existing sum, low and high bytes separately. + LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,HighBytesHigh4MultLow); + HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,HighBytesHigh4MultHigh); + + // Combine separate low and high cumulative sum bytes to 16-bit words. + __m128i HighBytesHigh4Mult0=_mm_unpacklo_epi8(LowBytesMultSum,HighBytesMultSum); + __m128i HighBytesHigh4Mult1=_mm_unpackhi_epi8(LowBytesMultSum,HighBytesMultSum); + + // Add result to ECC. + __m128i *StoreECC=(__m128i *)(ECC+Pos); + + StoreECC[0]=_mm_xor_si128(StoreECC[0],HighBytesHigh4Mult0); + StoreECC[1]=_mm_xor_si128(StoreECC[1],HighBytesHigh4Mult1); + } + + // If we have non 128 bit aligned data in the end of block, process them + // in a usual way. We cannot do the same in the beginning of block, + // because Data and ECC can have different alignment offsets. + for (; Pos<BlockSize; Pos+=2) + *(ushort*)(ECC+Pos) ^= gfMul( M, *(ushort*)(Data+Pos) ); + + return true; +} +#endif diff --git a/unrar/unrar/rs16.hpp b/unrar/unrar/rs16.hpp new file mode 100644 index 0000000..b67a7ca --- /dev/null +++ b/unrar/unrar/rs16.hpp @@ -0,0 +1,44 @@ +#ifndef _RAR_RS16_ +#define _RAR_RS16_ + +class RSCoder16 +{ + private: + static const uint gfSize=65535; // Galois field size. + void gfInit(); // Galois field inititalization. + inline uint gfAdd(uint a,uint b); // Addition in Galois field. + inline uint gfMul(uint a,uint b); // Multiplication in Galois field. + inline uint gfInv(uint a); // Inverse element in Galois field. + uint *gfExp; // Galois field exponents. + uint *gfLog; // Galois field logarithms. + + void MakeEncoderMatrix(); + void MakeDecoderMatrix(); + void InvertDecoderMatrix(); + +#ifdef USE_SSE + bool SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize); +#endif + + bool Decoding; // If we are decoding or encoding data. + uint ND; // Number of data units. + uint NR; // Number of Reed-Solomon code units. + uint NE; // Number of erasures. + bool *ValidFlags; // Validity flags for data and ECC units. + uint *MX; // Cauchy based coding or decoding matrix. + + uint *DataLog; // Buffer to store data logarithms for UpdateECC. + size_t DataLogSize; + + public: + RSCoder16(); + ~RSCoder16(); + + bool Init(uint DataCount, uint RecCount, bool *ValidityFlags); +#if 0 // We use only UpdateECC now. + void Process(const uint *Data, uint *Out); +#endif + void UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte *ECC, size_t BlockSize); +}; + +#endif diff --git a/unrar/unrar/savepos.cpp b/unrar/unrar/savepos.cpp deleted file mode 100644 index e46c4e6..0000000 --- a/unrar/unrar/savepos.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "rar.hpp" - -SaveFilePos::SaveFilePos(File &SaveFile) -{ - SaveFilePos::SaveFile=&SaveFile; - SavePos=SaveFile.Tell(); - CloseCount=SaveFile.CloseCount; -} - - -SaveFilePos::~SaveFilePos() -{ - if (CloseCount==SaveFile->CloseCount) - SaveFile->Seek(SavePos,SEEK_SET); -} diff --git a/unrar/unrar/savepos.hpp b/unrar/unrar/savepos.hpp deleted file mode 100644 index 303550a..0000000 --- a/unrar/unrar/savepos.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _RAR_SAVEPOS_ -#define _RAR_SAVEPOS_ - -class SaveFilePos -{ - private: - File *SaveFile; - Int64 SavePos; - uint CloseCount; - public: - SaveFilePos(File &SaveFile); - ~SaveFilePos(); -}; - -#endif diff --git a/unrar/unrar/scantree.cpp b/unrar/unrar/scantree.cpp index cfd2ef7..dbaf1e4 100644 --- a/unrar/unrar/scantree.cpp +++ b/unrar/unrar/scantree.cpp @@ -1,6 +1,6 @@ #include "rar.hpp" -ScanTree::ScanTree(StringList *FileMasks,int Recurse,bool GetLinks,int GetDirs) +ScanTree::ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs) { ScanTree::FileMasks=FileMasks; ScanTree::Recurse=Recurse; @@ -8,16 +8,17 @@ ScanTree::ScanTree(StringList *FileMasks,int Recurse,bool GetLinks,int GetDirs) ScanTree::GetDirs=GetDirs; ScanEntireDisk=false; + FolderWildcards=false; SetAllMaskDepth=0; *CurMask=0; - *CurMaskW=0; memset(FindStack,0,sizeof(FindStack)); Depth=0; Errors=0; - FastFindFile=false; *ErrArcName=0; Cmd=NULL; + ErrDirList=NULL; + ErrDirSpecPathLength=NULL; } @@ -29,17 +30,30 @@ ScanTree::~ScanTree() } -int ScanTree::GetNext(FindData *FindData) +SCAN_CODE ScanTree::GetNext(FindData *FD) { if (Depth<0) - return(SCAN_DONE); + return SCAN_DONE; - int FindCode; +#ifndef SILENT + uint LoopCount=0; +#endif + + SCAN_CODE FindCode; while (1) { - if ((*CurMask==0 || FastFindFile && Depth==0) && !PrepareMasks()) - return(SCAN_DONE); - FindCode=FindProc(FindData); + if (*CurMask==0 && !GetNextMask()) + return SCAN_DONE; + +#ifndef SILENT + // Let's return some ticks to system or WinRAR can become irresponsible + // while scanning files in command like "winrar a -r arc c:\file.ext". + // Also we reset system sleep timer here. + if ((++LoopCount & 0x3ff)==0) + Wait(); +#endif + + FindCode=FindProc(FD); if (FindCode==SCAN_ERROR) { Errors++; @@ -47,258 +61,445 @@ int ScanTree::GetNext(FindData *FindData) } if (FindCode==SCAN_NEXT) continue; - if (FindCode==SCAN_SUCCESS && FindData->IsDir && GetDirs==SCAN_SKIPDIRS) + if (FindCode==SCAN_SUCCESS && FD->IsDir && GetDirs==SCAN_SKIPDIRS) continue; - if (FindCode==SCAN_DONE && PrepareMasks()) + if (FindCode==SCAN_DONE && GetNextMask()) continue; + if (FilterList.ItemsCount()>0 && FindCode==SCAN_SUCCESS) + if (!CommandData::CheckArgs(&FilterList,FD->IsDir,FD->Name,false,MATCH_WILDSUBPATH)) + continue; break; } - return(FindCode); + return FindCode; } -bool ScanTree::PrepareMasks() +// For masks like dir1\dir2*\*.ext in non-recursive mode. +bool ScanTree::ExpandFolderMask() { - ScanEntireDisk=false; - if (!FileMasks->GetString(CurMask,CurMaskW,sizeof(CurMask))) - return(false); - CurMask[ASIZE(CurMask)-1]=0; - CurMaskW[ASIZE(CurMaskW)-1]=0; -#ifdef _WIN_32 - UnixSlashToDos(CurMask); -#endif + bool WildcardFound=false; + uint SlashPos=0; + for (int I=0;CurMask[I]!=0;I++) + { + if (CurMask[I]=='?' || CurMask[I]=='*') + WildcardFound=true; + if (WildcardFound && IsPathDiv(CurMask[I])) + { + // First path separator position after folder wildcard mask. + // In case of dir1\dir2*\dir3\name.ext mask it may point not to file + // name, so we cannot use PointToName() here. + SlashPos=I; + break; + } + } - // We wish to scan entire disk if mask like c:\ is specified - // regardless of recursion mode. Use c:\*.* mask when need to scan only - // the root directory. - ScanEntireDisk=IsDiskLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + wchar Mask[NM]; + wcsncpyz(Mask,CurMask,ASIZE(Mask)); + Mask[SlashPos]=0; + + // Prepare the list of all folders matching the wildcard mask. + ExpandedFolderList.Reset(); + FindFile Find; + Find.SetMask(Mask); + FindData FD; + while (Find.Next(&FD)) + if (FD.IsDir) + { + wcsncatz(FD.Name,CurMask+SlashPos,ASIZE(FD.Name)); - char *Name=PointToName(CurMask); - if (*Name==0) - strcat(CurMask,MASKALL); - if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0)) + // Treat dir*\* or dir*\*.* as dir, so empty 'dir' is also matched + // by such mask. Skipping empty dir with dir*\*.* confused some users. + wchar *LastMask=PointToName(FD.Name); + if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) + RemoveNameFromPath(FD.Name); + + ExpandedFolderList.AddString(FD.Name); + } + if (ExpandedFolderList.ItemsCount()==0) + return false; + // Return the first matching folder name now. + ExpandedFolderList.GetString(CurMask,ASIZE(CurMask)); + return true; +} + + +// For masks like dir1\dir2*\file.ext this function sets 'dir1' recursive mask +// and '*\dir2*\file.ext' filter. Masks without folder wildcards are +// returned as is. +bool ScanTree::GetFilteredMask() +{ + // If we have some matching folders left for non-recursive folder wildcard + // mask, we return it here. + if (ExpandedFolderList.ItemsCount()>0 && ExpandedFolderList.GetString(CurMask,ASIZE(CurMask))) + return true; + + FolderWildcards=false; + FilterList.Reset(); + if (!FileMasks->GetString(CurMask,ASIZE(CurMask))) + return false; + + // Check if folder wildcards present. + bool WildcardFound=false; + uint FolderWildcardCount=0; + uint SlashPos=0; + uint StartPos=0; +#ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard. + if (CurMask[0]=='\\' && CurMask[1]=='\\' && CurMask[2]=='?' && CurMask[3]=='\\') + StartPos=4; +#endif + for (uint I=StartPos;CurMask[I]!=0;I++) { - AddEndSlash(CurMask); - strcat(CurMask,MASKALL); + if (CurMask[I]=='?' || CurMask[I]=='*') + WildcardFound=true; + if (IsPathDiv(CurMask[I]) || IsDriveDiv(CurMask[I])) + { + if (WildcardFound) + { + // Calculate a number of folder wildcards in current mask. + FolderWildcardCount++; + WildcardFound=false; + } + if (FolderWildcardCount==0) + SlashPos=I; // Slash position before first folder wildcard mask. + } } - SpecPathLength=Name-CurMask; -// if (SpecPathLength>1) -// SpecPathLength--; + if (FolderWildcardCount==0) + return true; + FolderWildcards=true; // Global folder wildcards flag. + + // If we have only one folder wildcard component and -r is missing or -r- + // is specified, prepare matching folders in non-recursive mode. + // We assume -r for masks like dir1*\dir2*\file*, because it is complicated + // to fast find them using OS file find API call. + if ((Recurse==RECURSE_NONE || Recurse==RECURSE_DISABLE) && FolderWildcardCount==1) + return ExpandFolderMask(); + + wchar Filter[NM]; + // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders. + wcsncpyz(Filter,L"*",ASIZE(Filter)); + AddEndSlash(Filter,ASIZE(Filter)); + // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*' + wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos; + wcsncatz(Filter,WildName,ASIZE(Filter)); + + // Treat dir*\* or dir*\*.* as dir\, so empty 'dir' is also matched + // by such mask. Skipping empty dir with dir*\*.* confused some users. + wchar *LastMask=PointToName(Filter); + if (wcscmp(LastMask,L"*")==0 || wcscmp(LastMask,L"*.*")==0) + *LastMask=0; + + FilterList.AddString(Filter); + + bool RelativeDrive=IsDriveDiv(CurMask[SlashPos]); + if (RelativeDrive) + SlashPos++; // Use "d:" instead of "d" for d:* mask. + + CurMask[SlashPos]=0; + + if (!RelativeDrive) // Keep d: mask as is, not convert to d:\* + { + // We need to append "\*" both for -ep1 to work correctly and to + // convert d:\* masks previously truncated to d: back to original form. + AddEndSlash(CurMask,ASIZE(CurMask)); + wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); + } + return true; +} + - bool WideName=(*CurMaskW!=0); +bool ScanTree::GetNextMask() +{ + if (!GetFilteredMask()) + return false; +#ifdef _WIN_ALL + UnixSlashToDos(CurMask,CurMask,ASIZE(CurMask)); +#endif - if (WideName) + // We prefer to scan entire disk if mask like \\server\share\ or c:\ + // is specified regardless of recursion mode. Use \\server\share\*.* + // or c:\*.* mask to scan only the root directory. + if (CurMask[0]=='\\' && CurMask[1]=='\\') { - wchar *NameW=PointToName(CurMaskW); - if (*NameW==0) - strcatw(CurMaskW,MASKALLW); - if (NameW[0]=='.' && (NameW[1]==0 || NameW[1]=='.' && NameW[2]==0)) + const wchar *Slash=wcschr(CurMask+2,'\\'); + if (Slash!=NULL) { - AddEndSlash(CurMaskW); - strcatw(CurMaskW,MASKALLW); + Slash=wcschr(Slash+1,'\\'); + ScanEntireDisk=Slash!=NULL && *(Slash+1)==0; } - SpecPathLengthW=NameW-CurMaskW; } else + ScanEntireDisk=IsDriveLetter(CurMask) && IsPathDiv(CurMask[2]) && CurMask[3]==0; + + + wchar *Name=PointToName(CurMask); + if (*Name==0) + wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); + if (Name[0]=='.' && (Name[1]==0 || Name[1]=='.' && Name[2]==0)) { - wchar WideMask[NM]; - CharToWide(CurMask,WideMask); - SpecPathLengthW=PointToName(WideMask)-WideMask; + AddEndSlash(CurMask,ASIZE(CurMask)); + wcsncatz(CurMask,MASKALL,ASIZE(CurMask)); } + SpecPathLength=Name-CurMask; Depth=0; - strcpy(OrigCurMask,CurMask); - strcpyw(OrigCurMaskW,CurMaskW); + wcsncpyz(OrigCurMask,CurMask,ASIZE(OrigCurMask)); - return(true); + return true; } -int ScanTree::FindProc(FindData *FindData) +SCAN_CODE ScanTree::FindProc(FindData *FD) { if (*CurMask==0) - return(SCAN_NEXT); - FastFindFile=false; - if (FindStack[Depth]==NULL) + return SCAN_NEXT; + bool FastFindFile=false; + + if (FindStack[Depth]==NULL) // No FindFile object for this depth yet. { - bool Wildcards=IsWildcard(CurMask,CurMaskW); - bool FindCode=!Wildcards && FindFile::FastFind(CurMask,CurMaskW,FindData,GetLinks); - bool IsDir=FindCode && FindData->IsDir; + bool Wildcards=IsWildcard(CurMask); + + // If we have a file name without wildcards, we can try to use + // FastFind to optimize speed. For example, in Unix it results in + // stat call instead of opendir/readdir/closedir. + bool FindCode=!Wildcards && FindFile::FastFind(CurMask,FD,GetLinks); + + // Link check is important for NTFS, where links can have "Directory" + // attribute, but we do not want to recurse to them in "get links" mode. + bool IsDir=FindCode && FD->IsDir && (!GetLinks || !FD->IsLink); + + // SearchAll means that we'll use "*" mask for search, so we'll find + // subdirectories and will be able to recurse into them. + // We do not use "*" for directories at any level or for files + // at top level in recursion mode. We always comrpess the entire directory + // if folder wildcard is specified. bool SearchAll=!IsDir && (Depth>0 || Recurse==RECURSE_ALWAYS || - Wildcards && Recurse==RECURSE_WILDCARDS || ScanEntireDisk); + FolderWildcards && Recurse!=RECURSE_DISABLE || + Wildcards && Recurse==RECURSE_WILDCARDS || + ScanEntireDisk && Recurse!=RECURSE_DISABLE); if (Depth==0) SearchAllInRoot=SearchAll; if (SearchAll || Wildcards) { + // Create the new FindFile object for wildcard based search. FindStack[Depth]=new FindFile; - char SearchMask[NM]; - strcpy(SearchMask,CurMask); + + wchar SearchMask[NM]; + wcsncpyz(SearchMask,CurMask,ASIZE(SearchMask)); if (SearchAll) - strcpy(PointToName(SearchMask),MASKALL); + SetName(SearchMask,MASKALL,ASIZE(SearchMask)); FindStack[Depth]->SetMask(SearchMask); - if (*CurMaskW) - { - wchar SearchMaskW[NM]; - strcpyw(SearchMaskW,CurMaskW); - if (SearchAll) - strcpyw(PointToName(SearchMaskW),MASKALLW); - FindStack[Depth]->SetMaskW(SearchMaskW); - } } else { - FastFindFile=true; - if (!FindCode) + // Either we failed to fast find or we found a file or we found + // a directory in RECURSE_DISABLE mode, so we do not need to scan it. + // We can return here and do not need to process further. + // We need to process further only if we fast found a directory. + if (!FindCode || !IsDir || Recurse==RECURSE_DISABLE) { - if (Cmd!=NULL && Cmd->ExclCheck(CurMask,true)) - return(SCAN_NEXT); - ErrHandler.OpenErrorMsg(ErrArcName,CurMask); - return(FindData->Error ? SCAN_ERROR:SCAN_NEXT); + // Return SCAN_SUCCESS if we found a file. + SCAN_CODE RetCode=SCAN_SUCCESS; + + if (!FindCode) + { + // Return SCAN_ERROR if problem is more serious than just + // "file not found". + RetCode=FD->Error ? SCAN_ERROR:SCAN_NEXT; + + // If we failed to find an object, but our current mask is excluded, + // we skip this object and avoid indicating an error. + if (Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true)) + RetCode=SCAN_NEXT; + else + { + ErrHandler.OpenErrorMsg(ErrArcName,CurMask); + // User asked to return RARX_NOFILES and not RARX_OPEN here. + ErrHandler.SetErrorCode(RARX_NOFILES); + } + } + + // If we searched only for one file or directory in "fast find" + // (without a wildcard) mode, let's set masks to zero, + // so calling function will know that current mask is used + // and next one must be read from mask list for next call. + // It is not necessary for directories, because even in "fast find" + // mode, directory recursing will quit by (Depth < 0) condition, + // which returns SCAN_DONE to calling function. + *CurMask=0; + + return RetCode; } + + // We found a directory using only FindFile::FastFind function. + FastFindFile=true; } } - if (!FastFindFile && !FindStack[Depth]->Next(FindData,GetLinks)) + if (!FastFindFile && !FindStack[Depth]->Next(FD,GetLinks)) { - bool Error=FindData->Error; - -#ifdef _WIN_32 - if (Error) - { - // Do not display an error if we cannot scan contents of reparse - // point. Vista contains a lot of reparse (or junction) points, - // which are not accessible. - if ((FindData->FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) - Error=false; - - // Do not display an error if we cannot scan contents of - // "System Volume Information" folder. Normally it is not accessible. - if (strstr(CurMask,"System Volume Information\\")!=NULL) - Error=false; - } -#endif - - if (Cmd!=NULL && Cmd->ExclCheck(CurMask,true)) - Error=false; + // We cannot find anything more in directory either because of + // some error or just as result of all directory entries already read. -#ifndef SILENT + bool Error=FD->Error; if (Error) - { - Log(NULL,St(MScanError),CurMask); - } -#endif + ScanError(Error); - char DirName[NM]; - wchar DirNameW[NM]; + wchar DirName[NM]; *DirName=0; - *DirNameW=0; + // Going to at least one directory level higher. delete FindStack[Depth]; FindStack[Depth--]=NULL; while (Depth>=0 && FindStack[Depth]==NULL) Depth--; if (Depth < 0) { + // Directories scanned both in normal and FastFindFile mode, + // finally exit from scan here, by (Depth < 0) condition. + if (Error) Errors++; - return(SCAN_DONE); + return SCAN_DONE; } - char *Slash=strrchrd(CurMask,CPATHDIVIDER); + + wchar *Slash=wcsrchr(CurMask,CPATHDIVIDER); if (Slash!=NULL) { - char Mask[NM]; - strcpy(Mask,Slash); + wchar Mask[NM]; + wcsncpyz(Mask,Slash,ASIZE(Mask)); if (Depth<SetAllMaskDepth) - strcpy(Mask+1,PointToName(OrigCurMask)); + wcsncpyz(Mask+1,PointToName(OrigCurMask),ASIZE(Mask)-1); *Slash=0; - strcpy(DirName,CurMask); - char *PrevSlash=strrchrd(CurMask,CPATHDIVIDER); + wcsncpyz(DirName,CurMask,ASIZE(DirName)); + wchar *PrevSlash=wcsrchr(CurMask,CPATHDIVIDER); if (PrevSlash==NULL) - strcpy(CurMask,Mask+1); + wcsncpyz(CurMask,Mask+1,ASIZE(CurMask)); else - strcpy(PrevSlash,Mask); - } - - if (*CurMaskW!=0) - { - wchar *Slash=strrchrw(CurMaskW,CPATHDIVIDER); - if (Slash!=NULL) { - wchar Mask[NM]; - strcpyw(Mask,Slash); - if (Depth<SetAllMaskDepth) - strcpyw(Mask+1,PointToName(OrigCurMaskW)); - *Slash=0; - strcpyw(DirNameW,CurMaskW); - wchar *PrevSlash=strrchrw(CurMaskW,CPATHDIVIDER); - if (PrevSlash==NULL) - strcpyw(CurMaskW,Mask+1); - else - strcpyw(PrevSlash,Mask); + *PrevSlash=0; + wcsncatz(CurMask,Mask,ASIZE(CurMask)); } -#ifndef _WIN_CE - if (LowAscii(CurMaskW)) - *CurMaskW=0; -#endif } if (GetDirs==SCAN_GETDIRSTWICE && - FindFile::FastFind(DirName,DirNameW,FindData,GetLinks) && FindData->IsDir) + FindFile::FastFind(DirName,FD,GetLinks) && FD->IsDir) { - FindData->Flags|=FDDF_SECONDDIR; - return(Error ? SCAN_ERROR:SCAN_SUCCESS); + FD->Flags|=FDDF_SECONDDIR; + return Error ? SCAN_ERROR:SCAN_SUCCESS; } - return(Error ? SCAN_ERROR:SCAN_NEXT); + return Error ? SCAN_ERROR:SCAN_NEXT; } - if (FindData->IsDir) + // Link check is required for NTFS links, not for Unix. + if (FD->IsDir && (!GetLinks || !FD->IsLink)) { + // If we found the directory in top (Depth==0) directory + // and if we are not in "fast find" (directory name only as argument) + // or in recurse (SearchAll was set when opening the top directory) mode, + // we do not recurse into this directory. We either return it by itself + // or skip it. if (!FastFindFile && Depth==0 && !SearchAllInRoot) - return(GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT); - -// if (GetDirs==SCAN_GETCURDIRS && Depth==0 && !SearchAllInRoot) -// return(SCAN_SUCCESS); + return GetDirs==SCAN_GETCURDIRS ? SCAN_SUCCESS:SCAN_NEXT; - char Mask[NM]; - bool MaskAll=FastFindFile; + // Let's check if directory name is excluded, so we do not waste + // time searching in directory, which will be excluded anyway. + if (Cmd!=NULL && (Cmd->ExclCheck(FD->Name,true,false,false) || + Cmd->ExclDirByAttr(FD->FileAttr))) + { + // If we are here in "fast find" mode, it means that entire directory + // specified in command line is excluded. Then we need to return + // SCAN_DONE to go to next mask and avoid the infinite loop + // in GetNext() function. Such loop would be possible in case of + // SCAN_NEXT code and "rar a arc dir -xdir" command. -// bool MaskAll=CmpName(CurMask,FindData->Name,MATCH_NAMES); + return FastFindFile ? SCAN_DONE:SCAN_NEXT; + } + + wchar Mask[NM]; - strcpy(Mask,MaskAll ? MASKALL:PointToName(CurMask)); - strcpy(CurMask,FindData->Name); + wcsncpyz(Mask,FastFindFile ? MASKALL:PointToName(CurMask),ASIZE(Mask)); + wcsncpyz(CurMask,FD->Name,ASIZE(CurMask)); - if (strlen(CurMask)+strlen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1) + if (wcslen(CurMask)+wcslen(Mask)+1>=NM || Depth>=MAXSCANDEPTH-1) { -#ifndef SILENT - Log(NULL,"\n%s%c%s",CurMask,CPATHDIVIDER,Mask); - Log(NULL,St(MPathTooLong)); -#endif - return(SCAN_ERROR); + uiMsg(UIERROR_PATHTOOLONG,CurMask,SPATHDIVIDER,Mask); + return SCAN_ERROR; } - AddEndSlash(CurMask); - strcat(CurMask,Mask); + AddEndSlash(CurMask,ASIZE(CurMask)); + wcsncatz(CurMask,Mask,ASIZE(CurMask)); - if (*CurMaskW && *FindData->NameW==0) - CharToWide(FindData->Name,FindData->NameW); - if (*FindData->NameW!=0) - { - wchar Mask[NM]; - if (FastFindFile) - strcpyw(Mask,MASKALLW); - else - if (*CurMaskW) - strcpyw(Mask,PointToName(CurMaskW)); - else - CharToWide(PointToName(CurMask),Mask); - strcpyw(CurMaskW,FindData->NameW); - AddEndSlash(CurMaskW); - strcatw(CurMaskW,Mask); - } Depth++; - if (MaskAll) + + // We need to use OrigCurMask for depths less than SetAllMaskDepth + // and "*" for depths equal or larger than SetAllMaskDepth. + // It is important when "fast finding" directories at Depth > 0. + // For example, if current directory is RootFolder and we compress + // the following directories structure: + // RootFolder + // +--Folder1 + // | +--Folder2 + // | +--Folder3 + // +--Folder4 + // with 'rar a -r arcname Folder2' command, rar could add not only + // Folder1\Folder2 contents, but also Folder1\Folder3 if we were using + // "*" mask at all levels. We need to use "*" mask inside of Folder2, + // but return to "Folder2" mask when completing scanning Folder2. + // We can rewrite SearchAll expression above to avoid fast finding + // directories at Depth > 0, but then 'rar a -r arcname Folder2' + // will add the empty Folder2 and do not add its contents. + + if (FastFindFile) SetAllMaskDepth=Depth; } - if (!FastFindFile && !CmpName(CurMask,FindData->Name,MATCH_NAMES)) - return(SCAN_NEXT); - return(SCAN_SUCCESS); + if (!FastFindFile && !CmpName(CurMask,FD->Name,MATCH_NAMES)) + return SCAN_NEXT; + + return SCAN_SUCCESS; +} + + +void ScanTree::ScanError(bool &Error) +{ +#ifdef _WIN_ALL + if (Error) + { + // Get attributes of parent folder and do not display an error + // if it is reparse point. We cannot scan contents of standard + // Windows reparse points like "C:\Documents and Settings" + // and we do not want to issue numerous useless errors for them. + // We cannot just check FD->FileAttr here, it can be undefined + // if we process "folder\*" mask or if we process "folder" mask, + // but "folder" is inaccessible. + wchar *Slash=PointToName(CurMask); + if (Slash>CurMask) + { + *(Slash-1)=0; + DWORD Attr=GetFileAttributes(CurMask); + *(Slash-1)=CPATHDIVIDER; + if (Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) + Error=false; + } + + // Do not display an error if we cannot scan contents of + // "System Volume Information" folder. Normally it is not accessible. + if (wcsstr(CurMask,L"System Volume Information\\")!=NULL) + Error=false; + } +#endif + + if (Error && Cmd!=NULL && Cmd->ExclCheck(CurMask,false,true,true)) + Error=false; + + if (Error) + { + if (ErrDirList!=NULL) + ErrDirList->AddString(CurMask); + if (ErrDirSpecPathLength!=NULL) + ErrDirSpecPathLength->Push((uint)SpecPathLength); + wchar FullName[NM]; + // This conversion works for wildcard masks too. + ConvertNameToFull(CurMask,FullName,ASIZE(FullName)); + uiMsg(UIERROR_DIRSCAN,FullName); + ErrHandler.SysErrMsg(); + } } diff --git a/unrar/unrar/scantree.hpp b/unrar/unrar/scantree.hpp index 34d52fe..7ebe69a 100644 --- a/unrar/unrar/scantree.hpp +++ b/unrar/unrar/scantree.hpp @@ -1,9 +1,15 @@ #ifndef _RAR_SCANTREE_ #define _RAR_SCANTREE_ -enum { RECURSE_NONE=0,RECURSE_ALWAYS,RECURSE_WILDCARDS }; -enum { SCAN_SKIPDIRS=0,SCAN_GETDIRS,SCAN_GETDIRSTWICE,SCAN_GETCURDIRS }; -enum { SCAN_SUCCESS,SCAN_DONE,SCAN_ERROR,SCAN_NEXT }; +enum SCAN_DIRS +{ + SCAN_SKIPDIRS, // Skip directories, but recurse for files if recursion mode is enabled. + SCAN_GETDIRS, // Get subdirectories in recurse mode. + SCAN_GETDIRSTWICE, // Get the directory name both before and after the list of files it contains. + SCAN_GETCURDIRS // Get subdirectories in current directory even in RECURSE_NONE mode. +}; + +enum SCAN_CODE { SCAN_SUCCESS,SCAN_DONE,SCAN_ERROR,SCAN_NEXT }; #define MAXSCANDEPTH (NM/2) @@ -12,8 +18,11 @@ class CommandData; class ScanTree { private: - bool PrepareMasks(); - int FindProc(FindData *FindData); + bool ExpandFolderMask(); + bool GetFilteredMask(); + bool GetNextMask(); + SCAN_CODE FindProc(FindData *FD); + void ScanError(bool &Error); FindFile *FindStack[MAXSCANDEPTH]; int Depth; @@ -21,35 +30,49 @@ class ScanTree int SetAllMaskDepth; StringList *FileMasks; - int Recurse; + RECURSE_MODE Recurse; bool GetLinks; - int GetDirs; + SCAN_DIRS GetDirs; int Errors; - // set when processing paths like c:\ (root directory without wildcards) + // Set when processing paths like c:\ (root directory without wildcards). bool ScanEntireDisk; - char CurMask[NM]; - wchar CurMaskW[NM]; - char OrigCurMask[NM]; - wchar OrigCurMaskW[NM]; + wchar CurMask[NM]; + wchar OrigCurMask[NM]; + + // Store all folder masks generated from folder wildcard mask in non-recursive mode. + StringList ExpandedFolderList; + + // Store a filter string for folder wildcard in recursive mode. + StringList FilterList; + + // Save the list of unreadable dirs here. + StringList *ErrDirList; + Array<uint> *ErrDirSpecPathLength; + + // Set if processing a folder wildcard mask. + bool FolderWildcards; + bool SearchAllInRoot; - bool FastFindFile; - int SpecPathLength; - int SpecPathLengthW; + size_t SpecPathLength; - char ErrArcName[NM]; + wchar ErrArcName[NM]; CommandData *Cmd; public: - ScanTree(StringList *FileMasks,int Recurse,bool GetLinks,int GetDirs); + ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs); ~ScanTree(); - int GetNext(FindData *FindData); - int GetSpecPathLength() {return(SpecPathLength);}; - int GetSpecPathLengthW() {return(SpecPathLengthW);}; - int GetErrors() {return(Errors);}; - void SetErrArcName(const char *Name) {strcpy(ErrArcName,Name);} + SCAN_CODE GetNext(FindData *FindData); + size_t GetSpecPathLength() {return SpecPathLength;} + int GetErrors() {return Errors;}; + void SetErrArcName(const wchar *Name) {wcsncpyz(ErrArcName,Name,ASIZE(ErrArcName));} void SetCommandData(CommandData *Cmd) {ScanTree::Cmd=Cmd;} + void SetErrDirList(StringList *List,Array<uint> *Lengths) + { + ErrDirList=List; + ErrDirSpecPathLength=Lengths; + } }; #endif diff --git a/unrar/unrar/secpassword.cpp b/unrar/unrar/secpassword.cpp new file mode 100644 index 0000000..08da549 --- /dev/null +++ b/unrar/unrar/secpassword.cpp @@ -0,0 +1,218 @@ +#include "rar.hpp" + +#if defined(_WIN_ALL) +typedef BOOL (WINAPI *CRYPTPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags); +typedef BOOL (WINAPI *CRYPTUNPROTECTMEMORY)(LPVOID pData,DWORD cbData,DWORD dwFlags); + +#ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE +#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 +#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00 +#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x01 +#endif + +class CryptLoader +{ + private: + HMODULE hCrypt; + bool LoadCalled; + public: + CryptLoader() + { + hCrypt=NULL; + pCryptProtectMemory=NULL; + pCryptUnprotectMemory=NULL; + LoadCalled=false; + } + ~CryptLoader() + { + if (hCrypt!=NULL) + FreeLibrary(hCrypt); + hCrypt=NULL; + pCryptProtectMemory=NULL; + pCryptUnprotectMemory=NULL; + }; + void Load() + { + if (!LoadCalled) + { + hCrypt = LoadSysLibrary(L"Crypt32.dll"); + if (hCrypt != NULL) + { + // Available since Vista. + pCryptProtectMemory = (CRYPTPROTECTMEMORY)GetProcAddress(hCrypt, "CryptProtectMemory"); + pCryptUnprotectMemory = (CRYPTUNPROTECTMEMORY)GetProcAddress(hCrypt, "CryptUnprotectMemory"); + } + LoadCalled=true; + } + } + + CRYPTPROTECTMEMORY pCryptProtectMemory; + CRYPTUNPROTECTMEMORY pCryptUnprotectMemory; +}; + +// We need to call FreeLibrary when RAR is exiting. +static CryptLoader GlobalCryptLoader; +#endif + +SecPassword::SecPassword() +{ + Set(L""); +} + + +SecPassword::~SecPassword() +{ + Clean(); +} + + +void SecPassword::Clean() +{ + PasswordSet=false; + if (Password.size()>0) + cleandata(&Password[0],Password.size()*sizeof(Password[0])); +} + + +// When we call memset in end of function to clean local variables +// for security reason, compiler optimizer can remove such call. +// So we use our own function for this purpose. +void cleandata(void *data,size_t size) +{ + if (data==NULL || size==0) + return; +#if defined(_WIN_ALL) && defined(_MSC_VER) + SecureZeroMemory(data,size); +#else + // 'volatile' is required. Otherwise optimizers can remove this function + // if cleaning local variables, which are not used after that. + volatile byte *d = (volatile byte *)data; + for (size_t i=0;i<size;i++) + d[i]=0; +#endif +} + + +// We got a complain from user that it is possible to create WinRAR dump +// with "Create dump file" command in Windows Task Manager and then easily +// locate Unicode password string in the dump. It is unsecure if several +// people share the same computer and somebody left WinRAR copy with entered +// password. So we decided to obfuscate the password to make it more difficult +// to find it in dump. +void SecPassword::Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode) +{ + // Source string can be shorter than destination as in case when we process + // -p<pwd> parameter, so we need to take into account both sizes. + memcpy(Dst,Src,Min(SrcSize,DstSize)*sizeof(*Dst)); + SecHideData(Dst,DstSize*sizeof(*Dst),Encode,false); +} + + +void SecPassword::Get(wchar *Psw,size_t MaxSize) +{ + if (PasswordSet) + { + Process(&Password[0],Password.size(),Psw,MaxSize,false); + Psw[MaxSize-1]=0; + } + else + *Psw=0; +} + + + + +void SecPassword::Set(const wchar *Psw) +{ + // Eliminate any traces of previously stored password for security reason + // in case it was longer than new one. + Clean(); + + if (*Psw!=0) + { + PasswordSet=true; + Process(Psw,wcslen(Psw)+1,&Password[0],Password.size(),true); + } +} + + +size_t SecPassword::Length() +{ + wchar Plain[MAXPASSWORD]; + Get(Plain,ASIZE(Plain)); + size_t Length=wcslen(Plain); + cleandata(Plain,sizeof(Plain)); + return Length; +} + + +bool SecPassword::operator == (SecPassword &psw) +{ + // We cannot compare encoded data directly, because there is no guarantee + // than encryption function will always produce the same result for same + // data (salt?) and because we do not clean the rest of password buffer + // after trailing zero before encoding password. So we decode first. + wchar Plain1[MAXPASSWORD],Plain2[MAXPASSWORD]; + Get(Plain1,ASIZE(Plain1)); + psw.Get(Plain2,ASIZE(Plain2)); + bool Result=wcscmp(Plain1,Plain2)==0; + cleandata(Plain1,sizeof(Plain1)); + cleandata(Plain2,sizeof(Plain2)); + return Result; +} + + +// Set CrossProcess to true if we need to pass a password to another process. +// We use CrossProcess when transferring parameters to UAC elevated WinRAR +// and Windows GUI SFX modules. +void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess) +{ + // CryptProtectMemory is not available in UWP and CryptProtectData + // increases data size not allowing in place conversion. +#if defined(_WIN_ALL) + // Try to utilize the secure Crypt[Un]ProtectMemory if possible. + if (GlobalCryptLoader.pCryptProtectMemory==NULL) + GlobalCryptLoader.Load(); + size_t Aligned=DataSize-DataSize%CRYPTPROTECTMEMORY_BLOCK_SIZE; + DWORD Flags=CrossProcess ? CRYPTPROTECTMEMORY_CROSS_PROCESS : CRYPTPROTECTMEMORY_SAME_PROCESS; + if (Encode) + { + if (GlobalCryptLoader.pCryptProtectMemory!=NULL) + { + if (!GlobalCryptLoader.pCryptProtectMemory(Data,DWORD(Aligned),Flags)) + { + ErrHandler.GeneralErrMsg(L"CryptProtectMemory failed"); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return; + } + } + else + { + if (GlobalCryptLoader.pCryptUnprotectMemory!=NULL) + { + if (!GlobalCryptLoader.pCryptUnprotectMemory(Data,DWORD(Aligned),Flags)) + { + ErrHandler.GeneralErrMsg(L"CryptUnprotectMemory failed"); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return; + } + } +#endif + + // CryptProtectMemory is not available, so only slightly obfuscate data. + uint Key; +#ifdef _WIN_ALL + Key=GetCurrentProcessId(); +#elif defined(_UNIX) + Key=getpid(); +#else + Key=0; // Just an arbitrary value. +#endif + + for (size_t I=0;I<DataSize;I++) + *((byte *)Data+I)^=Key+I+75; +} diff --git a/unrar/unrar/secpassword.hpp b/unrar/unrar/secpassword.hpp new file mode 100644 index 0000000..5284bce --- /dev/null +++ b/unrar/unrar/secpassword.hpp @@ -0,0 +1,28 @@ +#ifndef _RAR_SECURE_PASSWORD_ +#define _RAR_SECURE_PASSWORD_ + +// Store a password securely (if data encryption is provided by OS) +// or obfuscated to make search for password in memory dump less trivial. +class SecPassword +{ + private: + void Process(const wchar *Src,size_t SrcSize,wchar *Dst,size_t DstSize,bool Encode); + + std::vector<wchar> Password = std::vector<wchar>(MAXPASSWORD); + bool PasswordSet; + public: + SecPassword(); + ~SecPassword(); + void Clean(); + void Get(wchar *Psw,size_t MaxSize); + void Set(const wchar *Psw); + bool IsSet() {return PasswordSet;} + size_t Length(); + bool operator == (SecPassword &psw); +}; + + +void cleandata(void *data,size_t size); +void SecHideData(void *Data,size_t DataSize,bool Encode,bool CrossProcess); + +#endif diff --git a/unrar/unrar/sha1.cpp b/unrar/unrar/sha1.cpp index 686ff94..562aadd 100644 --- a/unrar/unrar/sha1.cpp +++ b/unrar/unrar/sha1.cpp @@ -4,228 +4,201 @@ SHA-1 in C By Steve Reid <steve@edmweb.com> 100% Public Domain - -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ -#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) - #if defined(_M_IX86) || defined(_M_I86) || defined(__alpha) - #define LITTLE_ENDIAN - #else - #error "LITTLE_ENDIAN or BIG_ENDIAN must be defined" - #endif +#ifndef SFX_MODULE +#define SHA1_UNROLL #endif -/* #define SHA1HANDSOFF * Copies data before messing with it. */ - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #ifdef LITTLE_ENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ - |(rol(block->l[i],8)&0x00FF00FF)) +#define blk0(i) (block->l[i] = ByteSwap32(block->l[i])) #else #define blk0(i) block->l[i] #endif -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ +#define blk(i) (block->l[i&15] = rotl32(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);} -#define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);} -#define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);} -#define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);} -#define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);} - +#define R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);} +#define R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+blk(i)+0x5A827999+rotl32(v,5);w=rotl32(w,30);} +#define R2(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0x6ED9EBA1+rotl32(v,5);w=rotl32(w,30);} +#define R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rotl32(v,5);w=rotl32(w,30);} +#define R4(v,w,x,y,z,i) {z+=(w^x^y)+blk(i)+0xCA62C1D6+rotl32(v,5);w=rotl32(w,30);} /* Hash a single 512-bit block. This is the core of the algorithm. */ - -void SHA1Transform(uint32 state[5], unsigned char buffer[64], bool handsoff) +void SHA1Transform(uint32 state[5], uint32 workspace[16], const byte buffer[64], bool inplace) { uint32 a, b, c, d, e; - typedef union { + + union CHAR64LONG16 + { unsigned char c[64]; uint32 l[16]; -} CHAR64LONG16; -CHAR64LONG16* block; -static unsigned char workspace[64]; - if (handsoff) - { - block = (CHAR64LONG16*)workspace; - memcpy(block, buffer, 64); - } - else - block = (CHAR64LONG16*)buffer; -#ifdef SFX_MODULE - static int pos[80][5]; - static bool pinit=false; - if (!pinit) - { - for (int I=0,P=0;I<80;I++,P=(P ? P-1:4)) - { - pos[I][0]=P; - pos[I][1]=(P+1)%5; - pos[I][2]=(P+2)%5; - pos[I][3]=(P+3)%5; - pos[I][4]=(P+4)%5; - } - pinit=true; - } - uint32 s[5]; - for (int I=0;I<sizeof(s)/sizeof(s[0]);I++) - s[I]=state[I]; - - for (int I=0;I<16;I++) - R0(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I); - for (int I=16;I<20;I++) - R1(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I); - for (int I=20;I<40;I++) - R2(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I); - for (int I=40;I<60;I++) - R3(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I); - for (int I=60;I<80;I++) - R4(s[pos[I][0]],s[pos[I][1]],s[pos[I][2]],s[pos[I][3]],s[pos[I][4]],I); - - for (int I=0;I<sizeof(s)/sizeof(s[0]);I++) - state[I]+=s[I]; + } *block; + + if (inplace) + block = (CHAR64LONG16*)buffer; + else + { + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + } + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + +#ifdef SHA1_UNROLL + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); #else - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - - /* Wipe variables */ - a = b = c = d = e = 0; - memset(&a,0,sizeof(a)); + for (uint I=0;;I+=5) + { + R0(a,b,c,d,e, I+0); if (I==15) break; + R0(e,a,b,c,d, I+1); R0(d,e,a,b,c, I+2); + R0(c,d,e,a,b, I+3); R0(b,c,d,e,a, I+4); + } + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + for (uint I=20;I<=35;I+=5) + { + R2(a,b,c,d,e,I+0); R2(e,a,b,c,d,I+1); R2(d,e,a,b,c,I+2); + R2(c,d,e,a,b,I+3); R2(b,c,d,e,a,I+4); + } + for (uint I=40;I<=55;I+=5) + { + R3(a,b,c,d,e,I+0); R3(e,a,b,c,d,I+1); R3(d,e,a,b,c,I+2); + R3(c,d,e,a,b,I+3); R3(b,c,d,e,a,I+4); + } + for (uint I=60;I<=75;I+=5) + { + R4(a,b,c,d,e,I+0); R4(e,a,b,c,d,I+1); R4(d,e,a,b,c,I+2); + R4(c,d,e,a,b,I+3); R4(b,c,d,e,a,I+4); + } #endif + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; } /* Initialize new context */ - -void hash_initial(hash_context* context) +void sha1_init(sha1_context* context) { - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; + context->count = 0; + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; } /* Run your data through this. */ -void hash_process( hash_context * context, unsigned char * data, unsigned len, - bool handsoff ) +void sha1_process( sha1_context * context, const unsigned char * data, size_t len) { -unsigned int i, j; -uint blen = ((uint)len)<<3; - - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += blen) < blen ) context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer, handsoff); - for ( ; i + 63 < len; i += 64) { -#ifdef ALLOW_NOT_ALIGNED_INT - SHA1Transform(context->state, &data[i], handsoff); -#else - unsigned char buffer[64]; - memcpy(buffer,data+i,sizeof(buffer)); - SHA1Transform(context->state, buffer, handsoff); - memcpy(data+i,buffer,sizeof(buffer)); -#endif -#ifdef BIG_ENDIAN - if (!handsoff) - { - unsigned char *d=data+i; - for (int k=0;k<64;k+=4) - { - byte b0=d[k],b1=d[k+1]; - d[k]=d[k+3]; - d[k+1]=d[k+2]; - d[k+2]=b1; - d[k+3]=b0; - } - } -#endif - } - j = 0; + size_t i, j = (size_t)(context->count & 63); + context->count += len; + + if ((j + len) > 63) + { + memcpy(context->buffer+j, data, (i = 64-j)); + uint32 workspace[16]; + SHA1Transform(context->state, workspace, context->buffer, true); + for ( ; i + 63 < len; i += 64) + SHA1Transform(context->state, workspace, data+i, false); + j = 0; + } + else + i = 0; + if (len > i) + memcpy(context->buffer+j, data+i, len - i); +} + + +void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len) +{ + size_t i, j = (size_t)(context->count & 63); + context->count += len; + + if ((j + len) > 63) + { + memcpy(context->buffer+j, data, (i = 64-j)); + uint32 workspace[16]; + SHA1Transform(context->state, workspace, context->buffer, true); + for ( ; i + 63 < len; i += 64) + { + SHA1Transform(context->state, workspace, data+i, false); + for (uint k = 0; k < 16; k++) + RawPut4(workspace[k],(void*)(data+i+k*4)); } - else i = 0; - if (len > i) - memcpy(&context->buffer[j], &data[i], len - i); + j = 0; + } + else + i = 0; + if (len > i) + memcpy(context->buffer+j, data+i, len - i); } /* Add padding and return the message digest. */ - -void hash_final( hash_context* context, uint32 digest[5], bool handsoff) +void sha1_done( sha1_context* context, uint32 digest[5]) { -uint i, j; -unsigned char finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - unsigned char ch=(unsigned char)'\200'; - hash_process(context, &ch, 1, handsoff); - while ((context->count[0] & 504) != 448) { - ch=0; - hash_process(context, &ch, 1, handsoff); - } - hash_process(context, finalcount, 8, handsoff); /* Should cause a SHA1Transform() */ - for (i = 0; i < 5; i++) { - digest[i] = context->state[i] & 0xffffffff; + uint32 workspace[16]; + uint64 BitLength = context->count * 8; + uint BufPos = (uint)context->count & 0x3f; + context->buffer[BufPos++] = 0x80; // Padding the message with "1" bit. + + if (BufPos!=56) // We need 56 bytes block followed by 8 byte length. + { + if (BufPos>56) + { + while (BufPos<64) + context->buffer[BufPos++] = 0; + BufPos=0; } - /* Wipe variables */ - memset(&i,0,sizeof(i)); - memset(&j,0,sizeof(j)); - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(&finalcount, 0, 8); - if (handsoff) - SHA1Transform(context->state, context->buffer, true); + if (BufPos==0) + SHA1Transform(context->state, workspace, context->buffer, true); + memset(context->buffer+BufPos,0,56-BufPos); + } + + RawPutBE4((uint32)(BitLength>>32), context->buffer + 56); + RawPutBE4((uint32)(BitLength), context->buffer + 60); + + SHA1Transform(context->state, workspace, context->buffer, true); + + for (uint i = 0; i < 5; i++) + digest[i] = context->state[i]; + + /* Wipe variables */ + sha1_init(context); } diff --git a/unrar/unrar/sha1.hpp b/unrar/unrar/sha1.hpp index 155c62a..7c0b7fb 100644 --- a/unrar/unrar/sha1.hpp +++ b/unrar/unrar/sha1.hpp @@ -1,17 +1,15 @@ #ifndef _RAR_SHA1_ #define _RAR_SHA1_ -#define HW 5 - typedef struct { uint32 state[5]; - uint32 count[2]; + uint64 count; unsigned char buffer[64]; -} hash_context; +} sha1_context; -void hash_initial( hash_context * c ); -void hash_process( hash_context * c, unsigned char * data, unsigned len, - bool handsoff); -void hash_final( hash_context * c, uint32[HW], bool handsoff); +void sha1_init( sha1_context * c ); +void sha1_process(sha1_context * c, const byte *data, size_t len); +void sha1_process_rar29(sha1_context *context, const unsigned char *data, size_t len); +void sha1_done( sha1_context * c, uint32 digest[5] ); #endif diff --git a/unrar/unrar/sha256.cpp b/unrar/unrar/sha256.cpp new file mode 100644 index 0000000..f90d2c0 --- /dev/null +++ b/unrar/unrar/sha256.cpp @@ -0,0 +1,148 @@ +#include "rar.hpp" +#include "sha256.hpp" + +static const uint32 K[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +// SHA-256 functions. We could optimize Ch and Maj a little, +// but with no visible speed benefit. +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +// Sigma functions. +#define Sg0(x) (rotr32(x, 2) ^ rotr32(x,13) ^ rotr32(x, 22)) +#define Sg1(x) (rotr32(x, 6) ^ rotr32(x,11) ^ rotr32(x, 25)) +#define sg0(x) (rotr32(x, 7) ^ rotr32(x,18) ^ (x >> 3)) +#define sg1(x) (rotr32(x,17) ^ rotr32(x,19) ^ (x >> 10)) + +void sha256_init(sha256_context *ctx) +{ + ctx->H[0] = 0x6a09e667; // Set the initial hash value. + ctx->H[1] = 0xbb67ae85; + ctx->H[2] = 0x3c6ef372; + ctx->H[3] = 0xa54ff53a; + ctx->H[4] = 0x510e527f; + ctx->H[5] = 0x9b05688c; + ctx->H[6] = 0x1f83d9ab; + ctx->H[7] = 0x5be0cd19; + ctx->Count = 0; // Processed data counter. +} + + +static void sha256_transform(sha256_context *ctx) +{ + uint32 W[64]; // Words of message schedule. + uint32 v[8]; // FIPS a, b, c, d, e, f, g, h working variables. + + // Prepare message schedule. + for (uint I = 0; I < 16; I++) + W[I] = RawGetBE4(ctx->Buffer + I * 4); + for (uint I = 16; I < 64; I++) + W[I] = sg1(W[I-2]) + W[I-7] + sg0(W[I-15]) + W[I-16]; + + uint32 *H=ctx->H; + v[0]=H[0]; v[1]=H[1]; v[2]=H[2]; v[3]=H[3]; + v[4]=H[4]; v[5]=H[5]; v[6]=H[6]; v[7]=H[7]; + + for (uint I = 0; I < 64; I++) + { + uint T1 = v[7] + Sg1(v[4]) + Ch(v[4], v[5], v[6]) + K[I] + W[I]; + + // It is possible to eliminate variable copying if we unroll loop + // and rename variables every time. But my test did not show any speed + // gain on i7 for such full or partial unrolling. + v[7] = v[6]; + v[6] = v[5]; + v[5] = v[4]; + v[4] = v[3] + T1; + + // It works a little faster when moved here from beginning of loop. + uint T2 = Sg0(v[0]) + Maj(v[0], v[1], v[2]); + + v[3] = v[2]; + v[2] = v[1]; + v[1] = v[0]; + v[0] = T1 + T2; + } + + H[0]+=v[0]; H[1]+=v[1]; H[2]+=v[2]; H[3]+=v[3]; + H[4]+=v[4]; H[5]+=v[5]; H[6]+=v[6]; H[7]+=v[7]; +} + + +void sha256_process(sha256_context *ctx, const void *Data, size_t Size) +{ + const byte *Src=(const byte *)Data; + size_t BufPos = (uint)ctx->Count & 0x3f; + ctx->Count+=Size; + while (Size > 0) + { + size_t BufSpace=sizeof(ctx->Buffer)-BufPos; + size_t CopySize=Size>BufSpace ? BufSpace:Size; + + memcpy(ctx->Buffer+BufPos,Src,CopySize); + + Src+=CopySize; + BufPos+=CopySize; + Size-=CopySize; + if (BufPos == 64) + { + BufPos = 0; + sha256_transform(ctx); + } + } +} + + +void sha256_done(sha256_context *ctx, byte *Digest) +{ + uint64 BitLength = ctx->Count * 8; + uint BufPos = (uint)ctx->Count & 0x3f; + ctx->Buffer[BufPos++] = 0x80; // Padding the message with "1" bit. + + if (BufPos!=56) // We need 56 bytes block followed by 8 byte length. + { + if (BufPos>56) + { + while (BufPos<64) + ctx->Buffer[BufPos++] = 0; + BufPos=0; + } + if (BufPos==0) + sha256_transform(ctx); + memset(ctx->Buffer+BufPos,0,56-BufPos); + } + + RawPutBE4((uint32)(BitLength>>32), ctx->Buffer + 56); + RawPutBE4((uint32)(BitLength), ctx->Buffer + 60); + + sha256_transform(ctx); + + RawPutBE4(ctx->H[0], Digest + 0); + RawPutBE4(ctx->H[1], Digest + 4); + RawPutBE4(ctx->H[2], Digest + 8); + RawPutBE4(ctx->H[3], Digest + 12); + RawPutBE4(ctx->H[4], Digest + 16); + RawPutBE4(ctx->H[5], Digest + 20); + RawPutBE4(ctx->H[6], Digest + 24); + RawPutBE4(ctx->H[7], Digest + 28); + + sha256_init(ctx); +} diff --git a/unrar/unrar/sha256.hpp b/unrar/unrar/sha256.hpp new file mode 100644 index 0000000..b6837e7 --- /dev/null +++ b/unrar/unrar/sha256.hpp @@ -0,0 +1,17 @@ +#ifndef _RAR_SHA256_ +#define _RAR_SHA256_ + +#define SHA256_DIGEST_SIZE 32 + +typedef struct +{ + uint32 H[8]; + uint64 Count; + byte Buffer[64]; +} sha256_context; + +void sha256_init(sha256_context *ctx); +void sha256_process(sha256_context *ctx, const void *Data, size_t Size); +void sha256_done(sha256_context *ctx, byte *Digest); + +#endif diff --git a/unrar/unrar/smallfn.cpp b/unrar/unrar/smallfn.cpp index 51997c5..81259d0 100644 --- a/unrar/unrar/smallfn.cpp +++ b/unrar/unrar/smallfn.cpp @@ -1,23 +1,19 @@ #include "rar.hpp" -int ToPercent(Int64 N1,Int64 N2) +int ToPercent(int64 N1,int64 N2) { if (N2<N1) - return(100); - return(ToPercentUnlim(N1,N2)); + return 100; + return ToPercentUnlim(N1,N2); } -/* allows percent larger than 100 */ -int ToPercentUnlim(Int64 N1,Int64 N2) + +// Allows the percent larger than 100. +int ToPercentUnlim(int64 N1,int64 N2) { if (N2==0) - return(0); - return(int64to32(N1*100/N2)); + return 0; + return (int)(N1*100/N2); } -void RARInitData() -{ - InitCRC(); - ErrHandler.Clean(); -} diff --git a/unrar/unrar/smallfn.hpp b/unrar/unrar/smallfn.hpp index 83266f0..f53daa8 100644 --- a/unrar/unrar/smallfn.hpp +++ b/unrar/unrar/smallfn.hpp @@ -1,8 +1,8 @@ #ifndef _RAR_SMALLFN_ #define _RAR_SMALLFN_ -int ToPercent(Int64 N1,Int64 N2); -int ToPercentUnlim(Int64 N1,Int64 N2); +int ToPercent(int64 N1,int64 N2); +int ToPercentUnlim(int64 N1,int64 N2); void RARInitData(); #endif diff --git a/unrar/unrar/strfn.cpp b/unrar/unrar/strfn.cpp index 52b7a5f..7617f7a 100644 --- a/unrar/unrar/strfn.cpp +++ b/unrar/unrar/strfn.cpp @@ -2,200 +2,469 @@ const char *NullToEmpty(const char *Str) { - return(Str==NULL ? "":Str); + return Str==NULL ? "":Str; } const wchar *NullToEmpty(const wchar *Str) { - return(Str==NULL ? L"":Str); + return Str==NULL ? L"":Str; } -char *IntNameToExt(const char *Name) +void IntToExt(const char *Src,char *Dest,size_t DestSize) { - static char OutName[NM]; - IntToExt(Name,OutName); - return(OutName); -} - - -void ExtToInt(const char *Src,char *Dest) -{ -#if defined(_WIN_32) - CharToOem(Src,Dest); +#ifdef _WIN_ALL + // OemToCharBuff does not stop at 0, so let's check source length. + size_t SrcLength=strlen(Src)+1; + if (DestSize>SrcLength) + DestSize=SrcLength; + OemToCharBuffA(Src,Dest,(DWORD)DestSize); + Dest[DestSize-1]=0; #else if (Dest!=Src) - strcpy(Dest,Src); + strncpyz(Dest,Src,DestSize); #endif } -void IntToExt(const char *Src,char *Dest) +// Convert archived names and comments to Unicode. +// Allows user to select a code page in GUI. +void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding) { -#if defined(_WIN_32) - OemToChar(Src,Dest); -#else - if (Dest!=Src) - strcpy(Dest,Src); +#if defined(_WIN_ALL) // Console Windows RAR. + if (Encoding==ACTW_UTF8) + UtfToWide(Src,Dest,DestSize); + else + { + Array<char> NameA; + if (Encoding==ACTW_OEM) + { + NameA.Alloc(DestSize+1); + IntToExt(Src,&NameA[0],NameA.Size()); + Src=&NameA[0]; + } + CharToWide(Src,Dest,DestSize); + } +#else // RAR for Unix. + if (Encoding==ACTW_UTF8) + UtfToWide(Src,Dest,DestSize); + else + CharToWide(Src,Dest,DestSize); #endif + // Ensure that we return a zero terminate string for security reason. + // While [Jni]CharToWide might already do it, be protected in case of future + // changes in these functions. + if (DestSize>0) + Dest[DestSize-1]=0; } -char* strlower(char *Str) + + +int stricomp(const char *s1,const char *s2) { -#ifdef _WIN_32 - CharLower((LPTSTR)Str); +#ifdef _WIN_ALL + return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; #else - for (char *ChPtr=Str;*ChPtr;ChPtr++) - *ChPtr=(char)loctolower(*ChPtr); + while (toupper(*s1)==toupper(*s2)) + { + if (*s1==0) + return 0; + s1++; + s2++; + } + return s1 < s2 ? -1 : 1; #endif - return(Str); } -char* strupper(char *Str) +int strnicomp(const char *s1,const char *s2,size_t n) { -#ifdef _WIN_32 - CharUpper((LPTSTR)Str); +#ifdef _WIN_ALL + // If we specify 'n' exceeding the actual string length, CompareString goes + // beyond the trailing zero and compares garbage. So we need to limit 'n' + // to real string length. + // It is important to use strnlen (or memchr(...,0)) instead of strlen, + // because data can be not zero terminated. + size_t l1=Min(strnlen(s1,n),n); + size_t l2=Min(strnlen(s2,n),n); + return CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; #else - for (char *ChPtr=Str;*ChPtr;ChPtr++) - *ChPtr=(char)loctoupper(*ChPtr); + if (n==0) + return 0; + while (toupper(*s1)==toupper(*s2)) + { + if (*s1==0 || --n==0) + return 0; + s1++; + s2++; + } + return s1 < s2 ? -1 : 1; #endif - return(Str); } -int stricomp(const char *Str1,const char *Str2) +wchar* RemoveEOL(wchar *Str) { - char S1[NM*2],S2[NM*2]; - strncpyz(S1,Str1,ASIZE(S1)); - strncpyz(S2,Str2,ASIZE(S2)); - return(strcmp(strupper(S1),strupper(S2))); + for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--) + Str[I]=0; + return Str; } -int strnicomp(const char *Str1,const char *Str2,int N) +wchar* RemoveLF(wchar *Str) { - char S1[NM*2],S2[NM*2]; - strncpyz(S1,Str1,ASIZE(S1)); - strncpyz(S2,Str2,ASIZE(S2)); - return(strncmp(strupper(S1),strupper(S2),N)); + for (int I=(int)wcslen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) + Str[I]=0; + return Str; } -char* RemoveEOL(char *Str) +#if defined(SFX_MODULE) +// char version of etoupperw. Used in console SFX module only. +// Fast toupper for English only input and output. Additionally to speed, +// it also avoids Turkish small i to big I with dot conversion problem. +// We do not define 'c' as 'int' to avoid necessity to cast all +// signed chars passed to this function to unsigned char. +unsigned char etoupper(unsigned char c) { - for (int I=(int)strlen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n' || Str[I]==' ' || Str[I]=='\t');I--) - Str[I]=0; - return(Str); + return c>='a' && c<='z' ? c-'a'+'A' : c; } +#endif -char* RemoveLF(char *Str) +// Fast toupper for English only input and output. Additionally to speed, +// it also avoids Turkish small i to big I with dot conversion problem. +// We do not define 'c' as 'int' to avoid necessity to cast all +// signed wchars passed to this function to unsigned char. +wchar etoupperw(wchar c) { - for (int I=(int)strlen(Str)-1;I>=0 && (Str[I]=='\r' || Str[I]=='\n');I--) - Str[I]=0; - return(Str); + return c>='a' && c<='z' ? c-'a'+'A' : c; } -unsigned int loctolower(byte ch) +// We do not want to cast every signed char to unsigned when passing to +// isdigit, so we implement the replacement. Shall work for Unicode too. +// If chars are signed, conversion from char to int could generate negative +// values, resulting in undefined behavior in standard isdigit. +bool IsDigit(int ch) { -#ifdef _WIN_32 - // convert to LPARAM first to avoid a warning in 64 bit mode - return((int)(LPARAM)CharLower((LPTSTR)ch)); -#else - return(tolower(ch)); -#endif + return ch>='0' && ch<='9'; } -unsigned int loctoupper(byte ch) +// We do not want to cast every signed char to unsigned when passing to +// isspace, so we implement the replacement. Shall work for Unicode too. +// If chars are signed, conversion from char to int could generate negative +// values, resulting in undefined behavior in standard isspace. +bool IsSpace(int ch) { -#ifdef _WIN_32 - // convert to LPARAM first to avoid a warning in 64 bit mode - return((int)(LPARAM)CharUpper((LPTSTR)ch)); -#else - return(toupper(ch)); -#endif + return ch==' ' || ch=='\t'; } -// toupper with English only results. Avoiding Turkish i -> I conversion -// problem -int etoupper(int ch) +// We do not want to cast every signed char to unsigned when passing to +// isalpha, so we implement the replacement. Shall work for Unicode too. +// If chars are signed, conversion from char to int could generate negative +// values, resulting in undefined behavior in standard function. +bool IsAlpha(int ch) { - if (ch=='i') - return('I'); - return(toupper(ch)); + return ch>='A' && ch<='Z' || ch>='a' && ch<='z'; } +void BinToHex(const byte *Bin,size_t BinSize,char *HexA,wchar *HexW,size_t HexSize) +{ + uint A=0,W=0; // ASCII and Unicode hex output positions. + for (uint I=0;I<BinSize;I++) + { + uint High=Bin[I] >> 4; + uint Low=Bin[I] & 0xf; + uint HighHex=High>9 ? 'a'+High-10:'0'+High; + uint LowHex=Low>9 ? 'a'+Low-10:'0'+Low; + if (HexA!=NULL && A<HexSize-2) // Need space for 2 chars and final zero. + { + HexA[A++]=(char)HighHex; + HexA[A++]=(char)LowHex; + } + if (HexW!=NULL && W<HexSize-2) // Need space for 2 chars and final zero. + { + HexW[W++]=HighHex; + HexW[W++]=LowHex; + } + } + if (HexA!=NULL && HexSize>0) + HexA[A]=0; + if (HexW!=NULL && HexSize>0) + HexW[W]=0; +} -bool LowAscii(const char *Str) +#ifndef SFX_MODULE +uint GetDigits(uint Number) { - for (int I=0;Str[I]!=0;I++) - if ((byte)Str[I]<32 || (byte)Str[I]>127) - return(false); - return(true); + uint Digits=1; + while (Number>=10) + { + Number/=10; + Digits++; + } + return Digits; } +#endif -bool LowAscii(const wchar *Str) +bool LowAscii(const char *Str) { - for (int I=0;Str[I]!=0;I++) - if (Str[I]<32 || Str[I]>127) - return(false); - return(true); + for (size_t I=0;Str[I]!=0;I++) + if (/*(byte)Str[I]<32 || */(byte)Str[I]>127) + return false; + return true; } +bool LowAscii(const wchar *Str) +{ + for (size_t I=0;Str[I]!=0;I++) + { + // We convert wchar_t to uint just in case if some compiler + // uses signed wchar_t. + if (/*(uint)Str[I]<32 || */(uint)Str[I]>127) + return false; + } + return true; +} -int stricompc(const char *Str1,const char *Str2) +int wcsicompc(const wchar *s1,const wchar *s2) // For path comparison. { #if defined(_UNIX) - return(strcmp(Str1,Str2)); + return wcscmp(s1,s2); #else - return(stricomp(Str1,Str2)); + return wcsicomp(s1,s2); #endif } -#ifndef SFX_MODULE -int stricompcw(const wchar *Str1,const wchar *Str2) +int wcsnicompc(const wchar *s1,const wchar *s2,size_t n) { #if defined(_UNIX) - return(strcmpw(Str1,Str2)); + return wcsncmp(s1,s2,n); #else - return(stricmpw(Str1,Str2)); + return wcsnicomp(s1,s2,n); #endif } -#endif -// safe strncpy: copies maxlen-1 max and always returns zero terminated dest -char* strncpyz(char *dest, const char *src, size_t maxlen) +// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. +void strncpyz(char *dest, const char *src, size_t maxlen) { if (maxlen>0) { - strncpy(dest,src,maxlen-1); - dest[maxlen-1]=0; + while (--maxlen>0 && *src!=0) + *dest++=*src++; + *dest=0; } - return(dest); } -// safe strncpyw: copies maxlen-1 max and always returns zero terminated dest -wchar* strncpyzw(wchar *dest, const wchar *src, size_t maxlen) + +// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest. +void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen) { if (maxlen>0) { - strncpyw(dest,src,maxlen-1); - dest[maxlen-1]=0; + while (--maxlen>0 && *src!=0) + *dest++=*src++; + *dest=0; + } +} + + +// Safe append: resulting dest length cannot exceed maxlen and dest +// is always zero terminated. 'maxlen' parameter defines the entire +// dest buffer size and is not compatible with wcsncat. +void strncatz(char* dest, const char* src, size_t maxlen) +{ + size_t length = strlen(dest); + if (maxlen > length) + strncpyz(dest + length, src, maxlen - length); +} + + +// Safe append: resulting dest length cannot exceed maxlen and dest +// is always zero terminated. 'maxlen' parameter defines the entire +// dest buffer size and is not compatible with wcsncat. +void wcsncatz(wchar* dest, const wchar* src, size_t maxlen) +{ + size_t length = wcslen(dest); + if (maxlen > length) + wcsncpyz(dest + length, src, maxlen - length); +} + + +void itoa(int64 n,char *Str,size_t MaxSize) +{ + char NumStr[50]; + size_t Pos=0; + + int Neg=n < 0 ? 1 : 0; + if (Neg) + n=-n; + + do + { + if (Pos+1>=MaxSize-Neg) + break; + NumStr[Pos++]=char(n%10)+'0'; + n=n/10; + } while (n!=0); + + if (Neg) + NumStr[Pos++]='-'; + + for (size_t I=0;I<Pos;I++) + Str[I]=NumStr[Pos-I-1]; + Str[Pos]=0; +} + + +void itoa(int64 n,wchar *Str,size_t MaxSize) +{ + wchar NumStr[50]; + size_t Pos=0; + + int Neg=n < 0 ? 1 : 0; + if (Neg) + n=-n; + + do + { + if (Pos+1>=MaxSize-Neg) + break; + NumStr[Pos++]=wchar(n%10)+'0'; + n=n/10; + } while (n!=0); + + if (Neg) + NumStr[Pos++]='-'; + + for (size_t I=0;I<Pos;I++) + Str[I]=NumStr[Pos-I-1]; + Str[Pos]=0; +} + + +// Convert the number to string using thousand separators. +void fmtitoa(int64 n,wchar *Str,size_t MaxSize) +{ + static wchar ThSep=0; // Thousands separator. +#ifdef _WIN_ALL + wchar Info[10]; + if (!ThSep!=0 && GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,Info,ASIZE(Info))>0) + ThSep=*Info; +#elif defined(_UNIX) + ThSep=*localeconv()->thousands_sep; +#endif + if (ThSep==0) // If failed to detect the actual separator value. + ThSep=' '; + wchar RawText[30]; // 20 characters are enough for largest unsigned 64 bit int. + itoa(n,RawText,ASIZE(RawText)); + uint S=0,D=0,L=wcslen(RawText)%3; + while (RawText[S]!=0 && D+1<MaxSize) + { + if (S!=0 && (S+3-L)%3==0) + Str[D++]=ThSep; + Str[D++]=RawText[S++]; + } + Str[D]=0; +} + + +const wchar* GetWide(const char *Src) +{ + const size_t MaxLength=NM; + static wchar StrTable[4][MaxLength]; + static uint StrNum=0; + if (++StrNum >= ASIZE(StrTable)) + StrNum=0; + wchar *Str=StrTable[StrNum]; + CharToWide(Src,Str,MaxLength); + Str[MaxLength-1]=0; + return Str; +} + + +// Parse string containing parameters separated with spaces. +// Support quote marks. Param can be NULL to return the pointer to next +// parameter, which can be used to estimate the buffer size for Param. +const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize) +{ + while (IsSpace(*CmdLine)) + CmdLine++; + if (*CmdLine==0) + return NULL; + + size_t ParamSize=0; + bool Quote=false; + while (*CmdLine!=0 && (Quote || !IsSpace(*CmdLine))) + { + if (*CmdLine=='\"') + { + if (CmdLine[1]=='\"') + { + // Insert the quote character instead of two adjoining quote characters. + if (Param!=NULL && ParamSize<MaxSize-1) + Param[ParamSize++]='\"'; + CmdLine++; + } + else + Quote=!Quote; + } + else + if (Param!=NULL && ParamSize<MaxSize-1) + Param[ParamSize++]=*CmdLine; + CmdLine++; } - return(dest); + if (Param!=NULL) + Param[ParamSize]=0; + return CmdLine; } + + +#ifndef RARDLL +// For compatibility with existing translations we use %s to print Unicode +// strings in format strings and convert them to %ls here. %s could work +// without such conversion in Windows, but not in Unix wprintf. +void PrintfPrepareFmt(const wchar *Org,wchar *Cvt,size_t MaxSize) +{ + uint Src=0,Dest=0; + while (Org[Src]!=0 && Dest<MaxSize-1) + { + if (Org[Src]=='%' && (Src==0 || Org[Src-1]!='%')) + { + uint SPos=Src+1; + // Skipping a possible width specifier like %-50s. + while (IsDigit(Org[SPos]) || Org[SPos]=='-') + SPos++; + if (Org[SPos]=='s' && Dest<MaxSize-(SPos-Src+1)) + { + while (Src<SPos) + Cvt[Dest++]=Org[Src++]; + Cvt[Dest++]='l'; + } + } +#ifdef _WIN_ALL + // Convert \n to \r\n in Windows. Important when writing to log, + // so other tools like Notebook can view resulting log properly. + if (Org[Src]=='\n' && (Src==0 || Org[Src-1]!='\r')) + Cvt[Dest++]='\r'; +#endif + + Cvt[Dest++]=Org[Src++]; + } + Cvt[Dest]=0; +} +#endif diff --git a/unrar/unrar/strfn.hpp b/unrar/unrar/strfn.hpp index a167667..2a21fea 100644 --- a/unrar/unrar/strfn.hpp +++ b/unrar/unrar/strfn.hpp @@ -1,34 +1,52 @@ #ifndef _RAR_STRFN_ #define _RAR_STRFN_ -const char *NullToEmpty(const char *Str); -const wchar *NullToEmpty(const wchar *Str); -char *IntNameToExt(const char *Name); -void ExtToInt(const char *Src,char *Dest); -void IntToExt(const char *Src,char *Dest); -char* strlower(char *Str); -char* strupper(char *Str); -int stricomp(const char *Str1,const char *Str2); -int strnicomp(const char *Str1,const char *Str2,int N); -char* RemoveEOL(char *Str); -char* RemoveLF(char *Str); -unsigned int loctolower(byte ch); -unsigned int loctoupper(byte ch); +const char* NullToEmpty(const char *Str); +const wchar* NullToEmpty(const wchar *Str); +void IntToExt(const char *Src,char *Dest,size_t DestSize); -char* strncpyz(char *dest, const char *src, size_t maxlen); -wchar* strncpyzw(wchar *dest, const wchar *src, size_t maxlen); +enum ACTW_ENCODING { ACTW_DEFAULT, ACTW_OEM, ACTW_UTF8}; +void ArcCharToWide(const char *Src,wchar *Dest,size_t DestSize,ACTW_ENCODING Encoding); -int etoupper(int ch); +int stricomp(const char *s1,const char *s2); +int strnicomp(const char *s1,const char *s2,size_t n); +wchar* RemoveEOL(wchar *Str); +wchar* RemoveLF(wchar *Str); +void strncpyz(char *dest, const char *src, size_t maxlen); +void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen); +void strncatz(char* dest, const char* src, size_t maxlen); +void wcsncatz(wchar* dest, const wchar* src, size_t maxlen); + +#if defined(SFX_MODULE) +unsigned char etoupper(unsigned char c); +#endif +wchar etoupperw(wchar c); + +bool IsDigit(int ch); +bool IsSpace(int ch); +bool IsAlpha(int ch); + +void BinToHex(const byte *Bin,size_t BinSize,char *Hex,wchar *HexW,size_t HexSize); + +#ifndef SFX_MODULE +uint GetDigits(uint Number); +#endif bool LowAscii(const char *Str); bool LowAscii(const wchar *Str); +int wcsicompc(const wchar *s1,const wchar *s2); +int wcsnicompc(const wchar *s1,const wchar *s2,size_t n); -int stricompc(const char *Str1,const char *Str2); -#ifndef SFX_MODULE -int stricompcw(const wchar *Str1,const wchar *Str2); +void itoa(int64 n,char *Str,size_t MaxSize); +void itoa(int64 n,wchar *Str,size_t MaxSize); +void fmtitoa(int64 n,wchar *Str,size_t MaxSize); +const wchar* GetWide(const char *Src); +const wchar* GetCmdParam(const wchar *CmdLine,wchar *Param,size_t MaxSize); +#ifndef RARDLL +void PrintfPrepareFmt(const wchar *Org,wchar *Cvt,size_t MaxSize); #endif #endif diff --git a/unrar/unrar/strlist.cpp b/unrar/unrar/strlist.cpp index 7e7e50c..50d69c7 100644 --- a/unrar/unrar/strlist.cpp +++ b/unrar/unrar/strlist.cpp @@ -6,159 +6,123 @@ StringList::StringList() } -StringList::~StringList() -{ -} - - void StringList::Reset() { Rewind(); StringData.Reset(); - StringDataW.Reset(); - PosDataW.Reset(); StringsCount=0; SavePosNumber=0; } -unsigned int StringList::AddString(const char *Str) +void StringList::AddStringA(const char *Str) { - return(AddString(Str,NULL)); + Array<wchar> StrW(strlen(Str)); + CharToWide(Str,&StrW[0],StrW.Size()); + AddString(&StrW[0]); } -unsigned int StringList::AddString(const char *Str,const wchar *StrW) +void StringList::AddString(const wchar *Str) { - int PrevSize=StringData.Size(); - StringData.Add((int)(strlen(Str)+1)); - strcpy(&StringData[PrevSize],Str); - if (StrW!=NULL && *StrW!=0) - { - int PrevPos=PosDataW.Size(); - PosDataW.Add(1); - PosDataW[PrevPos]=PrevSize; + if (Str==NULL) + Str=L""; + + size_t PrevSize=StringData.Size(); + StringData.Add(wcslen(Str)+1); + wcscpy(&StringData[PrevSize],Str); - int PrevSizeW=StringDataW.Size(); - StringDataW.Add(strlenw(StrW)+1); - strcpyw(&StringDataW[PrevSizeW],StrW); - } StringsCount++; - return(PrevSize); } -bool StringList::GetString(char *Str,int MaxLength) +bool StringList::GetStringA(char *Str,size_t MaxLength) { - return(GetString(Str,NULL,MaxLength)); + Array<wchar> StrW(MaxLength); + if (!GetString(&StrW[0],StrW.Size())) + return false; + WideToChar(&StrW[0],Str,MaxLength); + return true; } -bool StringList::GetString(char *Str,wchar *StrW,int MaxLength) +bool StringList::GetString(wchar *Str,size_t MaxLength) { - char *StrPtr; - wchar *StrPtrW; - if (Str==NULL || !GetString(&StrPtr,&StrPtrW)) - return(false); - strncpy(Str,StrPtr,MaxLength); - if (StrW!=NULL) - strncpyw(StrW,NullToEmpty(StrPtrW),MaxLength); - return(true); + wchar *StrPtr; + if (!GetString(&StrPtr)) + return false; + wcsncpyz(Str,StrPtr,MaxLength); + return true; } #ifndef SFX_MODULE -bool StringList::GetString(char *Str,wchar *StrW,int MaxLength,int StringNum) +bool StringList::GetString(wchar *Str,size_t MaxLength,int StringNum) { SavePosition(); Rewind(); bool RetCode=true; while (StringNum-- >=0) - if (!GetString(Str,StrW,MaxLength)) + if (!GetString(Str,MaxLength)) { RetCode=false; break; } RestorePosition(); - return(RetCode); + return RetCode; } #endif -char* StringList::GetString() +wchar* StringList::GetString() { - char *Str; - GetString(&Str,NULL); - return(Str); + wchar *Str; + GetString(&Str); + return Str; } - -bool StringList::GetString(char **Str,wchar **StrW) +bool StringList::GetString(wchar **Str) { - if (CurPos>=StringData.Size()) + if (CurPos>=StringData.Size()) // No more strings left unprocessed. { - *Str=NULL; - return(false); + if (Str!=NULL) + *Str=NULL; + return false; } - *Str=&StringData[CurPos]; - if (PosDataItem<PosDataW.Size() && PosDataW[PosDataItem]==CurPos) - { - PosDataItem++; - if (StrW!=NULL) - *StrW=&StringDataW[CurPosW]; - CurPosW+=strlenw(&StringDataW[CurPosW])+1; - } - else - if (StrW!=NULL) - *StrW=NULL; - CurPos+=strlen(*Str)+1; - return(true); -} + wchar *CurStr=&StringData[CurPos]; + CurPos+=wcslen(CurStr)+1; + if (Str!=NULL) + *Str=CurStr; -char* StringList::GetString(unsigned int StringPos) -{ - if (StringPos>=StringData.Size()) - return(NULL); - return(&StringData[StringPos]); + return true; } void StringList::Rewind() { CurPos=0; - CurPosW=0; - PosDataItem=0; -} - - -int StringList::GetBufferSize() -{ - return(StringData.Size()+StringDataW.Size()); } #ifndef SFX_MODULE -bool StringList::Search(char *Str,wchar *StrW,bool CaseSensitive) +bool StringList::Search(const wchar *Str,bool CaseSensitive) { SavePosition(); Rewind(); bool Found=false; - char *CurStr; - wchar *CurStrW; - while (GetString(&CurStr,&CurStrW)) + wchar *CurStr; + while (GetString(&CurStr)) { - if ((CaseSensitive ? strcmp(Str,CurStr):stricomp(Str,CurStr))!=0) - continue; - if (StrW!=NULL && CurStrW!=NULL) - if ((CaseSensitive ? strcmpw(StrW,CurStrW):stricmpw(StrW,CurStrW))!=0) + if (Str!=NULL && CurStr!=NULL) + if ((CaseSensitive ? wcscmp(Str,CurStr):wcsicomp(Str,CurStr))!=0) continue; Found=true; break; } RestorePosition(); - return(Found); + return Found; } #endif @@ -166,11 +130,9 @@ bool StringList::Search(char *Str,wchar *StrW,bool CaseSensitive) #ifndef SFX_MODULE void StringList::SavePosition() { - if (SavePosNumber<sizeof(SaveCurPos)/sizeof(SaveCurPos[0])) + if (SavePosNumber<ASIZE(SaveCurPos)) { SaveCurPos[SavePosNumber]=CurPos; - SaveCurPosW[SavePosNumber]=CurPosW; - SavePosDataItem[SavePosNumber]=PosDataItem; SavePosNumber++; } } @@ -184,8 +146,6 @@ void StringList::RestorePosition() { SavePosNumber--; CurPos=SaveCurPos[SavePosNumber]; - CurPosW=SaveCurPosW[SavePosNumber]; - PosDataItem=SavePosDataItem[SavePosNumber]; } } #endif diff --git a/unrar/unrar/strlist.hpp b/unrar/unrar/strlist.hpp index c4d3b01..16a2cbb 100644 --- a/unrar/unrar/strlist.hpp +++ b/unrar/unrar/strlist.hpp @@ -4,34 +4,26 @@ class StringList { private: - Array<char> StringData; - unsigned int CurPos; + Array<wchar> StringData; + size_t CurPos; - Array<wchar> StringDataW; - unsigned int CurPosW; + size_t StringsCount; - Array<int> PosDataW; - uint PosDataItem; - - uint StringsCount; - - uint SaveCurPos[16],SaveCurPosW[16],SavePosDataItem[16],SavePosNumber; + size_t SaveCurPos[16],SavePosNumber; public: StringList(); - ~StringList(); void Reset(); - unsigned int AddString(const char *Str); - unsigned int AddString(const char *Str,const wchar *StrW); - bool GetString(char *Str,int MaxLength); - bool GetString(char *Str,wchar *StrW,int MaxLength); - bool GetString(char *Str,wchar *StrW,int MaxLength,int StringNum); - char* GetString(); - bool GetString(char **Str,wchar **StrW); - char* GetString(unsigned int StringPos); + void AddStringA(const char *Str); + void AddString(const wchar *Str); + bool GetStringA(char *Str,size_t MaxLength); + bool GetString(wchar *Str,size_t MaxLength); + bool GetString(wchar *Str,size_t MaxLength,int StringNum); + wchar* GetString(); + bool GetString(wchar **Str); void Rewind(); - unsigned int ItemsCount() {return(StringsCount);}; - int GetBufferSize(); - bool Search(char *Str,wchar *StrW,bool CaseSensitive); + size_t ItemsCount() {return StringsCount;}; + size_t GetCharCount() {return StringData.Size();} + bool Search(const wchar *Str,bool CaseSensitive); void SavePosition(); void RestorePosition(); }; diff --git a/unrar/unrar/suballoc.cpp b/unrar/unrar/suballoc.cpp index 0d42de5..07d3285 100644 --- a/unrar/unrar/suballoc.cpp +++ b/unrar/unrar/suballoc.cpp @@ -5,6 +5,9 @@ * Contents: memory allocation routines * ****************************************************************************/ +static const uint UNIT_SIZE=Max(sizeof(RARPPM_CONTEXT),sizeof(RARPPM_MEM_BLK)); +static const uint FIXED_UNIT_SIZE=12; + SubAllocator::SubAllocator() { Clean(); @@ -34,17 +37,18 @@ inline void* SubAllocator::RemoveNode(int indx) inline uint SubAllocator::U2B(int NU) { - return /*8*NU+4*NU*/UNIT_SIZE*NU; + // We calculate the size of units in bytes based on real UNIT_SIZE. + // In original implementation it was 8*NU+4*NU. + return UNIT_SIZE*NU; } -/* - calculate RAR_MEM_BLK + Items address. Real RAR_MEM_BLK size must be - equal to UNIT_SIZE, so we cannot just add Items to RAR_MEM_BLK address -*/ -inline RAR_MEM_BLK* SubAllocator::MBPtr(RAR_MEM_BLK *BasePtr,int Items) + +// Calculate RARPPM_MEM_BLK+Items address. Real RARPPM_MEM_BLK size must be +// equal to UNIT_SIZE, so we cannot just add Items to RARPPM_MEM_BLK address. +inline RARPPM_MEM_BLK* SubAllocator::MBPtr(RARPPM_MEM_BLK *BasePtr,int Items) { - return((RAR_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) )); + return((RARPPM_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) )); } @@ -62,14 +66,12 @@ inline void SubAllocator::SplitBlock(void* pv,int OldIndx,int NewIndx) } - - void SubAllocator::StopSubAllocator() { if ( SubAllocatorSize ) { SubAllocatorSize=0; - rarfree(HeapStart); + free(HeapStart); } } @@ -78,20 +80,26 @@ bool SubAllocator::StartSubAllocator(int SASize) { uint t=SASize << 20; if (SubAllocatorSize == t) - return TRUE; + return true; StopSubAllocator(); - uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE; -#ifdef STRICT_ALIGNMENT_REQUIRED - AllocSize+=UNIT_SIZE; -#endif - if ((HeapStart=(byte *)rarmalloc(AllocSize)) == NULL) + + // Original algorithm expects FIXED_UNIT_SIZE, but actual structure size + // can be larger. So let's recalculate the allocated size and add two more + // units: one as reserve for HeapEnd overflow checks and another + // to provide the space to correctly align UnitsStart. + uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+2*UNIT_SIZE; + if ((HeapStart=(byte *)malloc(AllocSize)) == NULL) { ErrHandler.MemoryError(); - return FALSE; + return false; } + + // HeapEnd did not present in original algorithm. We added it to control + // invalid memory access attempts when processing corrupt archived data. HeapEnd=HeapStart+AllocSize-UNIT_SIZE; + SubAllocatorSize=t; - return TRUE; + return true; } @@ -100,17 +108,46 @@ void SubAllocator::InitSubAllocator() int i, k; memset(FreeList,0,sizeof(FreeList)); pText=HeapStart; + + // Original algorithm operates with 12 byte FIXED_UNIT_SIZE, but actual + // size of RARPPM_MEM_BLK and RARPPM_CONTEXT structures can exceed this value + // because of alignment and larger pointer fields size. + // So we define UNIT_SIZE for this larger size and adjust memory + // pointers accordingly. + + // Size2 is (HiUnit-LoUnit) memory area size to allocate as originally + // supposed by compression algorithm. It is 7/8 of total allocated size. uint Size2=FIXED_UNIT_SIZE*(SubAllocatorSize/8/FIXED_UNIT_SIZE*7); + + // RealSize2 is the real adjusted size of (HiUnit-LoUnit) memory taking + // into account that our UNIT_SIZE can be larger than FIXED_UNIT_SIZE. uint RealSize2=Size2/FIXED_UNIT_SIZE*UNIT_SIZE; + + // Size1 is the size of memory area from HeapStart to FakeUnitsStart + // as originally supposed by compression algorithm. This area can contain + // different data types, both single symbols and structures. uint Size1=SubAllocatorSize-Size2; - uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+Size1%FIXED_UNIT_SIZE; -#ifdef STRICT_ALIGNMENT_REQUIRED - if (Size1%FIXED_UNIT_SIZE!=0) - RealSize1+=UNIT_SIZE-Size1%FIXED_UNIT_SIZE; -#endif - HiUnit=HeapStart+SubAllocatorSize; + + // Real size of this area. We correct it according to UNIT_SIZE vs + // FIXED_UNIT_SIZE difference. Also we add one more UNIT_SIZE + // to compensate a possible reminder from Size1/FIXED_UNIT_SIZE, + // which would be lost otherwise. We add UNIT_SIZE instead of + // this Size1%FIXED_UNIT_SIZE reminder, because it allows to align + // UnitsStart easily and adding more than reminder is ok for algorithm. + uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE; + + // RealSize1 must be divided by UNIT_SIZE without a reminder, so UnitsStart + // is aligned to UNIT_SIZE. It is important for those architectures, + // where a proper memory alignment is mandatory. Since we produce RealSize1 + // multiplying by UNIT_SIZE, this condition is always true. So LoUnit, + // UnitsStart, HeapStart are properly aligned, LoUnit=UnitsStart=HeapStart+RealSize1; + + // When we reach FakeUnitsStart, we restart the model. It is where + // the original algorithm expected to see UnitsStart. Real UnitsStart + // can have a larger value. FakeUnitsStart=HeapStart+Size1; + HiUnit=LoUnit+RealSize2; for (i=0,k=1;i < N1 ;i++,k += 1) Indx2Units[i]=k; @@ -130,14 +167,14 @@ void SubAllocator::InitSubAllocator() inline void SubAllocator::GlueFreeBlocks() { - RAR_MEM_BLK s0, * p, * p1; + RARPPM_MEM_BLK s0, * p, * p1; int i, k, sz; if (LoUnit != HiUnit) *LoUnit=0; for (i=0, s0.next=s0.prev=&s0;i < N_INDEXES;i++) while ( FreeList[i].next ) { - p=(RAR_MEM_BLK*)RemoveNode(i); + p=(RARPPM_MEM_BLK*)RemoveNode(i); p->insertAt(&s0); p->Stamp=0xFFFF; p->NU=Indx2Units[i]; @@ -178,13 +215,13 @@ void* SubAllocator::AllocUnitsRare(int indx) GlueCount--; i=U2B(Indx2Units[indx]); int j=FIXED_UNIT_SIZE*Indx2Units[indx]; - if (FakeUnitsStart-pText > j) + if (FakeUnitsStart - pText > j) { - FakeUnitsStart-=j; + FakeUnitsStart -= j; UnitsStart -= i; - return(UnitsStart); + return UnitsStart; } - return(NULL); + return NULL; } } while ( !FreeList[i].next ); void* RetVal=RemoveNode(i); diff --git a/unrar/unrar/suballoc.hpp b/unrar/unrar/suballoc.hpp index 474ee59..2a1d132 100644 --- a/unrar/unrar/suballoc.hpp +++ b/unrar/unrar/suballoc.hpp @@ -7,24 +7,21 @@ #if !defined(_SUBALLOC_H_) #define _SUBALLOC_H_ -const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4; -const int N_INDEXES=N1+N2+N3+N4; - -#if defined(__GNUC__) && !defined(STRICT_ALIGNMENT_REQUIRED) -#define _PACK_ATTR __attribute__ ((packed)) +#if defined(__GNUC__) && defined(ALLOW_MISALIGNED) +#define RARPPM_PACK_ATTR __attribute__ ((packed)) #else -#define _PACK_ATTR +#define RARPPM_PACK_ATTR #endif /* defined(__GNUC__) */ -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #pragma pack(1) #endif -struct RAR_MEM_BLK +struct RARPPM_MEM_BLK { ushort Stamp, NU; - RAR_MEM_BLK* next, * prev; - void insertAt(RAR_MEM_BLK* p) + RARPPM_MEM_BLK* next, * prev; + void insertAt(RARPPM_MEM_BLK* p) { next=(prev=p)->next; p->next=next->prev=this; @@ -34,9 +31,9 @@ struct RAR_MEM_BLK prev->next=next; next->prev=prev; } -} _PACK_ATTR; +} RARPPM_PACK_ATTR; -#ifndef STRICT_ALIGNMENT_REQUIRED +#ifdef ALLOW_MISALIGNED #ifdef _AIX #pragma pack(pop) #else @@ -45,22 +42,24 @@ struct RAR_MEM_BLK #endif -struct RAR_NODE -{ - RAR_NODE* next; -}; - class SubAllocator { private: + static const int N1=4, N2=4, N3=4, N4=(128+3-1*N1-2*N2-3*N3)/4; + static const int N_INDEXES=N1+N2+N3+N4; + + struct RAR_NODE + { + RAR_NODE* next; + }; + inline void InsertNode(void* p,int indx); inline void* RemoveNode(int indx); inline uint U2B(int NU); inline void SplitBlock(void* pv,int OldIndx,int NewIndx); - uint GetUsedMemory(); inline void GlueFreeBlocks(); void* AllocUnitsRare(int indx); - inline RAR_MEM_BLK* MBPtr(RAR_MEM_BLK *BasePtr,int Items); + inline RARPPM_MEM_BLK* MBPtr(RARPPM_MEM_BLK *BasePtr,int Items); long SubAllocatorSize; byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; @@ -78,7 +77,7 @@ class SubAllocator inline void* ExpandUnits(void* ptr,int OldNU); inline void* ShrinkUnits(void* ptr,int OldNU,int NewNU); inline void FreeUnits(void* ptr,int OldNU); - long GetAllocatedMemory() {return(SubAllocatorSize);}; + long GetAllocatedMemory() {return(SubAllocatorSize);} byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; }; diff --git a/unrar/unrar/system.cpp b/unrar/unrar/system.cpp index d5de22e..9e53622 100644 --- a/unrar/unrar/system.cpp +++ b/unrar/unrar/system.cpp @@ -1,20 +1,17 @@ #include "rar.hpp" -#ifndef _WIN_CE static int SleepTime=0; void InitSystemOptions(int SleepTime) { ::SleepTime=SleepTime; } -#endif - -#if !defined(SFX_MODULE) && !defined(_WIN_CE) +#if !defined(SFX_MODULE) void SetPriority(int Priority) { -#ifdef _WIN_32 +#ifdef _WIN_ALL uint PriorityClass; int PriorityLevel; if (Priority<1 || Priority>15) @@ -24,6 +21,10 @@ void SetPriority(int Priority) { PriorityClass=IDLE_PRIORITY_CLASS; PriorityLevel=THREAD_PRIORITY_IDLE; + +// Background mode for Vista, can be slow for many small files. +// if (WinNT()>=WNT_VISTA) +// SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN); } else if (Priority<7) @@ -57,29 +58,51 @@ void SetPriority(int Priority) SetPriorityClass(GetCurrentProcess(),PriorityClass); SetThreadPriority(GetCurrentThread(),PriorityLevel); -// background mode for Vista, too slow for real life use -// if (WinNT()>=6 && Priority==1) -// SetPriorityClass(GetCurrentProcess(),PROCESS_MODE_BACKGROUND_BEGIN); +#ifdef RAR_SMP + ThreadPool::SetPriority(PriorityLevel); +#endif #endif } #endif +// Monotonic clock. Like clock(), returns time passed in CLOCKS_PER_SEC items. +// In Android 5+ and Unix usual clock() returns time spent by all threads +// together, so we cannot use it to measure time intervals anymore. +clock_t MonoClock() +{ + return clock(); +} + + + void Wait() { -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) + if (ErrHandler.UserBreak) + ErrHandler.Exit(RARX_USERBREAK); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) if (SleepTime!=0) - Sleep(SleepTime); + { + static clock_t LastTime=MonoClock(); + if (MonoClock()-LastTime>10*CLOCKS_PER_SEC/1000) + { + Sleep(SleepTime); + LastTime=MonoClock(); + } + } +#endif +#if defined(_WIN_ALL) + // Reset system sleep timer to prevent system going sleep. + SetThreadExecutionState(ES_SYSTEM_REQUIRED); #endif } -#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE) && !defined(SHELL_EXT) - -void Shutdown() +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void Shutdown(POWER_MODE Mode) { HANDLE hToken; TOKEN_PRIVILEGES tkp; @@ -91,6 +114,102 @@ void Shutdown() AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0); } - ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE|EWX_POWEROFF,SHTDN_REASON_FLAG_PLANNED); + if (Mode==POWERMODE_OFF) + ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); + if (Mode==POWERMODE_SLEEP) + SetSuspendState(FALSE,FALSE,FALSE); + if (Mode==POWERMODE_HIBERNATE) + SetSuspendState(TRUE,FALSE,FALSE); + if (Mode==POWERMODE_RESTART) + ExitWindowsEx(EWX_REBOOT|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED); +} + + +bool ShutdownCheckAnother(bool Open) +{ + const wchar *EventName=L"rar -ioff"; + static HANDLE hEvent=NULL; + bool Result=false; // Return false if no other RAR -ioff are running. + if (Open) // Create or open the event. + hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); + else + { + if (hEvent!=NULL) + CloseHandle(hEvent); // Close our event. + // Check if other copies still own the event. While race conditions + // are possible, they are improbable and their harm is minimal. + hEvent=CreateEvent(NULL,FALSE,FALSE,EventName); + Result=GetLastError()==ERROR_ALREADY_EXISTS; + if (hEvent!=NULL) + CloseHandle(hEvent); + } + return Result; +} +#endif + + + + +#if defined(_WIN_ALL) +// Load library from Windows System32 folder. Use this function to prevent +// loading a malicious code from current folder or same folder as exe. +HMODULE WINAPI LoadSysLibrary(const wchar *Name) +{ + wchar SysDir[NM]; + if (GetSystemDirectory(SysDir,ASIZE(SysDir))==0) + return NULL; + MakeName(SysDir,Name,SysDir,ASIZE(SysDir)); + return LoadLibrary(SysDir); +} + + +bool IsUserAdmin() +{ + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + PSID AdministratorsGroup; + BOOL b = AllocateAndInitializeSid(&NtAuthority,2,SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); + if (b) + { + if (!CheckTokenMembership( NULL, AdministratorsGroup, &b)) + b = FALSE; + FreeSid(AdministratorsGroup); + } + return b!=FALSE; +} + +#endif + + +#ifdef USE_SSE +SSE_VERSION _SSE_Version=GetSSEVersion(); + +SSE_VERSION GetSSEVersion() +{ + int CPUInfo[4]; + __cpuid(CPUInfo, 0); + + // Maximum supported cpuid function. + uint MaxSupported=CPUInfo[0]; + + if (MaxSupported>=7) + { + __cpuid(CPUInfo, 7); + if ((CPUInfo[1] & 0x20)!=0) + return SSE_AVX2; + } + if (MaxSupported>=1) + { + __cpuid(CPUInfo, 1); + if ((CPUInfo[2] & 0x80000)!=0) + return SSE_SSE41; + if ((CPUInfo[2] & 0x200)!=0) + return SSE_SSSE3; + if ((CPUInfo[3] & 0x4000000)!=0) + return SSE_SSE2; + if ((CPUInfo[3] & 0x2000000)!=0) + return SSE_SSE; + } + return SSE_NONE; } #endif diff --git a/unrar/unrar/system.hpp b/unrar/unrar/system.hpp index d98e5a2..a56d6b7 100644 --- a/unrar/unrar/system.hpp +++ b/unrar/unrar/system.hpp @@ -1,7 +1,7 @@ #ifndef _RAR_SYSTEM_ #define _RAR_SYSTEM_ -#ifdef _WIN_32 +#ifdef _WIN_ALL #ifndef BELOW_NORMAL_PRIORITY_CLASS #define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 #define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 @@ -19,8 +19,22 @@ void InitSystemOptions(int SleepTime); void SetPriority(int Priority); +clock_t MonoClock(); void Wait(); -bool EmailFile(char *FileName,char *MailTo); -void Shutdown(); +bool EmailFile(const wchar *FileName,const wchar *MailToW); +void Shutdown(POWER_MODE Mode); +bool ShutdownCheckAnother(bool Open); + +#ifdef _WIN_ALL +HMODULE WINAPI LoadSysLibrary(const wchar *Name); +bool IsUserAdmin(); +#endif + + +#ifdef USE_SSE +enum SSE_VERSION {SSE_NONE,SSE_SSE,SSE_SSE2,SSE_SSSE3,SSE_SSE41,SSE_AVX2}; +SSE_VERSION GetSSEVersion(); +extern SSE_VERSION _SSE_Version; +#endif #endif diff --git a/unrar/unrar/threadmisc.cpp b/unrar/unrar/threadmisc.cpp new file mode 100644 index 0000000..7a6ec78 --- /dev/null +++ b/unrar/unrar/threadmisc.cpp @@ -0,0 +1,153 @@ +static inline bool CriticalSectionCreate(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + InitializeCriticalSection(CritSection); + return true; +#elif defined(_UNIX) + return pthread_mutex_init(CritSection,NULL)==0; +#endif +} + + +static inline void CriticalSectionDelete(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + DeleteCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_destroy(CritSection); +#endif +} + + +static inline void CriticalSectionStart(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + EnterCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_lock(CritSection); +#endif +} + + +static inline void CriticalSectionEnd(CRITSECT_HANDLE *CritSection) +{ +#ifdef _WIN_ALL + LeaveCriticalSection(CritSection); +#elif defined(_UNIX) + pthread_mutex_unlock(CritSection); +#endif +} + + +static THREAD_HANDLE ThreadCreate(NATIVE_THREAD_PTR Proc,void *Data) +{ +#ifdef _UNIX +/* + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); +*/ + pthread_t pt; + int Code=pthread_create(&pt,NULL/*&attr*/,Proc,Data); + if (Code!=0) + { + wchar Msg[100]; + swprintf(Msg,ASIZE(Msg),L"\npthread_create failed, code %d\n",Code); + ErrHandler.GeneralErrMsg(Msg); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return pt; +#else + DWORD ThreadId; + HANDLE hThread=CreateThread(NULL,0x10000,Proc,Data,0,&ThreadId); + if (hThread==NULL) + { + ErrHandler.GeneralErrMsg(L"CreateThread failed"); + ErrHandler.SysErrMsg(); + ErrHandler.Exit(RARX_FATAL); + } + return hThread; +#endif +} + + +static void ThreadClose(THREAD_HANDLE hThread) +{ +#ifdef _UNIX + pthread_join(hThread,NULL); +#else + CloseHandle(hThread); +#endif +} + + +#ifdef _WIN_ALL +static void CWaitForSingleObject(HANDLE hHandle) +{ + DWORD rc=WaitForSingleObject(hHandle,INFINITE); + if (rc==WAIT_FAILED) + { + ErrHandler.GeneralErrMsg(L"\nWaitForMultipleObjects error %d, GetLastError %d",rc,GetLastError()); + ErrHandler.Exit(RARX_FATAL); + } +} +#endif + + +#ifdef _UNIX +static void cpthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + int rc=pthread_cond_wait(cond,mutex); + if (rc!=0) + { + ErrHandler.GeneralErrMsg(L"\npthread_cond_wait error %d",rc); + ErrHandler.Exit(RARX_FATAL); + } +} +#endif + + +uint GetNumberOfCPU() +{ +#ifndef RAR_SMP + return 1; +#else +#ifdef _UNIX +#ifdef _SC_NPROCESSORS_ONLN + uint Count=(uint)sysconf(_SC_NPROCESSORS_ONLN); + return Count<1 ? 1:Count; +#elif defined(_APPLE) + uint Count; + size_t Size=sizeof(Count); + return sysctlbyname("hw.ncpu",&Count,&Size,NULL,0)==0 ? Count:1; +#endif +#else // !_UNIX + DWORD_PTR ProcessMask; + DWORD_PTR SystemMask; + + if (!GetProcessAffinityMask(GetCurrentProcess(),&ProcessMask,&SystemMask)) + return 1; + uint Count=0; + for (DWORD_PTR Mask=1;Mask!=0;Mask<<=1) + if ((ProcessMask & Mask)!=0) + Count++; + return Count<1 ? 1:Count; +#endif + +#endif // RAR_SMP +} + + +uint GetNumberOfThreads() +{ + uint NumCPU=GetNumberOfCPU(); + if (NumCPU<1) + return 1; + if (NumCPU>MaxPoolThreads) + return MaxPoolThreads; + return NumCPU; +} + + + diff --git a/unrar/unrar/threadpool.cpp b/unrar/unrar/threadpool.cpp new file mode 100644 index 0000000..8c63a8b --- /dev/null +++ b/unrar/unrar/threadpool.cpp @@ -0,0 +1,212 @@ +#include "rar.hpp" + +#ifdef RAR_SMP +#include "threadmisc.cpp" + +#ifdef _WIN_ALL +int ThreadPool::ThreadPriority=THREAD_PRIORITY_NORMAL; +#endif + +ThreadPool::ThreadPool(uint MaxThreads) +{ + MaxAllowedThreads = MaxThreads; + if (MaxAllowedThreads>MaxPoolThreads) + MaxAllowedThreads=MaxPoolThreads; + if (MaxAllowedThreads==0) + MaxAllowedThreads=1; + + ThreadsCreatedCount=0; + + // If we have more threads than queue size, we'll hang on pool destroying, + // not releasing all waiting threads. + if (MaxAllowedThreads>ASIZE(TaskQueue)) + MaxAllowedThreads=ASIZE(TaskQueue); + + Closing=false; + + bool Success = CriticalSectionCreate(&CritSection); +#ifdef _WIN_ALL + QueuedTasksCnt=CreateSemaphore(NULL,0,ASIZE(TaskQueue),NULL); + NoneActive=CreateEvent(NULL,TRUE,TRUE,NULL); + Success=Success && QueuedTasksCnt!=NULL && NoneActive!=NULL; +#elif defined(_UNIX) + AnyActive = false; + QueuedTasksCnt = 0; + Success=Success && pthread_cond_init(&AnyActiveCond,NULL)==0 && + pthread_mutex_init(&AnyActiveMutex,NULL)==0 && + pthread_cond_init(&QueuedTasksCntCond,NULL)==0 && + pthread_mutex_init(&QueuedTasksCntMutex,NULL)==0; +#endif + if (!Success) + { + ErrHandler.GeneralErrMsg(L"\nThread pool initialization failed."); + ErrHandler.Exit(RARX_FATAL); + } + + QueueTop = 0; + QueueBottom = 0; + ActiveThreads = 0; +} + + +ThreadPool::~ThreadPool() +{ + WaitDone(); + Closing=true; + +#ifdef _WIN_ALL + ReleaseSemaphore(QueuedTasksCnt,ASIZE(TaskQueue),NULL); +#elif defined(_UNIX) + // Threads still can access QueuedTasksCnt for a short time after WaitDone(), + // so lock is required. We would occassionally hang without it. + pthread_mutex_lock(&QueuedTasksCntMutex); + QueuedTasksCnt+=ASIZE(TaskQueue); + pthread_mutex_unlock(&QueuedTasksCntMutex); + + pthread_cond_broadcast(&QueuedTasksCntCond); +#endif + + for(uint I=0;I<ThreadsCreatedCount;I++) + { +#ifdef _WIN_ALL + // Waiting until the thread terminates. + CWaitForSingleObject(ThreadHandles[I]); +#endif + // Close the thread handle. In Unix it results in pthread_join call, + // which also waits for thread termination. + ThreadClose(ThreadHandles[I]); + } + + CriticalSectionDelete(&CritSection); +#ifdef _WIN_ALL + CloseHandle(QueuedTasksCnt); + CloseHandle(NoneActive); +#elif defined(_UNIX) + pthread_cond_destroy(&AnyActiveCond); + pthread_mutex_destroy(&AnyActiveMutex); + pthread_cond_destroy(&QueuedTasksCntCond); + pthread_mutex_destroy(&QueuedTasksCntMutex); +#endif +} + + +void ThreadPool::CreateThreads() +{ + for(uint I=0;I<MaxAllowedThreads;I++) + { + ThreadHandles[I] = ThreadCreate(PoolThread, this); + ThreadsCreatedCount++; +#ifdef _WIN_ALL + if (ThreadPool::ThreadPriority!=THREAD_PRIORITY_NORMAL) + SetThreadPriority(ThreadHandles[I],ThreadPool::ThreadPriority); +#endif + } +} + + +NATIVE_THREAD_TYPE ThreadPool::PoolThread(void *Param) +{ + ((ThreadPool*)Param)->PoolThreadLoop(); + return 0; +} + + +void ThreadPool::PoolThreadLoop() +{ + QueueEntry Task; + while (GetQueuedTask(&Task)) + { + Task.Proc(Task.Param); + + CriticalSectionStart(&CritSection); + if (--ActiveThreads == 0) + { +#ifdef _WIN_ALL + SetEvent(NoneActive); +#elif defined(_UNIX) + pthread_mutex_lock(&AnyActiveMutex); + AnyActive=false; + pthread_cond_signal(&AnyActiveCond); + pthread_mutex_unlock(&AnyActiveMutex); +#endif + } + CriticalSectionEnd(&CritSection); + } +} + + +bool ThreadPool::GetQueuedTask(QueueEntry *Task) +{ +#ifdef _WIN_ALL + CWaitForSingleObject(QueuedTasksCnt); +#elif defined(_UNIX) + pthread_mutex_lock(&QueuedTasksCntMutex); + while (QueuedTasksCnt==0) + cpthread_cond_wait(&QueuedTasksCntCond,&QueuedTasksCntMutex); + QueuedTasksCnt--; + pthread_mutex_unlock(&QueuedTasksCntMutex); +#endif + + if (Closing) + return false; + + CriticalSectionStart(&CritSection); + + *Task = TaskQueue[QueueBottom]; + QueueBottom = (QueueBottom + 1) % ASIZE(TaskQueue); + + CriticalSectionEnd(&CritSection); + + return true; +} + + +// Add task to queue. We assume that it is always called from main thread, +// it allows to avoid any locks here. We process collected tasks only +// when WaitDone is called. +void ThreadPool::AddTask(PTHREAD_PROC Proc,void *Data) +{ + if (ThreadsCreatedCount == 0) + CreateThreads(); + + // If queue is full, wait until it is empty. + if (ActiveThreads>=ASIZE(TaskQueue)) + WaitDone(); + + TaskQueue[QueueTop].Proc = Proc; + TaskQueue[QueueTop].Param = Data; + QueueTop = (QueueTop + 1) % ASIZE(TaskQueue); + ActiveThreads++; +} + + +// Start queued tasks and wait until all threads are inactive. +// We assume that it is always called from main thread, when pool threads +// are sleeping yet. +void ThreadPool::WaitDone() +{ + if (ActiveThreads==0) + return; +#ifdef _WIN_ALL + ResetEvent(NoneActive); + ReleaseSemaphore(QueuedTasksCnt,ActiveThreads,NULL); + CWaitForSingleObject(NoneActive); +#elif defined(_UNIX) + AnyActive=true; + + // Threads reset AnyActive before accessing QueuedTasksCnt and even + // preceding WaitDone() call does not guarantee that some slow thread + // is not accessing QueuedTasksCnt now. So lock is necessary. + pthread_mutex_lock(&QueuedTasksCntMutex); + QueuedTasksCnt+=ActiveThreads; + pthread_mutex_unlock(&QueuedTasksCntMutex); + + pthread_cond_broadcast(&QueuedTasksCntCond); + + pthread_mutex_lock(&AnyActiveMutex); + while (AnyActive) + cpthread_cond_wait(&AnyActiveCond,&AnyActiveMutex); + pthread_mutex_unlock(&AnyActiveMutex); +#endif +} +#endif // RAR_SMP diff --git a/unrar/unrar/threadpool.hpp b/unrar/unrar/threadpool.hpp new file mode 100644 index 0000000..85ed90d --- /dev/null +++ b/unrar/unrar/threadpool.hpp @@ -0,0 +1,107 @@ +#ifndef _RAR_THREADPOOL_ +#define _RAR_THREADPOOL_ + +#ifndef RAR_SMP +const uint MaxPoolThreads=1; // For single threaded version. +#else +// We need to use the processor groups API to increase it beyond 64. +// Also be sure to check and adjust if needed per thread and total block size +// when compressing if going above 64. +const uint MaxPoolThreads=64; + + +#ifdef _UNIX + #include <pthread.h> + #include <semaphore.h> +#endif + +// Undefine for debugging. +#define USE_THREADS + +#ifdef _UNIX + #define NATIVE_THREAD_TYPE void* + typedef void* (*NATIVE_THREAD_PTR)(void *Data); + typedef pthread_t THREAD_HANDLE; + typedef pthread_mutex_t CRITSECT_HANDLE; +#else + #define NATIVE_THREAD_TYPE DWORD WINAPI + typedef DWORD (WINAPI *NATIVE_THREAD_PTR)(void *Data); + typedef HANDLE THREAD_HANDLE; + typedef CRITICAL_SECTION CRITSECT_HANDLE; +#endif + +typedef void (*PTHREAD_PROC)(void *Data); +#define THREAD_PROC(fn) void fn(void *Data) + +uint GetNumberOfCPU(); +uint GetNumberOfThreads(); + + +class ThreadPool +{ + private: + struct QueueEntry + { + PTHREAD_PROC Proc; + void *Param; + }; + + void CreateThreads(); + static NATIVE_THREAD_TYPE PoolThread(void *Param); + void PoolThreadLoop(); + bool GetQueuedTask(QueueEntry *Task); + + // Number of threads in the pool. Must not exceed MaxPoolThreads. + uint MaxAllowedThreads; + THREAD_HANDLE ThreadHandles[MaxPoolThreads]; + + // Number of actually created threads. + uint ThreadsCreatedCount; + + uint ActiveThreads; + + QueueEntry TaskQueue[MaxPoolThreads]; + uint QueueTop; + uint QueueBottom; + + bool Closing; // Set true to quit all threads. + +#ifdef _WIN_ALL + // Semaphore counting number of tasks stored in queue. + HANDLE QueuedTasksCnt; + + // Event signalling if no active tasks are performing now. + HANDLE NoneActive; + +#elif defined(_UNIX) + // Semaphores seem to be slower than conditional variables in pthreads, + // so we use the conditional variable to count tasks stored in queue. + uint QueuedTasksCnt; + pthread_cond_t QueuedTasksCntCond; + pthread_mutex_t QueuedTasksCntMutex; + + bool AnyActive; // Active tasks present flag. + pthread_cond_t AnyActiveCond; + pthread_mutex_t AnyActiveMutex; +#endif + + // Pool critical section. We use the single section for all branches + // to avoid deadlocks, when thread1 has section1 and wants section2 + // and thread2 has section2 and wants section1. + CRITSECT_HANDLE CritSection; + public: + ThreadPool(uint MaxThreads); + ~ThreadPool(); + void AddTask(PTHREAD_PROC Proc,void *Data); + void WaitDone(); + +#ifdef _WIN_ALL + static int ThreadPriority; + static void SetPriority(int Priority) {ThreadPriority=Priority;} +#endif +}; + +#endif // RAR_SMP + +#endif // _RAR_THREADPOOL_ + diff --git a/unrar/unrar/timefn.cpp b/unrar/unrar/timefn.cpp index 3a7b834..0abf49d 100644 --- a/unrar/unrar/timefn.cpp +++ b/unrar/unrar/timefn.cpp @@ -1,229 +1,284 @@ #include "rar.hpp" -RarTime::RarTime() +void RarTime::GetLocal(RarLocalTime *lt) { - Reset(); -} +#ifdef _WIN_ALL + FILETIME ft; + GetWinFT(&ft); + FILETIME lft; -#ifdef _WIN_32 -RarTime& RarTime::operator =(FILETIME &ft) -{ - FILETIME lft,zft; - FileTimeToLocalFileTime(&ft,&lft); - SYSTEMTIME st; - FileTimeToSystemTime(&lft,&st); - rlt.Year=st.wYear; - rlt.Month=st.wMonth; - rlt.Day=st.wDay; - rlt.Hour=st.wHour; - rlt.Minute=st.wMinute; - rlt.Second=st.wSecond; - rlt.wDay=st.wDayOfWeek; - rlt.yDay=rlt.Day-1; - for (int I=1;I<rlt.Month;I++) + if (WinNT() < WNT_VISTA) + { + // SystemTimeToTzSpecificLocalTime based code produces 1 hour error on XP. + FileTimeToLocalFileTime(&ft,&lft); + } + else { - static int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31}; - rlt.yDay+=mdays[I-1]; + // We use these functions instead of FileTimeToLocalFileTime according to + // MSDN recommendation: "To account for daylight saving time + // when converting a file time to a local time ..." + SYSTEMTIME st1,st2; + FileTimeToSystemTime(&ft,&st1); + SystemTimeToTzSpecificLocalTime(NULL,&st1,&st2); + SystemTimeToFileTime(&st2,&lft); + + // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. + FILETIME rft; + SystemTimeToFileTime(&st1,&rft); + uint64 Corrected=INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime)- + INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ + INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime); + lft.dwLowDateTime=(DWORD)Corrected; + lft.dwHighDateTime=(DWORD)(Corrected>>32); } - if (rlt.Month>2 && IsLeapYear(rlt.Year)) - rlt.yDay++; - st.wMilliseconds=0; - SystemTimeToFileTime(&st,&zft); - rlt.Reminder=lft.dwLowDateTime-zft.dwLowDateTime; - return(*this); + SYSTEMTIME st; + FileTimeToSystemTime(&lft,&st); + lt->Year=st.wYear; + lt->Month=st.wMonth; + lt->Day=st.wDay; + lt->Hour=st.wHour; + lt->Minute=st.wMinute; + lt->Second=st.wSecond; + lt->wDay=st.wDayOfWeek; + lt->yDay=lt->Day-1; + + static int mdays[12]={31,28,31,30,31,30,31,31,30,31,30,31}; + for (uint I=1;I<lt->Month && I<=ASIZE(mdays);I++) + lt->yDay+=mdays[I-1]; + + if (lt->Month>2 && IsLeapYear(lt->Year)) + lt->yDay++; +#else + time_t ut=GetUnix(); + struct tm *t; + t=localtime(&ut); + + lt->Year=t->tm_year+1900; + lt->Month=t->tm_mon+1; + lt->Day=t->tm_mday; + lt->Hour=t->tm_hour; + lt->Minute=t->tm_min; + lt->Second=t->tm_sec; + lt->wDay=t->tm_wday; + lt->yDay=t->tm_yday; +#endif + lt->Reminder=(itime % TICKS_PER_SECOND); } -void RarTime::GetWin32(FILETIME *ft) +void RarTime::SetLocal(RarLocalTime *lt) { +#ifdef _WIN_ALL SYSTEMTIME st; - st.wYear=rlt.Year; - st.wMonth=rlt.Month; - st.wDay=rlt.Day; - st.wHour=rlt.Hour; - st.wMinute=rlt.Minute; - st.wSecond=rlt.Second; + st.wYear=lt->Year; + st.wMonth=lt->Month; + st.wDay=lt->Day; + st.wHour=lt->Hour; + st.wMinute=lt->Minute; + st.wSecond=lt->Second; st.wMilliseconds=0; + st.wDayOfWeek=0; FILETIME lft; - SystemTimeToFileTime(&st,&lft); - lft.dwLowDateTime+=rlt.Reminder; - if (lft.dwLowDateTime<rlt.Reminder) - lft.dwHighDateTime++; - LocalFileTimeToFileTime(&lft,ft); -} -#endif + if (SystemTimeToFileTime(&st,&lft)) + { + FILETIME ft; + if (WinNT() < WNT_VISTA) + { + // TzSpecificLocalTimeToSystemTime based code produces 1 hour error on XP. + LocalFileTimeToFileTime(&lft,&ft); + } + else + { + // Reverse procedure which we do in GetLocal. + SYSTEMTIME st1,st2; + FileTimeToSystemTime(&lft,&st2); // st2 might be unequal to st, because we added lt->Reminder to lft. + TzSpecificLocalTimeToSystemTime(NULL,&st2,&st1); + SystemTimeToFileTime(&st1,&ft); + + // Correct precision loss (low 4 decimal digits) in FileTimeToSystemTime. + FILETIME rft; + SystemTimeToFileTime(&st2,&rft); + uint64 Corrected=INT32TO64(lft.dwHighDateTime,lft.dwLowDateTime)- + INT32TO64(rft.dwHighDateTime,rft.dwLowDateTime)+ + INT32TO64(ft.dwHighDateTime,ft.dwLowDateTime); + ft.dwLowDateTime=(DWORD)Corrected; + ft.dwHighDateTime=(DWORD)(Corrected>>32); + } -#if defined(_UNIX) || defined(_EMX) -RarTime& RarTime::operator =(time_t ut) -{ - struct tm *t; - t=localtime(&ut); + SetWinFT(&ft); + } + else + Reset(); +#else + struct tm t; - rlt.Year=t->tm_year+1900; - rlt.Month=t->tm_mon+1; - rlt.Day=t->tm_mday; - rlt.Hour=t->tm_hour; - rlt.Minute=t->tm_min; - rlt.Second=t->tm_sec; - rlt.Reminder=0; - rlt.wDay=t->tm_wday; - rlt.yDay=t->tm_yday; - return(*this); + t.tm_sec=lt->Second; + t.tm_min=lt->Minute; + t.tm_hour=lt->Hour; + t.tm_mday=lt->Day; + t.tm_mon=lt->Month-1; + t.tm_year=lt->Year-1900; + t.tm_isdst=-1; + SetUnix(mktime(&t)); +#endif + itime+=lt->Reminder; } -time_t RarTime::GetUnix() -{ - struct tm t; - t.tm_sec=rlt.Second; - t.tm_min=rlt.Minute; - t.tm_hour=rlt.Hour; - t.tm_mday=rlt.Day; - t.tm_mon=rlt.Month-1; - t.tm_year=rlt.Year-1900; - t.tm_isdst=-1; - return(mktime(&t)); + +#ifdef _WIN_ALL +void RarTime::GetWinFT(FILETIME *ft) +{ + _ULARGE_INTEGER ul; + ul.QuadPart=GetWin(); + ft->dwLowDateTime=ul.LowPart; + ft->dwHighDateTime=ul.HighPart; } -#endif -Int64 RarTime::GetRaw() +void RarTime::SetWinFT(FILETIME *ft) { - if (!IsSet()) - return(0); -#ifdef _WIN_32 - FILETIME ft; - GetWin32(&ft); - return(int32to64(ft.dwHighDateTime,ft.dwLowDateTime)); -#elif defined(_UNIX) || defined(_EMX) - time_t ut=GetUnix(); - return(int32to64(0,ut)*10000000+rlt.Reminder); -#else - return(0); -#endif + _ULARGE_INTEGER ul = {ft->dwLowDateTime, ft->dwHighDateTime}; + SetWin(ul.QuadPart); } +#endif -#ifndef SFX_MODULE -void RarTime::SetRaw(Int64 RawTime) +// Get 64-bit representation of Windows FILETIME (100ns since 01.01.1601). +uint64 RarTime::GetWin() { -#ifdef _WIN_32 - FILETIME ft; - ft.dwHighDateTime=int64to32(RawTime>>32); - ft.dwLowDateTime=int64to32(RawTime); - *this=ft; -#elif defined(_UNIX) || defined(_EMX) - time_t ut=int64to32(RawTime/10000000); - *this=ut; - rlt.Reminder=int64to32(RawTime%10000000); -#endif + return itime/(TICKS_PER_SECOND/10000000); } -#endif -bool RarTime::operator == (RarTime &rt) +// Set 64-bit representation of Windows FILETIME (100ns since 01.01.1601). +void RarTime::SetWin(uint64 WinTime) { - return(rlt.Year==rt.rlt.Year && rlt.Month==rt.rlt.Month && - rlt.Day==rt.rlt.Day && rlt.Hour==rt.rlt.Hour && - rlt.Minute==rt.rlt.Minute && rlt.Second==rt.rlt.Second && - rlt.Reminder==rt.rlt.Reminder); + itime=WinTime*(TICKS_PER_SECOND/10000000); } -bool RarTime::operator < (RarTime &rt) +time_t RarTime::GetUnix() { - return(GetRaw()<rt.GetRaw()); + return time_t(GetUnixNS()/1000000000); } -bool RarTime::operator <= (RarTime &rt) +void RarTime::SetUnix(time_t ut) { - return(*this<rt || *this==rt); + if (sizeof(ut)>4) + SetUnixNS(uint64(ut)*1000000000); + else + { + // Convert 32-bit and possibly signed time_t to uint32 first, + // uint64 cast is not enough. Otherwise sign can expand to 64 bits. + SetUnixNS(uint64(uint32(ut))*1000000000); + } } -bool RarTime::operator > (RarTime &rt) +// Get the high precision Unix time in nanoseconds since 01-01-1970. +uint64 RarTime::GetUnixNS() { - return(GetRaw()>rt.GetRaw()); + // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. + uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); + return itime*(1000000000/TICKS_PER_SECOND)-ushift; } -bool RarTime::operator >= (RarTime &rt) +// Set the high precision Unix time in nanoseconds since 01-01-1970. +void RarTime::SetUnixNS(uint64 ns) { - return(*this>rt || *this==rt); + // 11644473600000000000 - number of ns between 01-01-1601 and 01-01-1970. + uint64 ushift=INT32TO64(0xA1997B0B,0x4C6A0000); + itime=(ns+ushift)/(1000000000/TICKS_PER_SECOND); } uint RarTime::GetDos() { - uint DosTime=(rlt.Second/2)|(rlt.Minute<<5)|(rlt.Hour<<11)| - (rlt.Day<<16)|(rlt.Month<<21)|((rlt.Year-1980)<<25); - return(DosTime); + RarLocalTime lt; + GetLocal(<); + uint DosTime=(lt.Second/2)|(lt.Minute<<5)|(lt.Hour<<11)| + (lt.Day<<16)|(lt.Month<<21)|((lt.Year-1980)<<25); + return DosTime; } void RarTime::SetDos(uint DosTime) { - rlt.Second=(DosTime & 0x1f)*2; - rlt.Minute=(DosTime>>5) & 0x3f; - rlt.Hour=(DosTime>>11) & 0x1f; - rlt.Day=(DosTime>>16) & 0x1f; - rlt.Month=(DosTime>>21) & 0x0f; - rlt.Year=(DosTime>>25)+1980; - rlt.Reminder=0; + RarLocalTime lt; + lt.Second=(DosTime & 0x1f)*2; + lt.Minute=(DosTime>>5) & 0x3f; + lt.Hour=(DosTime>>11) & 0x1f; + lt.Day=(DosTime>>16) & 0x1f; + lt.Month=(DosTime>>21) & 0x0f; + lt.Year=(DosTime>>25)+1980; + lt.Reminder=0; + SetLocal(<); } -#if !defined(GUI) || !defined(SFX_MODULE) -void RarTime::GetText(char *DateStr,bool FullYear) +void RarTime::GetText(wchar *DateStr,size_t MaxSize,bool FullMS) { - if (FullYear) - sprintf(DateStr,"%02u-%02u-%u %02u:%02u",rlt.Day,rlt.Month,rlt.Year,rlt.Hour,rlt.Minute); + if (IsSet()) + { + RarLocalTime lt; + GetLocal(<); + if (FullMS) + swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u:%02u,%09u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute,lt.Second,lt.Reminder*(1000000000/TICKS_PER_SECOND)); + else + swprintf(DateStr,MaxSize,L"%u-%02u-%02u %02u:%02u",lt.Year,lt.Month,lt.Day,lt.Hour,lt.Minute); + } else - sprintf(DateStr,"%02u-%02u-%02u %02u:%02u",rlt.Day,rlt.Month,rlt.Year%100,rlt.Hour,rlt.Minute); + { + // We use escape before '?' to avoid weird C trigraph characters. + wcsncpyz(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?",MaxSize); + } } -#endif #ifndef SFX_MODULE -void RarTime::SetIsoText(char *TimeText) +void RarTime::SetIsoText(const wchar *TimeText) { int Field[6]; memset(Field,0,sizeof(Field)); - for (int DigitCount=0;*TimeText!=0;TimeText++) - if (isdigit(*TimeText)) + for (uint DigitCount=0;*TimeText!=0;TimeText++) + if (IsDigit(*TimeText)) { int FieldPos=DigitCount<4 ? 0:(DigitCount-4)/2+1; - if (FieldPos<sizeof(Field)/sizeof(Field[0])) + if (FieldPos<ASIZE(Field)) Field[FieldPos]=Field[FieldPos]*10+*TimeText-'0'; DigitCount++; } - rlt.Second=Field[5]; - rlt.Minute=Field[4]; - rlt.Hour=Field[3]; - rlt.Day=Field[2]==0 ? 1:Field[2]; - rlt.Month=Field[1]==0 ? 1:Field[1]; - rlt.Year=Field[0]; - rlt.Reminder=0; + RarLocalTime lt; + lt.Second=Field[5]; + lt.Minute=Field[4]; + lt.Hour=Field[3]; + lt.Day=Field[2]==0 ? 1:Field[2]; + lt.Month=Field[1]==0 ? 1:Field[1]; + lt.Year=Field[0]; + lt.Reminder=0; + SetLocal(<); } #endif #ifndef SFX_MODULE -void RarTime::SetAgeText(char *TimeText) +void RarTime::SetAgeText(const wchar *TimeText) { uint Seconds=0,Value=0; - for (int I=0;TimeText[I]!=0;I++) + for (uint I=0;TimeText[I]!=0;I++) { - int Ch=TimeText[I]; - if (isdigit(Ch)) + wchar Ch=TimeText[I]; + if (IsDigit(Ch)) Value=Value*10+Ch-'0'; else { - switch(etoupper(Ch)) + switch(etoupperw(Ch)) { case 'D': Seconds+=Value*24*3600; @@ -242,45 +297,44 @@ void RarTime::SetAgeText(char *TimeText) } } SetCurrentTime(); - Int64 RawTime=GetRaw(); - SetRaw(RawTime-int32to64(0,Seconds)*10000000); + itime-=uint64(Seconds)*TICKS_PER_SECOND; } #endif void RarTime::SetCurrentTime() { -#ifdef _WIN_32 +#ifdef _WIN_ALL FILETIME ft; SYSTEMTIME st; GetSystemTime(&st); SystemTimeToFileTime(&st,&ft); - *this=ft; + SetWinFT(&ft); #else time_t st; time(&st); - *this=st; + SetUnix(st); #endif } -#if !defined(SFX_MODULE) && !defined(_WIN_CE) -const char *GetMonthName(int Month) +// Add the specified signed number of nanoseconds. +void RarTime::Adjust(int64 ns) { -#ifdef SILENT - return(""); -#else - static MSGID MonthID[]={ - MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, - MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec - }; - return(St(MonthID[Month])); -#endif + ns/=1000000000/TICKS_PER_SECOND; // Convert ns to internal ticks. + itime+=(uint64)ns; +} + + +#ifndef SFX_MODULE +const wchar *GetMonthName(int Month) +{ + return uiGetMonthName(Month); } #endif bool IsLeapYear(int Year) { - return((Year&3)==0 && (Year%100!=0 || Year%400==0)); + return (Year&3)==0 && (Year%100!=0 || Year%400==0); } diff --git a/unrar/unrar/timefn.hpp b/unrar/unrar/timefn.hpp index 9ab5032..49b61e8 100644 --- a/unrar/unrar/timefn.hpp +++ b/unrar/unrar/timefn.hpp @@ -9,7 +9,7 @@ struct RarLocalTime uint Hour; uint Minute; uint Second; - uint Reminder; + uint Reminder; // Part of time smaller than 1 second, represented in 1/REMINDER_PRECISION intervals. uint wDay; uint yDay; }; @@ -18,40 +18,59 @@ struct RarLocalTime class RarTime { private: - Int64 GetRaw(); - void SetRaw(Int64 RawTime); + static const uint TICKS_PER_SECOND = 1000000000; // Internal precision. - RarLocalTime rlt; - - Int64 Time; + // Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601. + // We use nanoseconds here to handle the high precision Unix time. + // It allows dates up to July 2185. + // + // If we'll ever need to extend the date range, we can define a lower + // precision Windows version of TICKS_PER_SECOND. But then Unix and Windows + // versions can differ in least significant digits of "lt" time output + // for Unix archives. + // Alternatively we can introduce 'bool HighPrecision' set to true + // in SetUnixNS() and TicksPerSecond() instead of constant above. + // It might be more reliable than defining TicksPerSecond variable, + // which wouldn't survive memset of any structure hosting RarTime. + // We would need to eliminate all such memsets in the entire code first. + uint64 itime; + public: + // RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND. + // Unlike TICKS_PER_SECOND, it is a public field. + static const uint REMINDER_PRECISION = TICKS_PER_SECOND; public: - RarTime(); -#ifdef _WIN_32 - RarTime& operator =(FILETIME &ft); - void GetWin32(FILETIME *ft); + RarTime() {Reset();} + bool operator == (RarTime &rt) {return itime==rt.itime;} + bool operator != (RarTime &rt) {return itime!=rt.itime;} + bool operator < (RarTime &rt) {return itime<rt.itime;} + bool operator <= (RarTime &rt) {return itime<rt.itime || itime==rt.itime;} + bool operator > (RarTime &rt) {return itime>rt.itime;} + bool operator >= (RarTime &rt) {return itime>rt.itime || itime==rt.itime;} + + void GetLocal(RarLocalTime *lt); + void SetLocal(RarLocalTime *lt); +#ifdef _WIN_ALL + void GetWinFT(FILETIME *ft); + void SetWinFT(FILETIME *ft); #endif -#if defined(_UNIX) || defined(_EMX) - RarTime& operator =(time_t ut); + uint64 GetWin(); + void SetWin(uint64 WinTime); time_t GetUnix(); -#endif - bool operator == (RarTime &rt); - bool operator < (RarTime &rt); - bool operator <= (RarTime &rt); - bool operator > (RarTime &rt); - bool operator >= (RarTime &rt); - void GetLocal(RarLocalTime *lt) {*lt=rlt;} - void SetLocal(RarLocalTime *lt) {rlt=*lt;} + void SetUnix(time_t ut); + uint64 GetUnixNS(); + void SetUnixNS(uint64 ns); uint GetDos(); void SetDos(uint DosTime); - void GetText(char *DateStr,bool FullYear); - void SetIsoText(char *TimeText); - void SetAgeText(char *TimeText); + void GetText(wchar *DateStr,size_t MaxSize,bool FullMS); + void SetIsoText(const wchar *TimeText); + void SetAgeText(const wchar *TimeText); void SetCurrentTime(); - void Reset() {rlt.Year=0;} - bool IsSet() {return(rlt.Year!=0);} + void Reset() {itime=0;} + bool IsSet() {return itime!=0;} + void Adjust(int64 ns); }; -const char *GetMonthName(int Month); +const wchar *GetMonthName(int Month); bool IsLeapYear(int Year); #endif diff --git a/unrar/unrar/ui.cpp b/unrar/unrar/ui.cpp new file mode 100644 index 0000000..9713a88 --- /dev/null +++ b/unrar/unrar/ui.cpp @@ -0,0 +1,14 @@ +#include "rar.hpp" + +#include "uicommon.cpp" + +#ifdef SILENT +#include "uisilent.cpp" +#else + + + + +#include "uiconsole.cpp" + +#endif diff --git a/unrar/unrar/ui.hpp b/unrar/unrar/ui.hpp new file mode 100644 index 0000000..5def26d --- /dev/null +++ b/unrar/unrar/ui.hpp @@ -0,0 +1,176 @@ +#ifndef _RAR_UI_ +#define _RAR_UI_ + +// UIERROR_ - error message; +// UIMSG_ - informational message; +// UIWAIT_ - message waiting for user confirmation; +// UIEVENT_ - if simple message is not enough; + +enum UIMESSAGE_CODE { + UIERROR_SYSERRMSG, UIERROR_GENERALERRMSG, UIERROR_INCERRCOUNT, + UIERROR_CHECKSUM, UIERROR_CHECKSUMENC, UIERROR_CHECKSUMPACKED, + UIERROR_BADPSW, UIERROR_MEMORY, UIERROR_FILEOPEN, UIERROR_FILECREATE, + UIERROR_FILECLOSE, UIERROR_FILESEEK, UIERROR_FILEREAD, UIERROR_FILEWRITE, + UIERROR_FILEDELETE, UIERROR_RECYCLEFAILED, UIERROR_FILERENAME, + UIERROR_FILEATTR, UIERROR_FILECOPY, UIERROR_FILECOPYHINT, + UIERROR_DIRCREATE, UIERROR_SLINKCREATE, UIERROR_HLINKCREATE, + UIERROR_NOLINKTARGET, UIERROR_NEEDADMIN, UIERROR_ARCBROKEN, + UIERROR_HEADERBROKEN, UIERROR_MHEADERBROKEN, UIERROR_FHEADERBROKEN, + UIERROR_SUBHEADERBROKEN, UIERROR_SUBHEADERUNKNOWN, + UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD, + UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR, + UIERROR_NOTSFX, UIERROR_OLDTOSFX, + UIERROR_WRONGSFXVER, UIERROR_HEADENCMISMATCH, UIERROR_DICTOUTMEM, + UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD, + UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME, + UIERROR_NOTFIRSTVOLUME, UIERROR_RECVOLLIMIT, UIERROR_RECVOLDIFFSETS, + UIERROR_RECVOLALLEXIST, UIERROR_RECVOLFOUND, UIERROR_RECONSTRUCTING, + UIERROR_RECVOLCANNOTFIX, UIERROR_OPFAILED, UIERROR_UNEXPEOF, + UIERROR_BADARCHIVE, UIERROR_CMTBROKEN, UIERROR_INVALIDNAME, + UIERROR_NEWRARFORMAT, UIERROR_NOTSUPPORTED, UIERROR_ENCRNOTSUPPORTED, + UIERROR_RARZIPONLY, UIERROR_REPAIROLDFORMAT, UIERROR_NOFILESREPAIRED, + UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE, UIERROR_NOFILESTOEXTRACT, + UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL, UIERROR_UNKNOWNEXTRA, + UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED, UIERROR_ZIPVOLSFX, + UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX, UIERROR_EMAIL, + UIERROR_ACLGET, UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN, UIERROR_ACLSET, + UIERROR_STREAMBROKEN, UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH, + UIERROR_PATHTOOLONG, UIERROR_DIRSCAN, UIERROR_UOWNERGET, + UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID, + UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST, + UIERROR_OPENPRESERVEATIME, UIERROR_READERRTRUNCATED, UIERROR_READERRCOUNT, + UIERROR_DIRNAMEEXISTS,UIERROR_TRUNCPSW,UIERROR_ADJUSTVALUE, + + UIMSG_FIRST, + UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA, + UIMSG_RRFOUND, UIMSG_RRNOTFOUND, UIMSG_RRDAMAGED, UIMSG_BLOCKSRECOVERED, + UIMSG_COPYINGDATA, UIMSG_AREADAMAGED, UIMSG_SECTORDAMAGED, + UIMSG_SECTORRECOVERED, UIMSG_SECTORNOTRECOVERED, UIMSG_FOUND, + UIMSG_CORRECTINGNAME, UIMSG_BADARCHIVE, UIMSG_CREATING, UIMSG_RENAMING, + UIMSG_RECVOLCALCCHECKSUM, UIMSG_RECVOLFOUND, UIMSG_RECVOLMISSING, + UIMSG_MISSINGVOL, UIMSG_RECONSTRUCTING, UIMSG_CHECKSUM, UIMSG_FAT32SIZE, + UIMSG_SKIPENCARC, UIMSG_FILERENAME, + + UIWAIT_FIRST, + UIWAIT_DISKFULLNEXT, UIWAIT_FCREATEERROR, UIWAIT_BADPSW, + + UIEVENT_FIRST, + UIEVENT_SEARCHDUPFILESSTART, UIEVENT_SEARCHDUPFILESEND, + UIEVENT_CLEARATTRSTART, UIEVENT_CLEARATTRFILE, + UIEVENT_DELADDEDSTART, UIEVENT_DELADDEDFILE, UIEVENT_FILESFOUND, + UIEVENT_ERASEDISK, UIEVENT_FILESUMSTART, UIEVENT_FILESUMPROGRESS, + UIEVENT_FILESUMEND, UIEVENT_PROTECTSTART, UIEVENT_PROTECTEND, + UIEVENT_TESTADDEDSTART, UIEVENT_TESTADDEDEND, UIEVENT_RRTESTINGSTART, + UIEVENT_RRTESTINGEND, UIEVENT_NEWARCHIVE, UIEVENT_NEWREVFILE +}; + +// Flags for uiAskReplace function. +enum UIASKREP_FLAGS { + UIASKREP_F_NORENAME=1,UIASKREP_F_EXCHSRCDEST=2,UIASKREP_F_SHOWNAMEONLY=4 +}; + +// Codes returned by uiAskReplace. Note that uiAskReplaceEx returns only +// UIASKREP_R_REPLACE, UIASKREP_R_SKIP and UIASKREP_R_CANCEL codes. +enum UIASKREP_RESULT { + UIASKREP_R_REPLACE,UIASKREP_R_SKIP,UIASKREP_R_REPLACEALL,UIASKREP_R_SKIPALL, + UIASKREP_R_RENAME,UIASKREP_R_RENAMEAUTO,UIASKREP_R_CANCEL,UIASKREP_R_UNUSED +}; + +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); +UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags); + +void uiInit(SOUND_NOTIFY_MODE Sound); + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName); +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip); +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize); +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize); + +enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE}; +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password,CheckPassword *CheckPwd); +bool uiIsGlobalPasswordSet(); + +enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION}; +void uiAlarm(UIALARM_TYPE Type); + +void uiEolAfterMsg(); + +bool uiAskNextVolume(wchar *VolName,size_t MaxSize); +#if !defined(SILENT) && !defined(SFX_MODULE) +void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit); +#endif +bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull); + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month); +#endif + +class uiMsgStore +{ + private: + static const size_t MAX_MSG = 8; + const wchar *Str[MAX_MSG]; + uint Num[MAX_MSG]; + uint StrSize,NumSize; + UIMESSAGE_CODE Code; + public: + uiMsgStore(UIMESSAGE_CODE Code) + { + // Init arrays in case a caller passes fewer parameters than expected. + for (uint I=0;I<ASIZE(Str);I++) + Str[I]=L""; + memset(Num,0,sizeof(Num)); + + NumSize=StrSize=0; + this->Code=Code; + } + uiMsgStore& operator << (const wchar *s) + { + if (StrSize<MAX_MSG) + Str[StrSize++]=s; + return *this; + } + uiMsgStore& operator << (uint n) + { + if (NumSize<MAX_MSG) + Num[NumSize++]=n; + return *this; + } + + void Msg(); +}; + + +// Templates recognize usual NULL as integer, not wchar*. +#define UINULL ((wchar *)NULL) + +inline void uiMsgBase(uiMsgStore &Store) +{ + // Called last, when no parameters are left. +} + +template<class T1,class... TN> void uiMsgBase(uiMsgStore &Store,T1&& a1,TN&&... aN) +{ + // Process first parameter and pass the rest to same uiMsgBase. + Store<<a1; + uiMsgBase(Store,aN...); +} + + +// Use variadic templates. +// +// We must pass variable parameters by reference, so no temporary copies are +// created for custom string objects like CStringBase in 7-Zip decompression +// code. Such temporary copies would be destroyed inside of recursive +// uiMsgBase calls, leaving us with Str[] items pointing at released memory. +// Since we pass integer values as well, we can't use & references +// and must resort to && rvalue references. +template<class... TN> void uiMsg(UIMESSAGE_CODE Code,TN&&... aN) +{ + uiMsgStore Store(Code); + uiMsgBase(Store,aN...); + Store.Msg(); +} + +#endif diff --git a/unrar/unrar/uicommon.cpp b/unrar/unrar/uicommon.cpp new file mode 100644 index 0000000..1a12011 --- /dev/null +++ b/unrar/unrar/uicommon.cpp @@ -0,0 +1,65 @@ +static SOUND_NOTIFY_MODE uiSoundNotify; + +void uiInit(SOUND_NOTIFY_MODE Sound) +{ + uiSoundNotify = Sound; +} + + +// Additionally to handling user input, it analyzes and sets command options. +// Returns only 'replace', 'skip' and 'cancel' codes. +UIASKREP_RESULT uiAskReplaceEx(CommandData *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + if (Cmd->Overwrite==OVERWRITE_NONE) + return UIASKREP_R_SKIP; + +#if !defined(SFX_MODULE) && !defined(SILENT) + // Must be before Cmd->AllYes check or -y switch would override -or. + if (Cmd->Overwrite==OVERWRITE_AUTORENAME && GetAutoRenamedName(Name,MaxNameSize)) + return UIASKREP_R_REPLACE; +#endif + + // This check must be after OVERWRITE_AUTORENAME processing or -y switch + // would override -or. + if (Cmd->AllYes || Cmd->Overwrite==OVERWRITE_ALL) + { + PrepareToDelete(Name); + return UIASKREP_R_REPLACE; + } + + wchar NewName[NM]; + wcsncpyz(NewName,Name,ASIZE(NewName)); + UIASKREP_RESULT Choice=uiAskReplace(NewName,ASIZE(NewName),FileSize,FileTime,Flags); + + if (Choice==UIASKREP_R_REPLACE || Choice==UIASKREP_R_REPLACEALL) + PrepareToDelete(Name); + + if (Choice==UIASKREP_R_REPLACEALL) + { + Cmd->Overwrite=OVERWRITE_ALL; + return UIASKREP_R_REPLACE; + } + if (Choice==UIASKREP_R_SKIPALL) + { + Cmd->Overwrite=OVERWRITE_NONE; + return UIASKREP_R_SKIP; + } + if (Choice==UIASKREP_R_RENAME) + { + if (PointToName(NewName)==NewName) + SetName(Name,NewName,MaxNameSize); + else + wcsncpyz(Name,NewName,MaxNameSize); + if (FileExist(Name)) + return uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,Flags); + return UIASKREP_R_REPLACE; + } +#if !defined(SFX_MODULE) && !defined(SILENT) + if (Choice==UIASKREP_R_RENAMEAUTO && GetAutoRenamedName(Name,MaxNameSize)) + { + Cmd->Overwrite=OVERWRITE_AUTORENAME; + return UIASKREP_R_REPLACE; + } +#endif + return Choice; +} diff --git a/unrar/unrar/uiconsole.cpp b/unrar/unrar/uiconsole.cpp new file mode 100644 index 0000000..b524c25 --- /dev/null +++ b/unrar/unrar/uiconsole.cpp @@ -0,0 +1,484 @@ +static bool AnyMessageDisplayed=false; // For console -idn switch. + +// Purely user interface function. Gets and returns user input. +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + wchar SizeText1[20],DateStr1[50],SizeText2[20],DateStr2[50]; + + FindData ExistingFD; + memset(&ExistingFD,0,sizeof(ExistingFD)); // In case find fails. + FindFile::FastFind(Name,&ExistingFD); + itoa(ExistingFD.Size,SizeText1,ASIZE(SizeText1)); + ExistingFD.mtime.GetText(DateStr1,ASIZE(DateStr1),false); + + if (FileSize==INT64NDF || FileTime==NULL) + { + eprintf(L"\n"); + eprintf(St(MAskOverwrite),Name); + } + else + { + itoa(FileSize,SizeText2,ASIZE(SizeText2)); + FileTime->GetText(DateStr2,ASIZE(DateStr2),false); + if ((Flags & UIASKREP_F_EXCHSRCDEST)==0) + eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2); + else + eprintf(St(MAskReplace),Name,SizeText2,DateStr2,SizeText1,DateStr1); + } + + bool AllowRename=(Flags & UIASKREP_F_NORENAME)==0; + int Choice=0; + do + { + Choice=Ask(St(AllowRename ? MYesNoAllRenQ : MYesNoAllQ)); + } while (Choice==0); // 0 means invalid input. + switch(Choice) + { + case 1: + return UIASKREP_R_REPLACE; + case 2: + return UIASKREP_R_SKIP; + case 3: + return UIASKREP_R_REPLACEALL; + case 4: + return UIASKREP_R_SKIPALL; + } + if (AllowRename && Choice==5) + { + mprintf(St(MAskNewName)); + if (getwstr(Name,MaxNameSize)) + return UIASKREP_R_RENAME; + else + return UIASKREP_R_SKIP; // Process fwgets failure as if user answered 'No'. + } + return UIASKREP_R_CANCEL; +} + + + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +{ + mprintf(St(Extract ? MExtracting : MExtrTest), ArcName); +} + + +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +{ + return true; +} + + +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) +{ + // We set the total size to 0 to update only the current progress and keep + // the total progress intact in WinRAR. Unlike WinRAR, console RAR has only + // the total progress and updates it with current values in such case. + int CurPercent=TotalSize!=0 ? ToPercent(CurSize,TotalSize) : ToPercent(CurFileSize,TotalFileSize); + mprintf(L"\b\b\b\b%3d%%",CurPercent); +} + + +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) +{ + int CurPercent=ToPercent(CurSize,TotalSize); + mprintf(L"\b\b\b\b%3d%%",CurPercent); +} + + +void uiMsgStore::Msg() +{ + // When creating volumes, AnyMessageDisplayed must be reset for UIEVENT_NEWARCHIVE, + // so it ignores this and all earlier messages like UIEVENT_PROTECTEND + // and UIEVENT_PROTECTEND, because they precede "Creating archive" message + // and do not interfere with -idn and file names. If we do not ignore them, + // uiEolAfterMsg() in uiStartFileAddit() can cause unneeded carriage return + // in archiving percent after creating a new volume with -v -idn (and -rr + // for UIEVENT_PROTECT*) switches. AnyMessageDisplayed is set for messages + // after UIEVENT_NEWARCHIVE, so archiving percent with -idn is moved to + // next line and does not delete their last characters. + // Similarly we ignore UIEVENT_RRTESTINGEND for volumes, because it is issued + // before "Testing archive" and would add an excessive \n otherwise. + AnyMessageDisplayed=(Code!=UIEVENT_NEWARCHIVE && Code!=UIEVENT_RRTESTINGEND); + + switch(Code) + { + case UIERROR_SYSERRMSG: + case UIERROR_GENERALERRMSG: + Log(NULL,L"\n%ls",Str[0]); + break; + case UIERROR_CHECKSUM: + Log(Str[0],St(MCRCFailed),Str[1]); + break; + case UIERROR_CHECKSUMENC: + Log(Str[0],St(MEncrBadCRC),Str[1]); + break; + case UIERROR_CHECKSUMPACKED: + Log(Str[0],St(MDataBadCRC),Str[1],Str[0]); + break; + case UIERROR_BADPSW: + Log(Str[0],St(MWrongFilePassword),Str[1]); + break; + case UIWAIT_BADPSW: + Log(Str[0],St(MWrongPassword)); + break; + case UIERROR_MEMORY: + mprintf(L"\n"); + Log(NULL,St(MErrOutMem)); + break; + case UIERROR_FILEOPEN: + Log(Str[0],St(MCannotOpen),Str[1]); + break; + case UIERROR_FILECREATE: + Log(Str[0],St(MCannotCreate),Str[1]); + break; + case UIERROR_FILECLOSE: + Log(NULL,St(MErrFClose),Str[0]); + break; + case UIERROR_FILESEEK: + Log(NULL,St(MErrSeek),Str[0]); + break; + case UIERROR_FILEREAD: + mprintf(L"\n"); + Log(Str[0],St(MErrRead),Str[1]); + break; + case UIERROR_FILEWRITE: + Log(Str[0],St(MErrWrite),Str[1]); + break; +#ifndef SFX_MODULE + case UIERROR_FILEDELETE: + Log(Str[0],St(MCannotDelete),Str[1]); + break; + case UIERROR_RECYCLEFAILED: + Log(Str[0],St(MRecycleFailed)); + break; + case UIERROR_FILERENAME: + Log(Str[0],St(MErrRename),Str[1],Str[2]); + break; +#endif + case UIERROR_FILEATTR: + Log(Str[0],St(MErrChangeAttr),Str[1]); + break; + case UIERROR_FILECOPY: + Log(Str[0],St(MCopyError),Str[1],Str[2]); + break; + case UIERROR_FILECOPYHINT: + Log(Str[0],St(MCopyErrorHint)); + mprintf(L" "); // For progress percent. + break; + case UIERROR_DIRCREATE: + Log(Str[0],St(MExtrErrMkDir),Str[1]); + break; + case UIERROR_SLINKCREATE: + Log(Str[0],St(MErrCreateLnkS),Str[1]); + break; + case UIERROR_HLINKCREATE: + Log(NULL,St(MErrCreateLnkH),Str[0]); + break; + case UIERROR_NOLINKTARGET: + Log(NULL,St(MErrLnkTarget)); + mprintf(L" "); // For progress percent. + break; + case UIERROR_NEEDADMIN: + Log(NULL,St(MNeedAdmin)); + break; + case UIERROR_ARCBROKEN: + mprintf(L"\n"); // So it is not merged with preceding UIERROR_HEADERBROKEN. + Log(Str[0],St(MErrBrokenArc)); + break; + case UIERROR_HEADERBROKEN: + Log(Str[0],St(MHeaderBroken)); + break; + case UIERROR_MHEADERBROKEN: + Log(Str[0],St(MMainHeaderBroken)); + break; + case UIERROR_FHEADERBROKEN: + Log(Str[0],St(MLogFileHead),Str[1]); + break; + case UIERROR_SUBHEADERBROKEN: + Log(Str[0],St(MSubHeadCorrupt)); + break; + case UIERROR_SUBHEADERUNKNOWN: + Log(Str[0],St(MSubHeadUnknown)); + break; + case UIERROR_SUBHEADERDATABROKEN: + Log(Str[0],St(MSubHeadDataCRC),Str[1]); + break; + case UIERROR_RRDAMAGED: + Log(Str[0],St(MRRDamaged)); + break; + case UIERROR_UNKNOWNMETHOD: + Log(Str[0],St(MUnknownMeth),Str[1]); + break; + case UIERROR_UNKNOWNENCMETHOD: + { + wchar Msg[256]; + swprintf(Msg,ASIZE(Msg),St(MUnkEncMethod),Str[1]); + Log(Str[0],L"%s: %s",Msg,Str[2]); + } + break; +#ifndef SFX_MODULE + case UIERROR_RENAMING: + Log(Str[0],St(MRenaming),Str[1],Str[2]); + break; + case UIERROR_NEWERRAR: + Log(Str[0],St(MNewerRAR)); + break; +#endif + case UIERROR_RECVOLDIFFSETS: + Log(NULL,St(MRecVolDiffSets),Str[0],Str[1]); + break; + case UIERROR_RECVOLALLEXIST: + mprintf(St(MRecVolAllExist)); + break; + case UIERROR_RECONSTRUCTING: + mprintf(St(MReconstructing)); + break; + case UIERROR_RECVOLCANNOTFIX: + mprintf(St(MRecVolCannotFix)); + break; + case UIERROR_UNEXPEOF: + Log(Str[0],St(MLogUnexpEOF)); + break; + case UIERROR_BADARCHIVE: + Log(Str[0],St(MBadArc),Str[0]); + break; + case UIERROR_CMTBROKEN: + Log(Str[0],St(MLogCommBrk)); + break; + case UIERROR_INVALIDNAME: + Log(Str[0],St(MInvalidName),Str[1]); + mprintf(L"\n"); // Needed when called from CmdExtract::ExtractCurrentFile. + break; +#ifndef SFX_MODULE + case UIERROR_OPFAILED: + Log(NULL,St(MOpFailed)); + break; + case UIERROR_NEWRARFORMAT: + Log(Str[0],St(MNewRarFormat)); + break; +#endif + case UIERROR_NOFILESTOEXTRACT: + mprintf(St(MExtrNoFiles)); + break; + case UIERROR_MISSINGVOL: + Log(Str[0],St(MAbsNextVol),Str[0]); + mprintf(L" "); // For progress percent. + break; +#ifndef SFX_MODULE + case UIERROR_NEEDPREVVOL: + Log(Str[0],St(MUnpCannotMerge),Str[1]); + break; + case UIERROR_UNKNOWNEXTRA: + Log(Str[0],St(MUnknownExtra),Str[1]); + break; + case UIERROR_CORRUPTEXTRA: + Log(Str[0],St(MCorruptExtra),Str[1],Str[2]); + break; +#endif +#if !defined(SFX_MODULE) && defined(_WIN_ALL) + case UIERROR_NTFSREQUIRED: + Log(NULL,St(MNTFSRequired),Str[0]); + break; +#endif +#if !defined(SFX_MODULE) && defined(_WIN_ALL) + case UIERROR_ACLBROKEN: + Log(Str[0],St(MACLBroken),Str[1]); + break; + case UIERROR_ACLUNKNOWN: + Log(Str[0],St(MACLUnknown),Str[1]); + break; + case UIERROR_ACLSET: + Log(Str[0],St(MACLSetError),Str[1]); + break; + case UIERROR_STREAMBROKEN: + Log(Str[0],St(MStreamBroken),Str[1]); + break; + case UIERROR_STREAMUNKNOWN: + Log(Str[0],St(MStreamUnknown),Str[1]); + break; +#endif + case UIERROR_INCOMPATSWITCH: + mprintf(St(MIncompatSwitch),Str[0],Num[0]); + break; + case UIERROR_PATHTOOLONG: + Log(NULL,L"\n%ls%ls%ls",Str[0],Str[1],Str[2]); + Log(NULL,St(MPathTooLong)); + break; +#ifndef SFX_MODULE + case UIERROR_DIRSCAN: + Log(NULL,St(MScanError),Str[0]); + break; +#endif + case UIERROR_UOWNERBROKEN: + Log(Str[0],St(MOwnersBroken),Str[1]); + break; + case UIERROR_UOWNERGETOWNERID: + Log(Str[0],St(MErrGetOwnerID),Str[1]); + break; + case UIERROR_UOWNERGETGROUPID: + Log(Str[0],St(MErrGetGroupID),Str[1]); + break; + case UIERROR_UOWNERSET: + Log(Str[0],St(MSetOwnersError),Str[1]); + break; + case UIERROR_ULINKREAD: + Log(NULL,St(MErrLnkRead),Str[0]); + break; + case UIERROR_ULINKEXIST: + Log(NULL,St(MSymLinkExists),Str[0]); + break; + case UIERROR_READERRTRUNCATED: + Log(NULL,St(MErrReadTrunc),Str[0]); + break; + case UIERROR_READERRCOUNT: + Log(NULL,St(MErrReadCount),Num[0]); + break; + case UIERROR_DIRNAMEEXISTS: + Log(NULL,St(MDirNameExists)); + break; + case UIERROR_TRUNCPSW: + eprintf(St(MTruncPsw),Num[0]); + eprintf(L"\n"); + break; + case UIERROR_ADJUSTVALUE: + Log(NULL,St(MAdjustValue),Str[0],Str[1]); + break; + +#ifndef SFX_MODULE + case UIMSG_STRING: + mprintf(L"\n%s",Str[0]); + break; +#endif + case UIMSG_CORRECTINGNAME: + Log(Str[0],St(MCorrectingName)); + break; + case UIMSG_BADARCHIVE: + mprintf(St(MBadArc),Str[0]); + break; + case UIMSG_CREATING: + mprintf(St(MCreating),Str[0]); + break; + case UIMSG_RENAMING: + mprintf(St(MRenaming),Str[0],Str[1]); + break; + case UIMSG_RECVOLCALCCHECKSUM: + mprintf(St(MCalcCRCAllVol)); + break; + case UIMSG_RECVOLFOUND: + mprintf(St(MRecVolFound),Num[0]); + break; + case UIMSG_RECVOLMISSING: + mprintf(St(MRecVolMissing),Num[0]); + break; + case UIMSG_MISSINGVOL: + mprintf(St(MAbsNextVol),Str[0]); + break; + case UIMSG_RECONSTRUCTING: + mprintf(St(MReconstructing)); + break; + case UIMSG_CHECKSUM: + mprintf(St(MCRCFailed),Str[0]); + break; + case UIMSG_FAT32SIZE: + mprintf(St(MFAT32Size)); + mprintf(L" "); // For progress percent. + break; + case UIMSG_SKIPENCARC: + Log(NULL,St(MSkipEncArc),Str[0]); + break; + + + + case UIEVENT_RRTESTINGSTART: + mprintf(L"%s ",St(MTestingRR)); + break; + } +} + + +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName, + SecPassword *Password,CheckPassword *CheckPwd) +{ + // Unlike GUI we cannot provide Cancel button here, so we use the empty + // password to abort. Otherwise user not knowing a password would need to + // press Ctrl+C multiple times to quit from infinite password request loop. + return GetConsolePassword(Type,FileName,Password) && Password->IsSet(); +} + + +bool uiIsGlobalPasswordSet() +{ + return false; +} + + +void uiAlarm(UIALARM_TYPE Type) +{ + if (uiSoundNotify==SOUND_NOTIFY_ON) + { + static clock_t LastTime=-10; // Negative to always beep first time. + if ((MonoClock()-LastTime)/CLOCKS_PER_SEC>5) + { +#ifdef _WIN_ALL + MessageBeep(-1); +#else + putwchar('\007'); +#endif + LastTime=MonoClock(); + } + } +} + + + + +bool uiAskNextVolume(wchar *VolName,size_t MaxSize) +{ + eprintf(St(MAskNextVol),VolName); + return Ask(St(MContinueQuit))!=2; +} + + +void uiAskRepeatRead(const wchar *FileName,bool &Ignore,bool &All,bool &Retry,bool &Quit) +{ + eprintf(St(MErrReadInfo)); + int Code=Ask(St(MIgnoreAllRetryQuit)); + + Ignore=(Code==1); + All=(Code==2); + Quit=(Code==4); + Retry=!Ignore && !All && !Quit; // Default also for invalid input, not just for 'Retry'. +} + + +bool uiAskRepeatWrite(const wchar *FileName,bool DiskFull) +{ + mprintf(L"\n"); + Log(NULL,St(DiskFull ? MNotEnoughDisk:MErrWrite),FileName); + return Ask(St(MRetryAbort))==1; +} + + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month) +{ + static MSGID MonthID[12]={ + MMonthJan,MMonthFeb,MMonthMar,MMonthApr,MMonthMay,MMonthJun, + MMonthJul,MMonthAug,MMonthSep,MMonthOct,MMonthNov,MMonthDec + }; + return St(MonthID[Month]); +} +#endif + + +void uiEolAfterMsg() +{ + if (AnyMessageDisplayed) + { + // Avoid deleting several last characters of any previous error message + // with percentage indicator in -idn mode. + AnyMessageDisplayed=false; + mprintf(L"\n"); + } +} diff --git a/unrar/unrar/uisilent.cpp b/unrar/unrar/uisilent.cpp new file mode 100644 index 0000000..8155885 --- /dev/null +++ b/unrar/unrar/uisilent.cpp @@ -0,0 +1,75 @@ +// Purely user interface function. Gets and returns user input. +UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags) +{ + return UIASKREP_R_REPLACE; +} + + + + +void uiStartArchiveExtract(bool Extract,const wchar *ArcName) +{ +} + + +bool uiStartFileExtract(const wchar *FileName,bool Extract,bool Test,bool Skip) +{ + return true; +} + + +void uiExtractProgress(int64 CurFileSize,int64 TotalFileSize,int64 CurSize,int64 TotalSize) +{ +} + + +void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize) +{ +} + + +void uiMsgStore::Msg() +{ +} + + +bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName, + SecPassword *Password,CheckPassword *CheckPwd) +{ + return false; +} + + +bool uiIsGlobalPasswordSet() +{ + return false; +} + + +void uiAlarm(UIALARM_TYPE Type) +{ +} + + +bool uiIsAborted() +{ + return false; +} + + +void uiGiveTick() +{ +} + + +#ifndef SFX_MODULE +const wchar *uiGetMonthName(int Month) +{ + return L""; +} +#endif + + +void uiEolAfterMsg() +{ +} diff --git a/unrar/unrar/ulinks.cpp b/unrar/unrar/ulinks.cpp index b8d6aa5..141a97f 100644 --- a/unrar/unrar/ulinks.cpp +++ b/unrar/unrar/ulinks.cpp @@ -1,32 +1,136 @@ -#include "rar.hpp" - -int ExtractLink(ComprDataIO &DataIO,Archive &Arc,char *DestName,uint &LinkCRC,bool Create) +static bool UnixSymlink(CommandData *Cmd,const char *Target,const wchar *LinkName,RarTime *ftm,RarTime *fta) { -#if defined(SAVE_LINKS) && defined(_UNIX) - char FileName[NM]; - if (IsLink(Arc.NewLhd.FileAttr)) + CreatePath(LinkName,true,Cmd->DisableNames); + + // Overwrite prompt was already issued and confirmed earlier, so we can + // remove existing symlink or regular file here. PrepareToDelete was also + // called earlier inside of uiAskReplaceEx. + DelFile(LinkName); + + char LinkNameA[NM]; + WideToChar(LinkName,LinkNameA,ASIZE(LinkNameA)); + if (symlink(Target,LinkNameA)==-1) // Error. { - int DataSize=Min(Arc.NewLhd.PackSize,sizeof(FileName)-1); - DataIO.UnpRead((byte *)FileName,DataSize); - FileName[DataSize]=0; - if (Create) + if (errno==EEXIST) + uiMsg(UIERROR_ULINKEXIST,LinkName); + else { - CreatePath(DestName,NULL,true); - if (symlink(FileName,DestName)==-1) - if (errno==EEXIST) - Log(Arc.FileName,St(MSymLinkExists),DestName); - else - { - Log(Arc.FileName,St(MErrCreateLnk),DestName); - ErrHandler.SetErrorCode(WARNING); - } + uiMsg(UIERROR_SLINKCREATE,UINULL,LinkName); + ErrHandler.SetErrorCode(RARX_WARNING); } - int NameSize=Min(DataSize,strlen(FileName)); - LinkCRC=CRC(0xffffffff,FileName,NameSize); - return(1); + return false; } +#ifdef USE_LUTIMES +#ifdef UNIX_TIME_NS + timespec times[2]; + times[0].tv_sec=fta->GetUnix(); + times[0].tv_nsec=fta->IsSet() ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; + times[1].tv_sec=ftm->GetUnix(); + times[1].tv_nsec=ftm->IsSet() ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; + utimensat(AT_FDCWD,LinkNameA,times,AT_SYMLINK_NOFOLLOW); +#else + struct timeval tv[2]; + tv[0].tv_sec=fta->GetUnix(); + tv[0].tv_usec=long(fta->GetUnixNS()%1000000000/1000); + tv[1].tv_sec=ftm->GetUnix(); + tv[1].tv_usec=long(ftm->GetUnixNS()%1000000000/1000); + lutimes(LinkNameA,tv); +#endif #endif - return(0); + + return true; +} + + +static bool IsFullPath(const char *PathA) // Unix ASCII version. +{ + return *PathA==CPATHDIVIDER; +} + + +// For security purpose we prefer to be sure that CharToWide completed +// successfully and even if it truncated a string for some reason, +// it didn't affect the number of path related characters we analyze +// in IsRelativeSymlinkSafe later. +// This check is likely to be excessive, but let's keep it anyway. +static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize) +{ + if (!CharToWide(Src,Dest,DestSize) || *Dest==0) + return false; + uint SrcChars=0,DestChars=0; + for (uint I=0;Src[I]!=0;I++) + if (Src[I]=='/' || Src[I]=='.') + SrcChars++; + for (uint I=0;Dest[I]!=0;I++) + if (Dest[I]=='/' || Dest[I]=='.') + DestChars++; + return SrcChars==DestChars; +} + + +static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc, + const wchar *LinkName,bool &UpLink) +{ + char Target[NM]; + if (IsLink(Arc.FileHead.FileAttr)) + { + size_t DataSize=(size_t)Arc.FileHead.PackSize; + if (DataSize>ASIZE(Target)-1) + return false; + if ((size_t)DataIO.UnpRead((byte *)Target,DataSize)!=DataSize) + return false; + Target[DataSize]=0; + + DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,1); + DataIO.UnpHash.Update(Target,strlen(Target)); + DataIO.UnpHash.Result(&Arc.FileHead.FileHash); + + // Return true in case of bad checksum, so link will be processed further + // and extraction routine will report the checksum error. + if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL)) + return true; + + wchar TargetW[NM]; + if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW))) + return false; + // Use Arc.FileHead.FileName instead of LinkName, since LinkName + // can include the destination path as a prefix, which can + // confuse IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || + !IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW))) + return false; + UpLink=strstr(Target,"..")!=NULL; + return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime); + } + return false; +} + + +static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd) +{ + char Target[NM]; + WideToChar(hd->RedirName,Target,ASIZE(Target)); + if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION) + { + // Cannot create Windows absolute path symlinks in Unix. Only relative path + // Windows symlinks can be created here. RAR 5.0 used \??\ prefix + // for Windows absolute symlinks, since RAR 5.1 /??/ is used. + // We escape ? as \? to avoid "trigraph" warning + if (strncmp(Target,"\\??\\",4)==0 || strncmp(Target,"/\?\?/",4)==0) + return false; + DosSlashToUnix(Target,Target,ASIZE(Target)); + } + + wchar TargetW[NM]; + if (!SafeCharToWide(Target,TargetW,ASIZE(TargetW))) + return false; + // Use hd->FileName instead of LinkName, since LinkName can include + // the destination path as a prefix, which can confuse + // IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) || + !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,TargetW))) + return false; + return UnixSymlink(Cmd,Target,Name,&hd->mtime,&hd->atime); } diff --git a/unrar/unrar/ulinks.hpp b/unrar/unrar/ulinks.hpp deleted file mode 100644 index 69b0e9f..0000000 --- a/unrar/unrar/ulinks.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _RAR_ULINKS_ -#define _RAR_ULINKS_ - -void SaveLinkData(ComprDataIO &DataIO,Archive &TempArc,FileHeader &hd, - char *Name); -int ExtractLink(ComprDataIO &DataIO,Archive &Arc,char *DestName, - uint &LinkCRC,bool Create); - -#endif diff --git a/unrar/unrar/unicode.cpp b/unrar/unrar/unicode.cpp index affc905..73b09bb 100644 --- a/unrar/unrar/unicode.cpp +++ b/unrar/unrar/unicode.cpp @@ -1,183 +1,345 @@ #include "rar.hpp" +#define MBFUNCTIONS + +#if defined(_UNIX) && defined(MBFUNCTIONS) + +static bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success); +static void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success); + +// In Unix we map high ASCII characters which cannot be converted to Unicode +// to 0xE000 - 0xE0FF private use Unicode area. +static const uint MapAreaStart=0xE000; + +// Mapped string marker. Initially we used 0xFFFF for this purpose, +// but it causes MSVC2008 swprintf to fail (it treats 0xFFFF as error marker). +// While we could workaround it, it is safer to use another character. +static const uint MappedStringMark=0xFFFE; -#if defined(_EMX) && !defined(_DJGPP) -#include "unios2.cpp" #endif -bool WideToChar(const wchar *Src,char *Dest,int DestSize) +bool WideToChar(const wchar *Src,char *Dest,size_t DestSize) { bool RetCode=true; -#ifdef _WIN_32 - if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,DestSize,NULL,NULL)==0) - RetCode=false; -#else -#ifdef _APPLE - WideToUtf(Src,Dest,DestSize); -#else -#ifdef MBFUNCTIONS + *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. - size_t ResultingSize=wcstombs(Dest,Src,DestSize); - if (ResultingSize==(size_t)-1) - RetCode=false; - if (ResultingSize==0 && *Src!=0) +#ifdef _WIN_ALL + if (WideCharToMultiByte(CP_ACP,0,Src,-1,Dest,(int)DestSize,NULL,NULL)==0) RetCode=false; - if ((!RetCode || *Dest==0 && *Src!=0) && DestSize>NM && strlenw(Src)<NM) +// wcstombs is broken in Android NDK r9. +#elif defined(_APPLE) + WideToUtf(Src,Dest,DestSize); + +#elif defined(MBFUNCTIONS) + if (!WideToCharMap(Src,Dest,DestSize,RetCode)) { - /* Workaround for strange Linux Unicode functions bug. - Some of wcstombs and mbstowcs implementations in some situations - (we are yet to find out what it depends on) can return an empty - string and success code if buffer size value is too large. - */ - return(WideToChar(Src,Dest,NM)); - } + mbstate_t ps; // Use thread safe external state based functions. + memset (&ps, 0, sizeof(ps)); + const wchar *SrcParam=Src; // wcsrtombs can change the pointer. + // Some implementations of wcsrtombs can cause memory analyzing tools + // like valgrind to report uninitialized data access. It happens because + // internally these implementations call SSE4 based wcslen function, + // which reads 16 bytes at once including those beyond of trailing 0. + size_t ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps); + + if (ResultingSize==(size_t)-1 && errno==EILSEQ) + { + // Aborted on inconvertible character not zero terminating the result. + // EILSEQ helps to distinguish it from small output buffer abort. + // We want to convert as much as we can, so we clean the output buffer + // and repeat conversion. + memset (&ps, 0, sizeof(ps)); + SrcParam=Src; // wcsrtombs can change the pointer. + memset(Dest,0,DestSize); + ResultingSize=wcsrtombs(Dest,&SrcParam,DestSize,&ps); + } + + if (ResultingSize==(size_t)-1) + RetCode=false; + if (ResultingSize==0 && *Src!=0) + RetCode=false; + } #else - if (UnicodeEnabled()) + for (int I=0;I<DestSize;I++) { -#if defined(_EMX) && !defined(_DJGPP) - int len=Min(strlenw(Src)+1,DestSize-1); - if (uni_fromucs((UniChar*)Src,len,Dest,(size_t*)&DestSize)==-1 || - DestSize>len*2) - RetCode=false; - Dest[DestSize]=0; -#endif + Dest[I]=(char)Src[I]; + if (Src[I]==0) + break; } - else - for (int I=0;I<DestSize;I++) - { - Dest[I]=(char)Src[I]; - if (Src[I]==0) - break; - } -#endif #endif -#endif - return(RetCode); + if (DestSize>0) + Dest[DestSize-1]=0; + + // We tried to return the empty string if conversion is failed, + // but it does not work well. WideCharToMultiByte returns 'failed' code + // and partially converted string even if we wanted to convert only a part + // of string and passed DestSize smaller than required for fully converted + // string. Such call is the valid behavior in RAR code and we do not expect + // the empty string in this case. + + return RetCode; } -bool CharToWide(const char *Src,wchar *Dest,int DestSize) +bool CharToWide(const char *Src,wchar *Dest,size_t DestSize) { bool RetCode=true; -#ifdef _WIN_32 - if (MultiByteToWideChar(CP_ACP,0,Src,-1,Dest,DestSize)==0) + *Dest=0; // Set 'Dest' to zero just in case the conversion will fail. + +#ifdef _WIN_ALL + if (MultiByteToWideChar(CP_ACP,0,Src,-1,Dest,(int)DestSize)==0) RetCode=false; -#else -#ifdef _APPLE + +// mbstowcs is broken in Android NDK r9. +#elif defined(_APPLE) UtfToWide(Src,Dest,DestSize); -#else -#ifdef MBFUNCTIONS - size_t ResultingSize=mbstowcs(Dest,Src,DestSize); +#elif defined(MBFUNCTIONS) + mbstate_t ps; + memset (&ps, 0, sizeof(ps)); + const char *SrcParam=Src; // mbsrtowcs can change the pointer. + size_t ResultingSize=mbsrtowcs(Dest,&SrcParam,DestSize,&ps); if (ResultingSize==(size_t)-1) RetCode=false; if (ResultingSize==0 && *Src!=0) RetCode=false; - if ((!RetCode || *Dest==0 && *Src!=0) && DestSize>NM && strlen(Src)<NM) - { - /* Workaround for strange Linux Unicode functions bug. - Some of wcstombs and mbstowcs implementations in some situations - (we are yet to find out what it depends on) can return an empty - string and success code if buffer size value is too large. - */ - return(CharToWide(Src,Dest,NM)); - } + if (RetCode==false && DestSize>1) + CharToWideMap(Src,Dest,DestSize,RetCode); #else - if (UnicodeEnabled()) + for (int I=0;I<DestSize;I++) { -#if defined(_EMX) && !defined(_DJGPP) - int len=Min(strlen(Src)+1,DestSize-1); - if (uni_toucs((char*)Src,len,(UniChar*)Dest,(size_t*)&DestSize)==-1 || - DestSize>len) - DestSize=0; - RetCode=false; + Dest[I]=(wchar_t)Src[I]; + if (Src[I]==0) + break; + } #endif + if (DestSize>0) + Dest[DestSize-1]=0; + + // We tried to return the empty string if conversion is failed, + // but it does not work well. MultiByteToWideChar returns 'failed' code + // even if we wanted to convert only a part of string and passed DestSize + // smaller than required for fully converted string. Such call is the valid + // behavior in RAR code and we do not expect the empty string in this case. + + return RetCode; +} + + +#if defined(_UNIX) && defined(MBFUNCTIONS) +// Convert and restore mapped inconvertible Unicode characters. +// We use it for extended ASCII names in Unix. +bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success) +{ + // String with inconvertible characters mapped to private use Unicode area + // must have the mark code somewhere. + if (wcschr(Src,(wchar)MappedStringMark)==NULL) + return false; + + // Seems to be that wcrtomb in some memory analyzing libraries + // can produce uninitilized output while reporting success on garbage input. + // So we clean the destination to calm analyzers. + memset(Dest,0,DestSize); + + Success=true; + uint SrcPos=0,DestPos=0; + while (Src[SrcPos]!=0 && DestPos<DestSize-MB_CUR_MAX) + { + if (uint(Src[SrcPos])==MappedStringMark) + { + SrcPos++; + continue; + } + // For security reasons do not restore low ASCII codes, so mapping cannot + // be used to hide control codes like path separators. + if (uint(Src[SrcPos])>=MapAreaStart+0x80 && uint(Src[SrcPos])<MapAreaStart+0x100) + Dest[DestPos++]=char(uint(Src[SrcPos++])-MapAreaStart); + else + { + mbstate_t ps; + memset(&ps,0,sizeof(ps)); + if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==(size_t)-1) + { + Dest[DestPos]='_'; + Success=false; + } + SrcPos++; + memset(&ps,0,sizeof(ps)); + int Length=mbrlen(Dest+DestPos,MB_CUR_MAX,&ps); + DestPos+=Max(Length,1); + } } - else - for (int I=0;I<DestSize;I++) + Dest[Min(DestPos,DestSize-1)]=0; + return true; +} +#endif + + +#if defined(_UNIX) && defined(MBFUNCTIONS) +// Convert and map inconvertible Unicode characters. +// We use it for extended ASCII names in Unix. +void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success) +{ + // Map inconvertible characters to private use Unicode area 0xE000. + // Mark such string by placing special non-character code before + // first inconvertible character. + Success=false; + bool MarkAdded=false; + uint SrcPos=0,DestPos=0; + while (DestPos<DestSize) + { + if (Src[SrcPos]==0) { - Dest[I]=(wchar_t)Src[I]; - if (Src[I]==0) + Success=true; + break; + } + mbstate_t ps; + memset(&ps,0,sizeof(ps)); + size_t res=mbrtowc(Dest+DestPos,Src+SrcPos,MB_CUR_MAX,&ps); + if (res==(size_t)-1 || res==(size_t)-2) + { + // For security reasons we do not want to map low ASCII characters, + // so we do not have additional .. and path separator codes. + if (byte(Src[SrcPos])>=0x80) + { + if (!MarkAdded) + { + Dest[DestPos++]=MappedStringMark; + MarkAdded=true; + if (DestPos>=DestSize) + break; + } + Dest[DestPos++]=byte(Src[SrcPos++])+MapAreaStart; + } + else break; } -#endif -#endif -#endif - return(RetCode); + else + { + memset(&ps,0,sizeof(ps)); + int Length=mbrlen(Src+SrcPos,MB_CUR_MAX,&ps); + SrcPos+=Max(Length,1); + DestPos++; + } + } + Dest[Min(DestPos,DestSize-1)]=0; } +#endif -byte* WideToRaw(const wchar *Src,byte *Dest,int DestSize) +// SrcSize is source data size in wide characters, not in bytes. +// DestSize is the maximum allowed destination size. +byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize) { - for (int I=0;I<DestSize;I++,Src++) + for (size_t I=0;I<SrcSize && I*2+1<DestSize;I++,Src++) { Dest[I*2]=(byte)*Src; Dest[I*2+1]=(byte)(*Src>>8); if (*Src==0) break; } - return(Dest); + return Dest; } -wchar* RawToWide(const byte *Src,wchar *Dest,int DestSize) +wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize) { - for (int I=0;I<DestSize;I++) + for (size_t I=0;I<DestSize;I++) if ((Dest[I]=Src[I*2]+(Src[I*2+1]<<8))==0) break; - return(Dest); + return Dest; } -void WideToUtf(const wchar *Src,char *Dest,int DestSize) +void WideToUtf(const wchar *Src,char *Dest,size_t DestSize) { - DestSize--; - while (*Src!=0 && --DestSize>=0) + long dsize=(long)DestSize; + dsize--; + while (*Src!=0 && --dsize>=0) { uint c=*(Src++); if (c<0x80) *(Dest++)=c; else - if (c<0x800 && --DestSize>=0) + if (c<0x800 && --dsize>=0) { *(Dest++)=(0xc0|(c>>6)); *(Dest++)=(0x80|(c&0x3f)); } else - if (c<0x10000 && (DestSize-=2)>=0) + { + if (c>=0xd800 && c<=0xdbff && *Src>=0xdc00 && *Src<=0xdfff) // Surrogate pair. + { + c=((c-0xd800)<<10)+(*Src-0xdc00)+0x10000; + Src++; + } + if (c<0x10000 && (dsize-=2)>=0) { *(Dest++)=(0xe0|(c>>12)); *(Dest++)=(0x80|((c>>6)&0x3f)); *(Dest++)=(0x80|(c&0x3f)); } else - if (c < 0x200000 && (DestSize-=3)>=0) + if (c < 0x200000 && (dsize-=3)>=0) { *(Dest++)=(0xf0|(c>>18)); *(Dest++)=(0x80|((c>>12)&0x3f)); *(Dest++)=(0x80|((c>>6)&0x3f)); *(Dest++)=(0x80|(c&0x3f)); } + } } *Dest=0; } -void UtfToWide(const char *Src,wchar *Dest,int DestSize) +size_t WideToUtfSize(const wchar *Src) +{ + size_t Size=0; + for (;*Src!=0;Src++) + if (*Src<0x80) + Size++; + else + if (*Src<0x800) + Size+=2; + else + if ((uint)*Src<0x10000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. + { + if (Src[0]>=0xd800 && Src[0]<=0xdbff && Src[1]>=0xdc00 && Src[1]<=0xdfff) + { + Size+=4; // 4 output bytes for Unicode surrogate pair. + Src++; + } + else + Size+=3; + } + else + if ((uint)*Src<0x200000) //(uint) to avoid Clang/win "always true" warning for 16-bit wchar_t. + Size+=4; + return Size+1; // Include terminating zero. +} + + +bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize) { - DestSize--; + bool Success=true; + long dsize=(long)DestSize; + dsize--; while (*Src!=0) { - uint c=(byte)*(Src++),d; + uint c=byte(*(Src++)),d; if (c<0x80) d=c; else if ((c>>5)==6) { if ((*Src&0xc0)!=0x80) + { + Success=false; break; + } d=((c&0x1f)<<6)|(*Src&0x3f); Src++; } @@ -185,7 +347,10 @@ void UtfToWide(const char *Src,wchar *Dest,int DestSize) if ((c>>4)==14) { if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80) + { + Success=false; break; + } d=((c&0xf)<<12)|((Src[0]&0x3f)<<6)|(Src[1]&0x3f); Src+=2; } @@ -193,244 +358,221 @@ void UtfToWide(const char *Src,wchar *Dest,int DestSize) if ((c>>3)==30) { if ((Src[0]&0xc0)!=0x80 || (Src[1]&0xc0)!=0x80 || (Src[2]&0xc0)!=0x80) + { + Success=false; break; + } d=((c&7)<<18)|((Src[0]&0x3f)<<12)|((Src[1]&0x3f)<<6)|(Src[2]&0x3f); Src+=3; } else + { + Success=false; break; - if (--DestSize<0) + } + if (--dsize<0) break; if (d>0xffff) { - if (--DestSize<0 || d>0x10ffff) + if (--dsize<0) break; - *(Dest++)=((d-0x10000)>>10)+0xd800; - *(Dest++)=(d&0x3ff)+0xdc00; + if (d>0x10ffff) // UTF-8 must end at 0x10ffff according to RFC 3629. + { + Success=false; + continue; + } + if (sizeof(*Dest)==2) // Use the surrogate pair. + { + *(Dest++)=((d-0x10000)>>10)+0xd800; + *(Dest++)=(d&0x3ff)+0xdc00; + } + else + *(Dest++)=d; } else *(Dest++)=d; } *Dest=0; + return Success; } -bool UnicodeEnabled() -{ -#ifdef UNICODE_SUPPORTED - #ifdef _EMX - return(uni_ready); - #else - return(true); - #endif -#else - return(false); -#endif -} - - -int strlenw(const wchar *str) +// For zero terminated strings. +bool IsTextUtf8(const byte *Src) { - int length=0; - while (*(str++)!=0) - length++; - return(length); + return IsTextUtf8(Src,strlen((const char *)Src)); } -wchar* strcpyw(wchar *dest,const wchar *src) +// Source data can be both with and without UTF-8 BOM. +bool IsTextUtf8(const byte *Src,size_t SrcSize) { - do { - *(dest++)=*src; - } while (*(src++)!=0); - return(dest); -} - - -wchar* strncpyw(wchar *dest,const wchar *src,size_t n) -{ - do { - *(dest++)=*src; - } while (*(src++)!=0 && (int)(--n) > 0); - return(dest); -} - - -wchar* strcatw(wchar *dest,const wchar *src) -{ - return(strcpyw(dest+strlenw(dest),src)); + while (SrcSize-- > 0) + { + byte C=*(Src++); + int HighOne=0; // Number of leftmost '1' bits. + for (byte Mask=0x80;Mask!=0 && (C & Mask)!=0;Mask>>=1) + HighOne++; + if (HighOne==1 || HighOne>6) + return false; + while (--HighOne > 0) + if (SrcSize-- <= 0 || (*(Src++) & 0xc0)!=0x80) + return false; + } + return true; } -#ifndef SFX_MODULE -wchar* strncatw(wchar *dest,const wchar *src,size_t n) +int wcsicomp(const wchar *s1,const wchar *s2) { - dest+=strlenw(dest); +#ifdef _WIN_ALL + return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,-1,s2,-1)-2; +#else while (true) - if ((int)(--n)<0) - { - *dest=0; - break; - } - else - if ((*(dest++)=*(src++))==0) - break; - return(dest); -} -#endif - - -int strcmpw(const wchar *s1,const wchar *s2) -{ - while (*s1==*s2) { + wchar u1 = towupper(*s1); + wchar u2 = towupper(*s2); + if (u1 != u2) + return u1 < u2 ? -1 : 1; if (*s1==0) - return(0); + break; s1++; s2++; } - return(*s1<*s2 ? -1:1); + return 0; +#endif } -int strncmpw(const wchar *s1,const wchar *s2,size_t n) +int wcsnicomp(const wchar *s1,const wchar *s2,size_t n) { - while ((int)(n--)>0) +#ifdef _WIN_ALL + // If we specify 'n' exceeding the actual string length, CompareString goes + // beyond the trailing zero and compares garbage. So we need to limit 'n' + // to real string length. + size_t l1=Min(wcslen(s1)+1,n); + size_t l2=Min(wcslen(s2)+1,n); + return CompareStringW(LOCALE_USER_DEFAULT,NORM_IGNORECASE|SORT_STRINGSORT,s1,(int)l1,s2,(int)l2)-2; +#else + if (n==0) + return 0; + while (true) { - if (*s1<*s2) - return(-1); - if (*s1>*s2) - return(-1); - if (*s1==0) + wchar u1 = towupper(*s1); + wchar u2 = towupper(*s2); + if (u1 != u2) + return u1 < u2 ? -1 : 1; + if (*s1==0 || --n==0) break; s1++; s2++; } - return(0); + return 0; +#endif } -#ifndef SFX_MODULE -int stricmpw(const wchar *s1,const wchar *s2) +// Case insensitive wcsstr(). +const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search) { - char Ansi1[NM*sizeof(wchar)],Ansi2[NM*sizeof(wchar)]; - WideToChar(s1,Ansi1,sizeof(Ansi1)); - WideToChar(s2,Ansi2,sizeof(Ansi2)); - return(stricomp(Ansi1,Ansi2)); + for (size_t i=0;str[i]!=0;i++) + for (size_t j=0;;j++) + { + if (search[j]==0) + return str+i; + if (tolowerw(str[i+j])!=tolowerw(search[j])) + break; + } + return NULL; } -#endif -#if !defined(SFX_MODULE) && !defined(_WIN_CE) -inline int strnicmpw_w2c(const wchar *s1,const wchar *s2,size_t n) +#ifndef SFX_MODULE +wchar* wcslower(wchar *s) { - wchar Wide1[NM*2],Wide2[NM*2]; - strncpyw(Wide1,s1,sizeof(Wide1)/sizeof(Wide1[0])-1); - strncpyw(Wide2,s2,sizeof(Wide2)/sizeof(Wide2[0])-1); - Wide1[Min(sizeof(Wide1)/sizeof(Wide1[0])-1,n)]=0; - Wide2[Min(sizeof(Wide2)/sizeof(Wide2[0])-1,n)]=0; - char Ansi1[NM*2],Ansi2[NM*2]; - WideToChar(Wide1,Ansi1,sizeof(Ansi1)); - WideToChar(Wide2,Ansi2,sizeof(Ansi2)); - return(stricomp(Ansi1,Ansi2)); +#ifdef _WIN_ALL + // _wcslwr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharLower involves less overhead. + CharLower(s); +#else + for (wchar *c=s;*c!=0;c++) + *c=towlower(*c); +#endif + return s; } #endif #ifndef SFX_MODULE -int strnicmpw(const wchar *s1,const wchar *s2,size_t n) +wchar* wcsupper(wchar *s) { - return(strnicmpw_w2c(s1,s2,n)); -} +#ifdef _WIN_ALL + // _wcsupr requires setlocale and we do not want to depend on setlocale + // in Windows. Also CharUpper involves less overhead. + CharUpper(s); +#else + for (wchar *c=s;*c!=0;c++) + *c=towupper(*c); #endif - - -wchar* strchrw(const wchar *s,int c) -{ - while (*s) - { - if (*s==c) - return((wchar *)s); - s++; - } - return(NULL); -} - - -wchar* strrchrw(const wchar *s,int c) -{ - for (int I=strlenw(s)-1;I>=0;I--) - if (s[I]==c) - return((wchar *)(s+I)); - return(NULL); + return s; } +#endif -wchar* strpbrkw(const wchar *s1,const wchar *s2) -{ - while (*s1) - { - if (strchrw(s2,*s1)!=NULL) - return((wchar *)s1); - s1++; - } - return(NULL); -} -#ifndef SFX_MODULE -wchar* strlowerw(wchar *Str) +int toupperw(int ch) { - for (wchar *ChPtr=Str;*ChPtr;ChPtr++) - if (*ChPtr<128) - *ChPtr=loctolower(*ChPtr); - return(Str); -} +#if defined(_WIN_ALL) + // CharUpper is more reliable than towupper in Windows, which seems to be + // C locale dependent even in Unicode version. For example, towupper failed + // to convert lowercase Russian characters. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharUpper((wchar *)(INT_PTR)(ch&0xffff)); +#else + return towupper(ch); #endif - - -#ifndef SFX_MODULE -wchar* strupperw(wchar *Str) -{ - for (wchar *ChPtr=Str;*ChPtr;ChPtr++) - if (*ChPtr<128) - *ChPtr=loctoupper(*ChPtr); - return(Str); } -#endif -#ifndef SFX_MODULE -wchar* strdupw(const wchar *Str) +int tolowerw(int ch) { - if (Str==NULL) - return(NULL); - wchar *n=(wchar *)malloc((strlenw(Str)+1)*sizeof(wchar)); - if (n==NULL) - return(NULL); - strcpyw(n,Str); - return(n); -} +#if defined(_WIN_ALL) + // CharLower is more reliable than towlower in Windows. + // See comment for towupper above. Use 0xffff mask to prevent crash + // if value larger than 0xffff is passed to this function. + return (int)(INT_PTR)CharLower((wchar *)(INT_PTR)(ch&0xffff)); +#else + return towlower(ch); #endif +} -#ifndef SFX_MODULE -int toupperw(int ch) +int atoiw(const wchar *s) { - return((ch<128) ? loctoupper(ch):ch); + return (int)atoilw(s); } -#endif -int atoiw(const wchar *s) +int64 atoilw(const wchar *s) { - int n=0; + bool sign=false; + if (*s=='-') // We do use signed integers here, for example, in GUI SFX. + { + s++; + sign=true; + } + // Use unsigned type here, since long string can overflow the variable + // and signed integer overflow is undefined behavior in C++. + uint64 n=0; while (*s>='0' && *s<='9') { n=n*10+(*s-'0'); s++; } - return(n); + // Check int64(n)>=0 to avoid the signed overflow with undefined behavior + // when negating 0x8000000000000000. + return sign && int64(n)>=0 ? -int64(n) : int64(n); } @@ -448,66 +590,18 @@ void SupportDBCS::Init() CPINFO CPInfo; GetCPInfo(CP_ACP,&CPInfo); DBCSMode=CPInfo.MaxCharSize > 1; - for (int I=0;I<sizeof(IsLeadByte)/sizeof(IsLeadByte[0]);I++) - IsLeadByte[I]=IsDBCSLeadByte(I); + for (uint I=0;I<ASIZE(IsLeadByte);I++) + IsLeadByte[I]=IsDBCSLeadByte(I)!=0; } char* SupportDBCS::charnext(const char *s) { - return (char *)(IsLeadByte[*s] ? s+2:s+1); -} - - -uint SupportDBCS::strlend(const char *s) -{ - uint Length=0; - while (*s!=0) - { - if (IsLeadByte[*s]) - s+=2; - else - s++; - Length++; - } - return(Length); -} - - -char* SupportDBCS::strchrd(const char *s, int c) -{ - while (*s!=0) - if (IsLeadByte[*s]) - s+=2; - else - if (*s==c) - return((char *)s); - else - s++; - return(NULL); -} - - -void SupportDBCS::copychrd(char *dest,const char *src) -{ - dest[0]=src[0]; - if (IsLeadByte[src[0]]) - dest[1]=src[1]; + // Zero cannot be the trail byte. So if next byte after the lead byte + // is 0, the string is corrupt and we'll better return the pointer to 0, + // to break string processing loops. + return (char *)(IsLeadByte[(byte)*s] && s[1]!=0 ? s+2:s+1); } +#endif -char* SupportDBCS::strrchrd(const char *s, int c) -{ - const char *found=NULL; - while (*s!=0) - if (IsLeadByte[*s]) - s+=2; - else - { - if (*s==c) - found=s; - s++; - } - return((char *)found); -} -#endif diff --git a/unrar/unrar/unicode.hpp b/unrar/unrar/unicode.hpp index 3b4e2c3..9bfd9c5 100644 --- a/unrar/unrar/unicode.hpp +++ b/unrar/unrar/unicode.hpp @@ -1,48 +1,31 @@ #ifndef _RAR_UNICODE_ #define _RAR_UNICODE_ -#ifndef _EMX -#define MBFUNCTIONS -#endif - -#if defined(MBFUNCTIONS) || defined(_WIN_32) || defined(_EMX) && !defined(_DJGPP) -#define UNICODE_SUPPORTED -#endif - -#ifdef _WIN_32 +#if defined( _WIN_ALL) #define DBCS_SUPPORTED #endif -#ifdef _EMX -int uni_init(int codepage); -int uni_done(); +bool WideToChar(const wchar *Src,char *Dest,size_t DestSize); +bool CharToWide(const char *Src,wchar *Dest,size_t DestSize); +byte* WideToRaw(const wchar *Src,size_t SrcSize,byte *Dest,size_t DestSize); +wchar* RawToWide(const byte *Src,wchar *Dest,size_t DestSize); +void WideToUtf(const wchar *Src,char *Dest,size_t DestSize); +size_t WideToUtfSize(const wchar *Src); +bool UtfToWide(const char *Src,wchar *Dest,size_t DestSize); +bool IsTextUtf8(const byte *Src); +bool IsTextUtf8(const byte *Src,size_t SrcSize); + +int wcsicomp(const wchar *s1,const wchar *s2); +int wcsnicomp(const wchar *s1,const wchar *s2,size_t n); +const wchar_t* wcscasestr(const wchar_t *str, const wchar_t *search); +#ifndef SFX_MODULE +wchar* wcslower(wchar *s); +wchar* wcsupper(wchar *s); #endif - -bool WideToChar(const wchar *Src,char *Dest,int DestSize=0x1000000); -bool CharToWide(const char *Src,wchar *Dest,int DestSize=0x1000000); -byte* WideToRaw(const wchar *Src,byte *Dest,int DestSize=0x1000000); -wchar* RawToWide(const byte *Src,wchar *Dest,int DestSize=0x1000000); -void WideToUtf(const wchar *Src,char *Dest,int DestSize); -void UtfToWide(const char *Src,wchar *Dest,int DestSize); -bool UnicodeEnabled(); - -int strlenw(const wchar *str); -wchar* strcpyw(wchar *dest,const wchar *src); -wchar* strncpyw(wchar *dest,const wchar *src,size_t n); -wchar* strcatw(wchar *dest,const wchar *src); -wchar* strncatw(wchar *dest,const wchar *src,size_t n); -int strcmpw(const wchar *s1,const wchar *s2); -int strncmpw(const wchar *s1,const wchar *s2,size_t n); -int stricmpw(const wchar *s1,const wchar *s2); -int strnicmpw(const wchar *s1,const wchar *s2,size_t n); -wchar *strchrw(const wchar *s,int c); -wchar* strrchrw(const wchar *s,int c); -wchar* strpbrkw(const wchar *s1,const wchar *s2); -wchar* strlowerw(wchar *Str); -wchar* strupperw(wchar *Str); -wchar* strdupw(const wchar *Str); int toupperw(int ch); +int tolowerw(int ch); int atoiw(const wchar *s); +int64 atoilw(const wchar *s); #ifdef DBCS_SUPPORTED class SupportDBCS @@ -50,34 +33,20 @@ class SupportDBCS public: SupportDBCS(); void Init(); - char* charnext(const char *s); - uint strlend(const char *s); - char *strchrd(const char *s, int c); - char *strrchrd(const char *s, int c); - void copychrd(char *dest,const char *src); bool IsLeadByte[256]; bool DBCSMode; }; - extern SupportDBCS gdbcs; inline char* charnext(const char *s) {return (char *)(gdbcs.DBCSMode ? gdbcs.charnext(s):s+1);} -inline uint strlend(const char *s) {return (uint)(gdbcs.DBCSMode ? gdbcs.strlend(s):strlen(s));} -inline char* strchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strchrd(s,c):strchr(s,c));} -inline char* strrchrd(const char *s, int c) {return (char *)(gdbcs.DBCSMode ? gdbcs.strrchrd(s,c):strrchr(s,c));} -inline void copychrd(char *dest,const char *src) {if (gdbcs.DBCSMode) gdbcs.copychrd(dest,src); else *dest=*src;} -inline bool IsDBCSMode() {return(gdbcs.DBCSMode);} -inline void InitDBCS() {gdbcs.Init();} +inline bool IsDBCSMode() {return gdbcs.DBCSMode;} #else #define charnext(s) ((s)+1) -#define strlend strlen -#define strchrd strchr -#define strrchrd strrchr -#define IsDBCSMode() (true) -inline void copychrd(char *dest,const char *src) {*dest=*src;} +#define IsDBCSMode() (false) #endif + #endif diff --git a/unrar/unrar/unios2.cpp b/unrar/unrar/unios2.cpp deleted file mode 100644 index 1261473..0000000 --- a/unrar/unrar/unios2.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// (c) 2001,2004 by Max Alekseyev -// ver. 2.1 - -#include <stddef.h> - -#define INCL_DOSMODULEMGR -#include <os2.h> - -typedef void* UconvObject; -typedef unsigned short UniChar; - -int uni_init(int codepage); - -int uni_done(); - -int uni_toucs( /* translate to Unicode */ - char*, /* I - input string */ - size_t, /* I - length of input string (chars) */ - UniChar*, /* O - output Unicode string */ - size_t* ); /* O - length of output string (UniChars) */ - -int uni_fromucs( /* translate from Unicode */ - UniChar*, /* I - input Unicode string */ - size_t, /* I - length of input string (UniChars) */ - char*, /* O - output string */ - size_t* ); /* O - length of output string (chars) */ - -/* IMPLEMENTATION */ - -static int (*uniMapCpToUcsCp) ( - unsigned long, /* I - Codepage to convert */ - UniChar*, /* O - Output buffer */ - size_t ); /* I - UniChars in output buffer */ - -static int (*uniCreateUconvObject) ( - UniChar*, /* I - Unicode name of uconv table */ - UconvObject* );/* O - Uconv object handle */ - -static int (*uniFreeUconvObject) ( - UconvObject ); /* I - Uconv object handle */ - -static int (*uniUconvToUcs) ( - UconvObject, /* I - Uconv object handle */ - void**, /* IO - Input buffer */ - size_t*, /* IO - Input buffer size (bytes) */ - UniChar**, /* IO - Output buffer size */ - size_t*, /* IO - Output size (chars) */ - size_t* ); /* IO - Substitution count */ - -static int (*uniUconvFromUcs) ( - UconvObject, /* I - Uconv object handle */ - UniChar**, /* IO - Input buffer */ - size_t*, /* IO - Input buffer size (bytes) */ - void**, /* IO - Output buffer size */ - size_t*, /* IO - Output size (chars) */ - size_t* ); /* IO - Substitution count */ - -static int uni_ready = 0; -static HMODULE uni_UCONV; -static UconvObject uni_obj; - -int uni_init(int codepage) { - UniChar unistr[256]; - - uni_ready = 0; - - if(!&DosLoadModule) { - /* DOS enviroment detected */ - return -1; - } - - if( DosLoadModule(0,0,(PCSZ)"UCONV",&uni_UCONV) ) { - /* no Unicode API found (obsolete OS/2 version) */ - return -2; - } - - if( !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniMapCpToUcsCp", (PPFN)&uniMapCpToUcsCp ) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniUconvToUcs", (PPFN)&uniUconvToUcs ) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniUconvFromUcs", (PPFN)&uniUconvFromUcs ) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniCreateUconvObject",(PPFN)&uniCreateUconvObject) && - !DosQueryProcAddr(uni_UCONV,0,(PCSZ)"UniFreeUconvObject", (PPFN)&uniFreeUconvObject ) - ) { - unistr[0] = 0; - if( (!codepage || !uniMapCpToUcsCp(codepage, unistr, 256)) && !uniCreateUconvObject(unistr,&uni_obj) ) { - uni_ready = 1; - return 0; - } - } - DosFreeModule(uni_UCONV); - return -2; -} - -int uni_toucs(char* src, size_t srclen, UniChar* dst, size_t* dstlen) { - size_t srcbytes, srcsize, dstsize, subsc=0; - - if(!uni_ready) return -1; - - dstsize = srcbytes = srclen * sizeof(UniChar); - - if( uniUconvToUcs(uni_obj,(void**)&src,&srclen,&dst,&dstsize,&subsc) ) { - return -1; - } - *dstlen = srcbytes - dstsize; - return 0; -} - -int uni_fromucs(UniChar* src, size_t srclen, char* dst, size_t* dstlen) { - size_t srcbytes, srcsize, dstsize, subsc=0; - - if(!uni_ready) return -1; - - dstsize = srcbytes = *dstlen; - - if( uniUconvFromUcs(uni_obj,&src,&srclen,(void**)&dst,&dstsize,&subsc) ) { - return -1; - } - *dstlen = srcbytes - dstsize; - return 0; -} - -int uni_done() { - if( uni_ready ) { - uniFreeUconvObject(uni_obj); - DosFreeModule(uni_UCONV); - uni_ready = 0; - } - return 0; -} diff --git a/unrar/unrar/unpack.cpp b/unrar/unrar/unpack.cpp index 3e26153..9236e74 100644 --- a/unrar/unrar/unpack.cpp +++ b/unrar/unrar/unpack.cpp @@ -3,1007 +3,363 @@ #include "coder.cpp" #include "suballoc.cpp" #include "model.cpp" +#include "unpackinline.cpp" +#ifdef RAR_SMP +#include "unpack50mt.cpp" +#endif #ifndef SFX_MODULE #include "unpack15.cpp" #include "unpack20.cpp" #endif +#include "unpack30.cpp" +#include "unpack50.cpp" +#include "unpack50frag.cpp" Unpack::Unpack(ComprDataIO *DataIO) +:Inp(true),VMCodeInp(true) { UnpIO=DataIO; Window=NULL; - ExternalWindow=false; + Fragmented=false; Suspended=false; UnpAllBuf=false; UnpSomeRead=false; -} - - -Unpack::~Unpack() -{ - if (Window!=NULL && !ExternalWindow) - delete[] Window; - InitFilters(); -} - - -void Unpack::Init(byte *Window) -{ - if (Window==NULL) - { - Unpack::Window=new byte[MAXWINSIZE]; -#ifndef ALLOW_EXCEPTIONS - if (Unpack::Window==NULL) - ErrHandler.MemoryError(); +#ifdef RAR_SMP + MaxUserThreads=1; + UnpThreadPool=NULL; + ReadBufMT=NULL; + UnpThreadData=NULL; #endif - } - else - { - Unpack::Window=Window; - ExternalWindow=true; - } - UnpInitData(false); + MaxWinSize=0; + MaxWinMask=0; + // Perform initialization, which should be done only once for all files. + // It prevents crash if first DoUnpack call is later made with wrong + // (true) 'Solid' value. + UnpInitData(false); #ifndef SFX_MODULE // RAR 1.5 decompression initialization - OldUnpInitData(false); + UnpInitData15(false); InitHuff(); #endif } -void Unpack::DoUnpack(int Method,bool Solid) +Unpack::~Unpack() { - switch(Method) - { -#ifndef SFX_MODULE - case 15: // rar 1.5 compression - Unpack15(Solid); - break; - case 20: // rar 2.x compression - case 26: // files larger than 2GB - Unpack20(Solid); - break; + InitFilters30(false); + + if (Window!=NULL) + free(Window); +#ifdef RAR_SMP + delete UnpThreadPool; + delete[] ReadBufMT; + delete[] UnpThreadData; #endif - case 29: // rar 3.x compression - case 36: // alternative hash - Unpack29(Solid); - break; - } -} - - -inline void Unpack::InsertOldDist(unsigned int Distance) -{ - OldDist[3]=OldDist[2]; - OldDist[2]=OldDist[1]; - OldDist[1]=OldDist[0]; - OldDist[0]=Distance; } -inline void Unpack::InsertLastMatch(unsigned int Length,unsigned int Distance) +#ifdef RAR_SMP +void Unpack::SetThreads(uint Threads) { - LastDist=Distance; - LastLength=Length; -} - - -void Unpack::CopyString(unsigned int Length,unsigned int Distance) -{ - unsigned int DestPtr=UnpPtr-Distance; - if (DestPtr<MAXWINSIZE-260 && UnpPtr<MAXWINSIZE-260) - { - Window[UnpPtr++]=Window[DestPtr++]; - while (--Length>0) - Window[UnpPtr++]=Window[DestPtr++]; - } - else - while (Length--) - { - Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK]; - UnpPtr=(UnpPtr+1) & MAXWINMASK; - } -} - - -int Unpack::DecodeNumber(struct Decode *Dec) -{ - unsigned int Bits; - unsigned int BitField=getbits() & 0xfffe; - if (BitField<Dec->DecodeLen[8]) - if (BitField<Dec->DecodeLen[4]) - if (BitField<Dec->DecodeLen[2]) - if (BitField<Dec->DecodeLen[1]) - Bits=1; - else - Bits=2; - else - if (BitField<Dec->DecodeLen[3]) - Bits=3; - else - Bits=4; - else - if (BitField<Dec->DecodeLen[6]) - if (BitField<Dec->DecodeLen[5]) - Bits=5; - else - Bits=6; - else - if (BitField<Dec->DecodeLen[7]) - Bits=7; - else - Bits=8; - else - if (BitField<Dec->DecodeLen[12]) - if (BitField<Dec->DecodeLen[10]) - if (BitField<Dec->DecodeLen[9]) - Bits=9; - else - Bits=10; - else - if (BitField<Dec->DecodeLen[11]) - Bits=11; - else - Bits=12; - else - if (BitField<Dec->DecodeLen[14]) - if (BitField<Dec->DecodeLen[13]) - Bits=13; - else - Bits=14; - else - Bits=15; - - addbits(Bits); - unsigned int N=Dec->DecodePos[Bits]+((BitField-Dec->DecodeLen[Bits-1])>>(16-Bits)); - if (N>=Dec->MaxNum) - N=0; - return(Dec->DecodeNum[N]); + // More than 8 threads are unlikely to provide noticeable gain + // for unpacking, but would use the additional memory. + MaxUserThreads=Min(Threads,8); + UnpThreadPool=new ThreadPool(MaxUserThreads); } +#endif -void Unpack::Unpack29(bool Solid) +void Unpack::Init(size_t WinSize,bool Solid) { - static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; - static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - static int DDecode[DC]; - static byte DBits[DC]; - static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; - static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; - static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; - unsigned int Bits; - - if (DDecode[1]==0) - { - int Dist=0,BitLength=0,Slot=0; - for (int I=0;I<sizeof(DBitLengthCounts)/sizeof(DBitLengthCounts[0]);I++,BitLength++) - for (int J=0;J<DBitLengthCounts[I];J++,Slot++,Dist+=(1<<BitLength)) - { - DDecode[Slot]=Dist; - DBits[Slot]=BitLength; - } - } + // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size + // will be 0 because of size_t overflow. Let's issue the memory error. + if (WinSize==0) + ErrHandler.MemoryError(); + + // Minimum window size must be at least twice more than maximum possible + // size of filter block, which is 0x10000 in RAR now. If window size is + // smaller, we can have a block with never cleared flt->NextWindow flag + // in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's + // use 0x40000 for extra safety and possible filter area size expansion. + const size_t MinAllocSize=0x40000; + if (WinSize<MinAllocSize) + WinSize=MinAllocSize; + + if (WinSize<=MaxWinSize) // Use the already allocated window. + return; + if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB. + return; - FileExtracted=true; + // Archiving code guarantees that window size does not grow in the same + // solid stream. So if we are here, we are either creating a new window + // or increasing the size of non-solid window. So we could safely reject + // current window data without copying them to a new window, though being + // extra cautious, we still handle the solid window grow case below. + bool Grow=Solid && (Window!=NULL || Fragmented); - if (!Suspended) - { - UnpInitData(Solid); - if (!UnpReadBuf()) - return; - if ((!Solid || !TablesRead) && !ReadTables()) - return; - } + // We do not handle growth for existing fragmented window. + if (Grow && Fragmented) + throw std::bad_alloc(); - while (true) - { - UnpPtr&=MAXWINMASK; + byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize); - if (InAddr>ReadBorder) + if (NewWindow==NULL) + if (Grow || WinSize<0x1000000) { - if (!UnpReadBuf()) - break; + // We do not support growth for new fragmented window. + // Also exclude RAR4 and small dictionaries. + throw std::bad_alloc(); } - if (((WrPtr-UnpPtr) & MAXWINMASK)<260 && WrPtr!=UnpPtr) + else { - UnpWriteBuf(); - if (WrittenFileSize>DestUnpSize) - return; - if (Suspended) + if (Window!=NULL) // If allocated by preceding files. { - FileExtracted=false; - return; + free(Window); + Window=NULL; } + FragWindow.Init(WinSize); + Fragmented=true; } - if (UnpBlockType==BLOCK_PPM) - { - int Ch=PPM.DecodeChar(); - if (Ch==-1) - { - PPM.CleanUp(); - // turn off PPM compression mode in case of error, so UnRAR will - // call PPM.DecodeInit in case it needs to turn it on back later. - UnpBlockType=BLOCK_LZ; - break; - } - if (Ch==PPMEscChar) - { - int NextCh=PPM.DecodeChar(); - if (NextCh==0) - { - if (!ReadTables()) - break; - continue; - } - if (NextCh==2 || NextCh==-1) - break; - if (NextCh==3) - { - if (!ReadVMCodePPM()) - break; - continue; - } - if (NextCh==4) - { - unsigned int Distance=0,Length; - bool Failed=false; - for (int I=0;I<4 && !Failed;I++) - { - int Ch=PPM.DecodeChar(); - if (Ch==-1) - Failed=true; - else - if (I==3) - Length=(byte)Ch; - else - Distance=(Distance<<8)+(byte)Ch; - } - if (Failed) - break; - -#ifdef _MSC_VER - // avoid a warning about uninitialized 'Length' variable - #pragma warning( disable : 4701 ) -#endif - CopyString(Length+32,Distance+2); - continue; - } - if (NextCh==5) - { - int Length=PPM.DecodeChar(); - if (Length==-1) - break; - CopyString(Length+4,1); - continue; - } - } - Window[UnpPtr++]=Ch; - continue; - } - - int Number=DecodeNumber((struct Decode *)&LD); - if (Number<256) - { - Window[UnpPtr++]=(byte)Number; - continue; - } - if (Number>=271) - { - int Length=LDecode[Number-=271]+3; - if ((Bits=LBits[Number])>0) - { - Length+=getbits()>>(16-Bits); - addbits(Bits); - } - - int DistNumber=DecodeNumber((struct Decode *)&DD); - unsigned int Distance=DDecode[DistNumber]+1; - if ((Bits=DBits[DistNumber])>0) - { - if (DistNumber>9) - { - if (Bits>4) - { - Distance+=((getbits()>>(20-Bits))<<4); - addbits(Bits-4); - } - if (LowDistRepCount>0) - { - LowDistRepCount--; - Distance+=PrevLowDist; - } - else - { - int LowDist=DecodeNumber((struct Decode *)&LDD); - if (LowDist==16) - { - LowDistRepCount=LOW_DIST_REP_COUNT-1; - Distance+=PrevLowDist; - } - else - { - Distance+=LowDist; - PrevLowDist=LowDist; - } - } - } - else - { - Distance+=getbits()>>(16-Bits); - addbits(Bits); - } - } + if (!Fragmented) + { + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access unused areas of sliding dictionary. + memset(NewWindow,0,WinSize); - if (Distance>=0x2000) - { - Length++; - if (Distance>=0x40000L) - Length++; - } + // If Window is not NULL, it means that window size has grown. + // In solid streams we need to copy data to a new window in such case. + // RAR archiving code does not allow it in solid streams now, + // but let's implement it anyway just in case we'll change it sometimes. + if (Grow) + for (size_t I=1;I<=MaxWinSize;I++) + NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)]; - InsertOldDist(Distance); - InsertLastMatch(Length,Distance); - CopyString(Length,Distance); - continue; - } - if (Number==256) - { - if (!ReadEndOfBlock()) - break; - continue; - } - if (Number==257) - { - if (!ReadVMCode()) - break; - continue; - } - if (Number==258) - { - if (LastLength!=0) - CopyString(LastLength,LastDist); - continue; - } - if (Number<263) - { - int DistNum=Number-259; - unsigned int Distance=OldDist[DistNum]; - for (int I=DistNum;I>0;I--) - OldDist[I]=OldDist[I-1]; - OldDist[0]=Distance; - - int LengthNumber=DecodeNumber((struct Decode *)&RD); - int Length=LDecode[LengthNumber]+2; - if ((Bits=LBits[LengthNumber])>0) - { - Length+=getbits()>>(16-Bits); - addbits(Bits); - } - InsertLastMatch(Length,Distance); - CopyString(Length,Distance); - continue; - } - if (Number<272) - { - unsigned int Distance=SDDecode[Number-=263]+1; - if ((Bits=SDBits[Number])>0) - { - Distance+=getbits()>>(16-Bits); - addbits(Bits); - } - InsertOldDist(Distance); - InsertLastMatch(2,Distance); - CopyString(2,Distance); - continue; - } + if (Window!=NULL) + free(Window); + Window=NewWindow; } - UnpWriteBuf(); + + MaxWinSize=WinSize; + MaxWinMask=MaxWinSize-1; } -bool Unpack::ReadEndOfBlock() +void Unpack::DoUnpack(uint Method,bool Solid) { - unsigned int BitField=getbits(); - bool NewTable,NewFile=false; - if (BitField & 0x8000) - { - NewTable=true; - addbits(1); - } - else + // Methods <50 will crash in Fragmented mode when accessing NULL Window. + // They cannot be called in such mode now, but we check it below anyway + // just for extra safety. + switch(Method) { - NewFile=true; - NewTable=(BitField & 0x4000); - addbits(2); +#ifndef SFX_MODULE + case 15: // rar 1.5 compression + if (!Fragmented) + Unpack15(Solid); + break; + case 20: // rar 2.x compression + case 26: // files larger than 2GB + if (!Fragmented) + Unpack20(Solid); + break; +#endif + case 29: // rar 3.x compression + if (!Fragmented) + Unpack29(Solid); + break; + case 50: // RAR 5.0 compression algorithm. +#ifdef RAR_SMP + if (MaxUserThreads>1) + { +// We do not use the multithreaded unpack routine to repack RAR archives +// in 'suspended' mode, because unlike the single threaded code it can +// write more than one dictionary for same loop pass. So we would need +// larger buffers of unknown size. Also we do not support multithreading +// in fragmented window mode. + if (!Fragmented) + { + Unpack5MT(Solid); + break; + } + } +#endif + Unpack5(Solid); + break; } - TablesRead=!NewTable; - return !(NewFile || NewTable && !ReadTables()); } -bool Unpack::ReadVMCode() +void Unpack::UnpInitData(bool Solid) { - unsigned int FirstByte=getbits()>>8; - addbits(8); - int Length=(FirstByte & 7)+1; - if (Length==7) - { - Length=(getbits()>>8)+7; - addbits(8); - } - else - if (Length==8) - { - Length=getbits(); - addbits(16); - } - Array<byte> VMCode(Length); - for (int I=0;I<Length;I++) + if (!Solid) { - if (InAddr>=ReadTop-1 && !UnpReadBuf() && I<Length-1) - return(false); - VMCode[I]=getbits()>>8; - addbits(8); + memset(OldDist,0,sizeof(OldDist)); + OldDistPtr=0; + LastDist=LastLength=0; +// memset(Window,0,MaxWinSize); + memset(&BlockTables,0,sizeof(BlockTables)); + UnpPtr=WrPtr=0; + WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; } - return(AddVMCode(FirstByte,&VMCode[0],Length)); -} + // Filters never share several solid files, so we can safely reset them + // even in solid archive. + InitFilters(); + Inp.InitBitInput(); + WrittenFileSize=0; + ReadTop=0; + ReadBorder=0; -bool Unpack::ReadVMCodePPM() -{ - unsigned int FirstByte=PPM.DecodeChar(); - if ((int)FirstByte==-1) - return(false); - int Length=(FirstByte & 7)+1; - if (Length==7) - { - int B1=PPM.DecodeChar(); - if (B1==-1) - return(false); - Length=B1+7; - } - else - if (Length==8) - { - int B1=PPM.DecodeChar(); - if (B1==-1) - return(false); - int B2=PPM.DecodeChar(); - if (B2==-1) - return(false); - Length=B1*256+B2; - } - Array<byte> VMCode(Length); - for (int I=0;I<Length;I++) - { - int Ch=PPM.DecodeChar(); - if (Ch==-1) - return(false); - VMCode[I]=Ch; - } - return(AddVMCode(FirstByte,&VMCode[0],Length)); + memset(&BlockHeader,0,sizeof(BlockHeader)); + BlockHeader.BlockSize=-1; // '-1' means not defined yet. +#ifndef SFX_MODULE + UnpInitData20(Solid); +#endif + UnpInitData30(Solid); + UnpInitData50(Solid); } -bool Unpack::AddVMCode(unsigned int FirstByte,byte *Code,int CodeSize) +// LengthTable contains the length in bits for every element of alphabet. +// Dec is the structure to decode Huffman code/ +// Size is size of length table and DecodeNum field in Dec structure, +void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) { - BitInput Inp; - Inp.InitBitInput(); - memcpy(Inp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize)); - VM.Init(); + // Size of alphabet and DecodePos array. + Dec->MaxNum=Size; - uint FiltPos; - if (FirstByte & 0x80) - { - FiltPos=RarVM::ReadData(Inp); - if (FiltPos==0) - InitFilters(); - else - FiltPos--; - } - else - FiltPos=LastFilter; // use the same filter as last time + // Calculate how many entries for every bit length in LengthTable we have. + uint LengthCount[16]; + memset(LengthCount,0,sizeof(LengthCount)); + for (size_t I=0;I<Size;I++) + LengthCount[LengthTable[I] & 0xf]++; - if (FiltPos>Filters.Size() || FiltPos>OldFilterLengths.Size()) - return(false); - LastFilter=FiltPos; - bool NewFilter=(FiltPos==Filters.Size()); + // We must not calculate the number of zero length codes. + LengthCount[0]=0; - UnpackFilter *StackFilter=new UnpackFilter; // new filter for PrgStack + // Set the entire DecodeNum to zero. + memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); - UnpackFilter *Filter; - if (NewFilter) // new filter code, never used before since VM reset - { - // too many different filters, corrupt archive - if (FiltPos>1024) - return(false); - - Filters.Add(1); - Filters[Filters.Size()-1]=Filter=new UnpackFilter; - StackFilter->ParentFilter=Filters.Size()-1; - OldFilterLengths.Add(1); - Filter->ExecCount=0; - } - else // filter was used in the past - { - Filter=Filters[FiltPos]; - StackFilter->ParentFilter=FiltPos; - Filter->ExecCount++; - } + // Initialize not really used entry for zero length code. + Dec->DecodePos[0]=0; - int EmptyCount=0; - for (int I=0;I<PrgStack.Size();I++) - { - PrgStack[I-EmptyCount]=PrgStack[I]; - if (PrgStack[I]==NULL) - EmptyCount++; - if (EmptyCount>0) - PrgStack[I]=NULL; - } - if (EmptyCount==0) - { - PrgStack.Add(1); - EmptyCount=1; - } - int StackPos=PrgStack.Size()-EmptyCount; - PrgStack[StackPos]=StackFilter; - StackFilter->ExecCount=Filter->ExecCount; - - uint BlockStart=RarVM::ReadData(Inp); - if (FirstByte & 0x40) - BlockStart+=258; - StackFilter->BlockStart=(BlockStart+UnpPtr)&MAXWINMASK; - if (FirstByte & 0x20) - StackFilter->BlockLength=RarVM::ReadData(Inp); - else - StackFilter->BlockLength=FiltPos<OldFilterLengths.Size() ? OldFilterLengths[FiltPos]:0; - StackFilter->NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MAXWINMASK)<=BlockStart; - -// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); - - OldFilterLengths[FiltPos]=StackFilter->BlockLength; - - memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); - StackFilter->Prg.InitR[3]=VM_GLOBALMEMADDR; - StackFilter->Prg.InitR[4]=StackFilter->BlockLength; - StackFilter->Prg.InitR[5]=StackFilter->ExecCount; - - if (FirstByte & 0x10) // set registers to optional parameters if any - { - unsigned int InitMask=Inp.fgetbits()>>9; - Inp.faddbits(7); - for (int I=0;I<7;I++) - if (InitMask & (1<<I)) - StackFilter->Prg.InitR[I]=RarVM::ReadData(Inp); - } + // Start code for bit length 1 is 0. + Dec->DecodeLen[0]=0; - if (NewFilter) - { - uint VMCodeSize=RarVM::ReadData(Inp); - if (VMCodeSize>=0x10000 || VMCodeSize==0) - return(false); - Array<byte> VMCode(VMCodeSize); - for (int I=0;I<VMCodeSize;I++) - { - if (Inp.Overflow(3)) - return(false); - VMCode[I]=Inp.fgetbits()>>8; - Inp.faddbits(8); - } - VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); - } - StackFilter->Prg.AltCmd=&Filter->Prg.Cmd[0]; - StackFilter->Prg.CmdCount=Filter->Prg.CmdCount; + // Right aligned upper limit code for current bit length. + uint UpperLimit=0; - int StaticDataSize=Filter->Prg.StaticData.Size(); - if (StaticDataSize>0 && StaticDataSize<VM_GLOBALMEMSIZE) + for (size_t I=1;I<16;I++) { - // read statically defined data contained in DB commands - StackFilter->Prg.StaticData.Add(StaticDataSize); - memcpy(&StackFilter->Prg.StaticData[0],&Filter->Prg.StaticData[0],StaticDataSize); - } + // Adjust the upper limit code. + UpperLimit+=LengthCount[I]; - if (StackFilter->Prg.GlobalData.Size()<VM_FIXEDGLOBALSIZE) - { - StackFilter->Prg.GlobalData.Reset(); - StackFilter->Prg.GlobalData.Add(VM_FIXEDGLOBALSIZE); - } - byte *GlobalData=&StackFilter->Prg.GlobalData[0]; - for (int I=0;I<7;I++) - VM.SetLowEndianValue((uint *)&GlobalData[I*4],StackFilter->Prg.InitR[I]); - VM.SetLowEndianValue((uint *)&GlobalData[0x1c],StackFilter->BlockLength); - VM.SetLowEndianValue((uint *)&GlobalData[0x20],0); - VM.SetLowEndianValue((uint *)&GlobalData[0x2c],StackFilter->ExecCount); - memset(&GlobalData[0x30],0,16); - - if (FirstByte & 8) // put data block passed as parameter if any - { - if (Inp.Overflow(3)) - return(false); - uint DataSize=RarVM::ReadData(Inp); - if (DataSize>VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE) - return(false); - unsigned int CurSize=StackFilter->Prg.GlobalData.Size(); - if (CurSize<DataSize+VM_FIXEDGLOBALSIZE) - StackFilter->Prg.GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE-CurSize); - byte *GlobalData=&StackFilter->Prg.GlobalData[VM_FIXEDGLOBALSIZE]; - for (int I=0;I<DataSize;I++) - { - if (Inp.Overflow(3)) - return(false); - GlobalData[I]=Inp.fgetbits()>>8; - Inp.faddbits(8); - } - } - return(true); -} + // Left aligned upper limit code. + uint LeftAligned=UpperLimit<<(16-I); + // Prepare the upper limit code for next bit length. + UpperLimit*=2; -bool Unpack::UnpReadBuf() -{ - int DataSize=ReadTop-InAddr; - if (DataSize<0) - return(false); - if (InAddr>BitInput::MAX_SIZE/2) - { - if (DataSize>0) - memmove(InBuf,InBuf+InAddr,DataSize); - InAddr=0; - ReadTop=DataSize; + // Store the left aligned upper limit code. + Dec->DecodeLen[I]=(uint)LeftAligned; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1]; } - else - DataSize=ReadTop; - int ReadCode=UnpIO->UnpRead(InBuf+DataSize,(BitInput::MAX_SIZE-DataSize)&~0xf); - if (ReadCode>0) - ReadTop+=ReadCode; - ReadBorder=ReadTop-30; - return(ReadCode!=-1); -} + // Prepare the copy of DecodePos. We'll modify this copy below, + // so we cannot use the original DecodePos. + uint CopyDecodePos[ASIZE(Dec->DecodePos)]; + memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); -void Unpack::UnpWriteBuf() -{ - unsigned int WrittenBorder=WrPtr; - unsigned int WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK; - for (int I=0;I<PrgStack.Size();I++) + // For every bit length in the bit length table and so for every item + // of alphabet. + for (uint I=0;I<Size;I++) { - UnpackFilter *flt=PrgStack[I]; - if (flt==NULL) - continue; - if (flt->NextWindow) - { - flt->NextWindow=false; - continue; - } - unsigned int BlockStart=flt->BlockStart; - unsigned int BlockLength=flt->BlockLength; - if (((BlockStart-WrittenBorder)&MAXWINMASK)<WriteSize) - { - if (WrittenBorder!=BlockStart) - { - UnpWriteArea(WrittenBorder,BlockStart); - WrittenBorder=BlockStart; - WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK; - } - if (BlockLength<=WriteSize) - { - unsigned int BlockEnd=(BlockStart+BlockLength)&MAXWINMASK; - if (BlockStart<BlockEnd || BlockEnd==0) - VM.SetMemory(0,Window+BlockStart,BlockLength); - else - { - unsigned int FirstPartLength=MAXWINSIZE-BlockStart; - VM.SetMemory(0,Window+BlockStart,FirstPartLength); - VM.SetMemory(FirstPartLength,Window,BlockEnd); - } - - VM_PreparedProgram *ParentPrg=&Filters[flt->ParentFilter]->Prg; - VM_PreparedProgram *Prg=&flt->Prg; - - if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // copy global data from previous script execution if any - Prg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); - memcpy(&Prg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } - - ExecuteCode(Prg); - - if (Prg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // save global data for next script execution - if (ParentPrg->GlobalData.Size()<Prg->GlobalData.Size()) - ParentPrg->GlobalData.Alloc(Prg->GlobalData.Size()); - memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&Prg->GlobalData[VM_FIXEDGLOBALSIZE],Prg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } - else - ParentPrg->GlobalData.Reset(); - - byte *FilteredData=Prg->FilteredData; - unsigned int FilteredDataSize=Prg->FilteredDataSize; - - delete PrgStack[I]; - PrgStack[I]=NULL; - while (I+1<PrgStack.Size()) - { - UnpackFilter *NextFilter=PrgStack[I+1]; - if (NextFilter==NULL || NextFilter->BlockStart!=BlockStart || - NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) - break; - - // apply several filters to same data block - - VM.SetMemory(0,FilteredData,FilteredDataSize); - - VM_PreparedProgram *ParentPrg=&Filters[NextFilter->ParentFilter]->Prg; - VM_PreparedProgram *NextPrg=&NextFilter->Prg; + // Get the current bit length. + byte CurBitLength=LengthTable[I] & 0xf; - if (ParentPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // copy global data from previous script execution if any - NextPrg->GlobalData.Alloc(ParentPrg->GlobalData.Size()); - memcpy(&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],ParentPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } + if (CurBitLength!=0) + { + // Last position in code list for current bit length. + uint LastPos=CopyDecodePos[CurBitLength]; - ExecuteCode(NextPrg); + // Prepare the decode table, so this position in code list will be + // decoded to current alphabet item number. + Dec->DecodeNum[LastPos]=(ushort)I; - if (NextPrg->GlobalData.Size()>VM_FIXEDGLOBALSIZE) - { - // save global data for next script execution - if (ParentPrg->GlobalData.Size()<NextPrg->GlobalData.Size()) - ParentPrg->GlobalData.Alloc(NextPrg->GlobalData.Size()); - memcpy(&ParentPrg->GlobalData[VM_FIXEDGLOBALSIZE],&NextPrg->GlobalData[VM_FIXEDGLOBALSIZE],NextPrg->GlobalData.Size()-VM_FIXEDGLOBALSIZE); - } - else - ParentPrg->GlobalData.Reset(); - - FilteredData=NextPrg->FilteredData; - FilteredDataSize=NextPrg->FilteredDataSize; - I++; - delete PrgStack[I]; - PrgStack[I]=NULL; - } - UnpIO->UnpWrite(FilteredData,FilteredDataSize); - UnpSomeRead=true; - WrittenFileSize+=FilteredDataSize; - WrittenBorder=BlockEnd; - WriteSize=(UnpPtr-WrittenBorder)&MAXWINMASK; - } - else - { - for (int J=I;J<PrgStack.Size();J++) - { - UnpackFilter *flt=PrgStack[J]; - if (flt!=NULL && flt->NextWindow) - flt->NextWindow=false; - } - WrPtr=WrittenBorder; - return; - } + // We'll use next position number for this bit length next time. + // So we pass through the entire range of positions available + // for every bit length. + CopyDecodePos[CurBitLength]++; } } - - UnpWriteArea(WrittenBorder,UnpPtr); - WrPtr=UnpPtr; -} - -void Unpack::ExecuteCode(VM_PreparedProgram *Prg) -{ - if (Prg->GlobalData.Size()>0) + // Define the number of bits to process in quick mode. We use more bits + // for larger alphabets. More bits means that more codes will be processed + // in quick mode, but also that more time will be spent to preparation + // of tables for quick decode. + switch (Size) { - Prg->InitR[6]=int64to32(WrittenFileSize); - VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x24],int64to32(WrittenFileSize)); - VM.SetLowEndianValue((uint *)&Prg->GlobalData[0x28],int64to32(WrittenFileSize>>32)); - VM.Execute(Prg); + case NC: + case NC20: + case NC30: + Dec->QuickBits=MAX_QUICK_DECODE_BITS; + break; + default: + Dec->QuickBits=MAX_QUICK_DECODE_BITS>3 ? MAX_QUICK_DECODE_BITS-3 : 0; + break; } -} + // Size of tables for quick mode. + uint QuickDataSize=1<<Dec->QuickBits; -void Unpack::UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr) -{ - if (EndPtr!=StartPtr) - UnpSomeRead=true; - if (EndPtr<StartPtr) + // Bit length for current code, start from 1 bit codes. It is important + // to use 1 bit instead of 0 for minimum code length, so we are moving + // forward even when processing a corrupt archive. + uint CurBitLength=1; + + // For every right aligned bit string which supports the quick decoding. + for (uint Code=0;Code<QuickDataSize;Code++) { - UnpWriteData(&Window[StartPtr],-StartPtr & MAXWINMASK); - UnpWriteData(Window,EndPtr); - UnpAllBuf=true; - } - else - UnpWriteData(&Window[StartPtr],EndPtr-StartPtr); -} + // Left align the current code, so it will be in usual bit field format. + uint BitField=Code<<(16-Dec->QuickBits); + // Prepare the table for quick decoding of bit lengths. + + // Find the upper limit for current bit field and adjust the bit length + // accordingly if necessary. + while (CurBitLength<ASIZE(Dec->DecodeLen) && BitField>=Dec->DecodeLen[CurBitLength]) + CurBitLength++; -void Unpack::UnpWriteData(byte *Data,int Size) -{ - if (WrittenFileSize>=DestUnpSize) - return; - int WriteSize=Size; - Int64 LeftToWrite=DestUnpSize-WrittenFileSize; - if (WriteSize>LeftToWrite) - WriteSize=int64to32(LeftToWrite); - UnpIO->UnpWrite(Data,WriteSize); - WrittenFileSize+=Size; -} + // Translation of right aligned bit string to bit length. + Dec->QuickLen[Code]=CurBitLength; + // Prepare the table for quick translation of position in code list + // to position in alphabet. -bool Unpack::ReadTables() -{ - byte BitLength[BC]; - unsigned char Table[HUFF_TABLE_SIZE]; - if (InAddr>ReadTop-25) - if (!UnpReadBuf()) - return(false); - faddbits((8-InBit)&7); - unsigned int BitField=fgetbits(); - if (BitField & 0x8000) - { - UnpBlockType=BLOCK_PPM; - return(PPM.DecodeInit(this,PPMEscChar)); - } - UnpBlockType=BLOCK_LZ; - - PrevLowDist=0; - LowDistRepCount=0; + // Calculate the distance from the start code for current bit length. + uint Dist=BitField-Dec->DecodeLen[CurBitLength-1]; - if (!(BitField & 0x4000)) - memset(UnpOldTable,0,sizeof(UnpOldTable)); - faddbits(2); + // Right align the distance. + Dist>>=(16-CurBitLength); - for (int I=0;I<BC;I++) - { - int Length=(byte)(fgetbits() >> 12); - faddbits(4); - if (Length==15) + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + uint Pos; + if (CurBitLength<ASIZE(Dec->DecodePos) && + (Pos=Dec->DecodePos[CurBitLength]+Dist)<Size) { - int ZeroCount=(byte)(fgetbits() >> 12); - faddbits(4); - if (ZeroCount==0) - BitLength[I]=15; - else - { - ZeroCount+=2; - while (ZeroCount-- > 0 && I<sizeof(BitLength)/sizeof(BitLength[0])) - BitLength[I++]=0; - I--; - } + // Define the code to alphabet number translation. + Dec->QuickNum[Code]=Dec->DecodeNum[Pos]; } else - BitLength[I]=Length; - } - MakeDecodeTables(BitLength,(struct Decode *)&BD,BC); - - const int TableSize=HUFF_TABLE_SIZE; - for (int I=0;I<TableSize;) - { - if (InAddr>ReadTop-5) - if (!UnpReadBuf()) - return(false); - int Number=DecodeNumber((struct Decode *)&BD); - if (Number<16) { - Table[I]=(Number+UnpOldTable[I]) & 0xf; - I++; + // Can be here for length table filled with zeroes only (empty). + Dec->QuickNum[Code]=0; } - else - if (Number<18) - { - int N; - if (Number==16) - { - N=(fgetbits() >> 13)+3; - faddbits(3); - } - else - { - N=(fgetbits() >> 9)+11; - faddbits(7); - } - while (N-- > 0 && I<TableSize) - { - Table[I]=Table[I-1]; - I++; - } - } - else - { - int N; - if (Number==18) - { - N=(fgetbits() >> 13)+3; - faddbits(3); - } - else - { - N=(fgetbits() >> 9)+11; - faddbits(7); - } - while (N-- > 0 && I<TableSize) - Table[I++]=0; - } } - TablesRead=true; - if (InAddr>ReadTop) - return(false); - MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC); - MakeDecodeTables(&Table[NC],(struct Decode *)&DD,DC); - MakeDecodeTables(&Table[NC+DC],(struct Decode *)&LDD,LDC); - MakeDecodeTables(&Table[NC+DC+LDC],(struct Decode *)&RD,RC); - memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); - return(true); -} - - -void Unpack::UnpInitData(int Solid) -{ - if (!Solid) - { - TablesRead=false; - memset(OldDist,0,sizeof(OldDist)); - OldDistPtr=0; - LastDist=LastLength=0; -// memset(Window,0,MAXWINSIZE); - memset(UnpOldTable,0,sizeof(UnpOldTable)); - memset(&LD,0,sizeof(LD)); - memset(&DD,0,sizeof(DD)); - memset(&LDD,0,sizeof(LDD)); - memset(&RD,0,sizeof(RD)); - memset(&BD,0,sizeof(BD)); - UnpPtr=WrPtr=0; - PPMEscChar=2; - UnpBlockType=BLOCK_LZ; - - InitFilters(); - } - InitBitInput(); - WrittenFileSize=0; - ReadTop=0; - ReadBorder=0; -#ifndef SFX_MODULE - UnpInitData20(Solid); -#endif -} - - -void Unpack::InitFilters() -{ - OldFilterLengths.Reset(); - LastFilter=0; - - for (int I=0;I<Filters.Size();I++) - delete Filters[I]; - Filters.Reset(); - for (int I=0;I<PrgStack.Size();I++) - delete PrgStack[I]; - PrgStack.Reset(); -} - - -void Unpack::MakeDecodeTables(unsigned char *LenTab,struct Decode *Dec,int Size) -{ - int LenCount[16],TmpPos[16],I; - long M,N; - memset(LenCount,0,sizeof(LenCount)); - memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); - for (I=0;I<Size;I++) - LenCount[LenTab[I] & 0xF]++; - - LenCount[0]=0; - for (TmpPos[0]=Dec->DecodePos[0]=Dec->DecodeLen[0]=0,N=0,I=1;I<16;I++) - { - N=2*(N+LenCount[I]); - M=N<<(15-I); - if (M>0xFFFF) - M=0xFFFF; - Dec->DecodeLen[I]=(unsigned int)M; - TmpPos[I]=Dec->DecodePos[I]=Dec->DecodePos[I-1]+LenCount[I-1]; - } - - for (I=0;I<Size;I++) - if (LenTab[I]!=0) - Dec->DecodeNum[TmpPos[LenTab[I] & 0xF]++]=I; - Dec->MaxNum=Size; } diff --git a/unrar/unrar/unpack.hpp b/unrar/unrar/unpack.hpp index 75df302..737b31c 100644 --- a/unrar/unrar/unpack.hpp +++ b/unrar/unrar/unpack.hpp @@ -1,80 +1,173 @@ #ifndef _RAR_UNPACK_ #define _RAR_UNPACK_ -enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM}; - -struct Decode +// Maximum allowed number of compressed bits processed in quick mode. +#define MAX_QUICK_DECODE_BITS 10 + +// Maximum number of filters per entire data block. Must be at least +// twice more than MAX_PACK_FILTERS to store filters from two data blocks. +#define MAX_UNPACK_FILTERS 8192 + +// Maximum number of filters per entire data block for RAR3 unpack. +// Must be at least twice more than v3_MAX_PACK_FILTERS to store filters +// from two data blocks. +#define MAX3_UNPACK_FILTERS 8192 + +// Limit maximum number of channels in RAR3 delta filter to some reasonable +// value to prevent too slow processing of corrupt archives with invalid +// channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS. +// No need to provide it for RAR5, which uses only 5 bits to store channels. +#define MAX3_UNPACK_CHANNELS 1024 + +// Maximum size of single filter block. We restrict it to limit memory +// allocation. Must be equal or larger than MAX_ANALYZE_SIZE. +#define MAX_FILTER_BLOCK_SIZE 0x400000 + +// Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_READ, +// so we keep the number of buffered filters in unpacker reasonable. +#define UNPACK_MAX_WRITE 0x400000 + +// Decode compressed bit fields to alphabet numbers. +struct DecodeTable:PackDef { - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[2]; + // Real size of DecodeNum table. + uint MaxNum; + + // Left aligned start and upper limit codes defining code space + // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of + // range for bit length and DecodeLen[BitLength] defines next code + // after the end of range or in other words the upper limit code + // for specified bit length. + uint DecodeLen[16]; + + // Every item of this array contains the sum of all preceding items. + // So it contains the start position in code list for every bit length. + uint DecodePos[16]; + + // Number of compressed bits processed in quick mode. + // Must not exceed MAX_QUICK_DECODE_BITS. + uint QuickBits; + + // Translates compressed bits (up to QuickBits length) + // to bit length in quick mode. + byte QuickLen[1<<MAX_QUICK_DECODE_BITS]; + + // Translates compressed bits (up to QuickBits length) + // to position in alphabet in quick mode. + // 'ushort' saves some memory and even provides a little speed gain + // comparting to 'uint' here. + ushort QuickNum[1<<MAX_QUICK_DECODE_BITS]; + + // Translate the position in code list to position in alphabet. + // We do not allocate it dynamically to avoid performance overhead + // introduced by pointer, so we use the largest possible table size + // as array dimension. Real size of this array is defined in MaxNum. + // We use this array if compressed bit field is too lengthy + // for QuickLen based translation. + // 'ushort' saves some memory and even provides a little speed gain + // comparting to 'uint' here. + ushort DecodeNum[LARGEST_TABLE_SIZE]; }; -struct LitDecode + +struct UnpackBlockHeader { - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[NC]; + int BlockSize; + int BlockBitSize; + int BlockStart; + int HeaderSize; + bool LastBlockInFile; + bool TablePresent; }; -struct DistDecode + +struct UnpackBlockTables { - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[DC]; + DecodeTable LD; // Decode literals. + DecodeTable DD; // Decode distances. + DecodeTable LDD; // Decode lower bits of distances. + DecodeTable RD; // Decode repeating distances. + DecodeTable BD; // Decode bit lengths in Huffman table. }; -struct LowDistDecode -{ - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[LDC]; + +#ifdef RAR_SMP +enum UNP_DEC_TYPE { + UNPDT_LITERAL=0,UNPDT_MATCH,UNPDT_FULLREP,UNPDT_REP,UNPDT_FILTER }; -struct RepDecode +struct UnpackDecodedItem { - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[RC]; + byte Type; // 'byte' instead of enum type to reduce memory use. + ushort Length; + union + { + uint Distance; + byte Literal[8]; // Store up to 8 chars here to speed up extraction. + }; }; -struct BitDecode + +struct UnpackThreadData { - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[BC]; + Unpack *UnpackPtr; + BitInput Inp; + bool HeaderRead; + UnpackBlockHeader BlockHeader; + bool TableRead; + UnpackBlockTables BlockTables; + int DataSize; // Data left in buffer. Can be less than block size. + bool DamagedData; + bool LargeBlock; + bool NoDataLeft; // 'true' if file is read completely. + bool Incomplete; // Not entire block was processed, need to read more data. + + UnpackDecodedItem *Decoded; + uint DecodedSize; + uint DecodedAllocated; + uint ThreadNumber; // For debugging. + + UnpackThreadData() + :Inp(false) + { + Decoded=NULL; + } + ~UnpackThreadData() + { + if (Decoded!=NULL) + free(Decoded); + } }; +#endif + struct UnpackFilter { + byte Type; + uint BlockStart; + uint BlockLength; + byte Channels; +// uint Width; +// byte PosR; + bool NextWindow; +}; + + +struct UnpackFilter30 +{ unsigned int BlockStart; unsigned int BlockLength; - unsigned int ExecCount; bool NextWindow; - // position of parent filter in Filters array used as prototype for filter + // Position of parent filter in Filters array used as prototype for filter // in PrgStack array. Not defined for filters in Filters array. unsigned int ParentFilter; VM_PreparedProgram Prg; }; -/***************************** Unpack v 2.0 *********************************/ -struct MultDecode -{ - unsigned int MaxNum; - unsigned int DecodeLen[16]; - unsigned int DecodePos[16]; - unsigned int DecodeNum[MC20]; -}; -struct AudioVariables +struct AudioVariables // For RAR 2.0 archives only. { int K1,K2,K3,K4,K5; int D1,D2,D3,D4; @@ -83,84 +176,112 @@ struct AudioVariables unsigned int ByteCount; int LastChar; }; -/***************************** Unpack v 2.0 *********************************/ -class Unpack:private BitInput +// We can use the fragmented dictionary in case heap does not have the single +// large enough memory block. It is slower than normal dictionary. +class FragmentedWindow { private: - friend class Pack; + enum {MAX_MEM_BLOCKS=32}; - void Unpack29(bool Solid); + void Reset(); + byte *Mem[MAX_MEM_BLOCKS]; + size_t MemSize[MAX_MEM_BLOCKS]; + public: + FragmentedWindow(); + ~FragmentedWindow(); + void Init(size_t WinSize); + byte& operator [](size_t Item); + void CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask); + void CopyData(byte *Dest,size_t WinPos,size_t Size); + size_t GetBlockSize(size_t StartPos,size_t RequiredSize); +}; + + +class Unpack:PackDef +{ + private: + + void Unpack5(bool Solid); + void Unpack5MT(bool Solid); bool UnpReadBuf(); void UnpWriteBuf(); - void ExecuteCode(VM_PreparedProgram *Prg); - void UnpWriteArea(unsigned int StartPtr,unsigned int EndPtr); - void UnpWriteData(byte *Data,int Size); - bool ReadTables(); - void MakeDecodeTables(unsigned char *LenTab,struct Decode *Dec,int Size); - int DecodeNumber(struct Decode *Dec); + byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt); + void UnpWriteArea(size_t StartPtr,size_t EndPtr); + void UnpWriteData(byte *Data,size_t Size); + _forceinline uint SlotToLength(BitInput &Inp,uint Slot); + void UnpInitData50(bool Solid); + bool ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header); + bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables); + void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size); + _forceinline uint DecodeNumber(BitInput &Inp,DecodeTable *Dec); void CopyString(); inline void InsertOldDist(unsigned int Distance); - inline void InsertLastMatch(unsigned int Length,unsigned int Distance); - void UnpInitData(int Solid); - void CopyString(unsigned int Length,unsigned int Distance); - bool ReadEndOfBlock(); - bool ReadVMCode(); - bool ReadVMCodePPM(); - bool AddVMCode(unsigned int FirstByte,byte *Code,int CodeSize); + void UnpInitData(bool Solid); + _forceinline void CopyString(uint Length,uint Distance); + uint ReadFilterData(BitInput &Inp); + bool ReadFilter(BitInput &Inp,UnpackFilter &Filter); + bool AddFilter(UnpackFilter &Filter); + bool AddFilter(); void InitFilters(); ComprDataIO *UnpIO; - ModelPPM PPM; - int PPMEscChar; + BitInput Inp; - RarVM VM; +#ifdef RAR_SMP + void InitMT(); + bool UnpackLargeBlock(UnpackThreadData &D); + bool ProcessDecoded(UnpackThreadData &D); - /* Filters code, one entry per filter */ - Array<UnpackFilter*> Filters; + ThreadPool *UnpThreadPool; + UnpackThreadData *UnpThreadData; + uint MaxUserThreads; + byte *ReadBufMT; +#endif - /* Filters stack, several entrances of same filter are possible */ - Array<UnpackFilter*> PrgStack; + Array<byte> FilterSrcMemory; + Array<byte> FilterDstMemory; - /* lengths of preceding blocks, one length per filter. Used to reduce - size required to write block length if lengths are repeating */ - Array<int> OldFilterLengths; + // Filters code, one entry per filter. + Array<UnpackFilter> Filters; - int LastFilter; + uint OldDist[4],OldDistPtr; + uint LastLength; - bool TablesRead; - struct LitDecode LD; - struct DistDecode DD; - struct LowDistDecode LDD; - struct RepDecode RD; - struct BitDecode BD; + // LastDist is necessary only for RAR2 and older with circular OldDist + // array. In RAR3 last distance is always stored in OldDist[0]. + uint LastDist; - unsigned int OldDist[4],OldDistPtr; - unsigned int LastDist,LastLength; - - unsigned int UnpPtr,WrPtr; + size_t UnpPtr,WrPtr; - int ReadTop; + // Top border of read packed data. + int ReadTop; + + // Border to call UnpReadBuf. We use it instead of (ReadTop-C) + // for optimization reasons. Ensures that we have C bytes in buffer + // unless we are at the end of file. int ReadBorder; - unsigned char UnpOldTable[HUFF_TABLE_SIZE]; + UnpackBlockHeader BlockHeader; + UnpackBlockTables BlockTables; - int UnpBlockType; + size_t WriteBorder; byte *Window; - bool ExternalWindow; + FragmentedWindow FragWindow; + bool Fragmented; - Int64 DestUnpSize; + + int64 DestUnpSize; bool Suspended; bool UnpAllBuf; bool UnpSomeRead; - Int64 WrittenFileSize; + int64 WrittenFileSize; bool FileExtracted; - int PrevLowDist,LowDistRepCount; /***************************** Unpack v 1.5 *********************************/ void Unpack15(bool Solid); @@ -168,49 +289,115 @@ class Unpack:private BitInput void LongLZ(); void HuffDecode(); void GetFlagsBuf(); - void OldUnpInitData(int Solid); + void UnpInitData15(int Solid); void InitHuff(); - void CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace); - void OldCopyString(unsigned int Distance,unsigned int Length); - unsigned int DecodeNum(int Num,unsigned int StartPos, - unsigned int *DecTab,unsigned int *PosTab); - void OldUnpWriteBuf(); - - unsigned int ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256]; - unsigned int Place[256],PlaceA[256],PlaceB[256],PlaceC[256]; - unsigned int NToPl[256],NToPlB[256],NToPlC[256]; - unsigned int FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; + void CorrHuff(ushort *CharSet,byte *NumToPlace); + void CopyString15(uint Distance,uint Length); + uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); + + ushort ChSet[256],ChSetA[256],ChSetB[256],ChSetC[256]; + byte NToPl[256],NToPlB[256],NToPlC[256]; + uint FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; int Buf60,NumHuf,StMode,LCount,FlagsCnt; - unsigned int Nhfb,Nlzb,MaxDist3; + uint Nhfb,Nlzb,MaxDist3; /***************************** Unpack v 1.5 *********************************/ /***************************** Unpack v 2.0 *********************************/ void Unpack20(bool Solid); - struct MultDecode MD[4]; + + DecodeTable MD[4]; // Decode multimedia data, up to 4 channels. + unsigned char UnpOldTable20[MC20*4]; - int UnpAudioBlock,UnpChannels,UnpCurChannel,UnpChannelDelta; - void CopyString20(unsigned int Length,unsigned int Distance); + bool UnpAudioBlock; + uint UnpChannels,UnpCurChannel; + int UnpChannelDelta; + void CopyString20(uint Length,uint Distance); bool ReadTables20(); + void UnpWriteBuf20(); void UnpInitData20(int Solid); void ReadLastTables(); byte DecodeAudio(int Delta); struct AudioVariables AudV[4]; /***************************** Unpack v 2.0 *********************************/ +/***************************** Unpack v 3.0 *********************************/ + enum BLOCK_TYPES {BLOCK_LZ,BLOCK_PPM}; + + void UnpInitData30(bool Solid); + void Unpack29(bool Solid); + void InitFilters30(bool Solid); + bool ReadEndOfBlock(); + bool ReadVMCode(); + bool ReadVMCodePPM(); + bool AddVMCode(uint FirstByte,byte *Code,uint CodeSize); + int SafePPMDecodeChar(); + bool ReadTables30(); + bool UnpReadBuf30(); + void UnpWriteBuf30(); + void ExecuteCode(VM_PreparedProgram *Prg); + + int PrevLowDist,LowDistRepCount; + + ModelPPM PPM; + int PPMEscChar; + + byte UnpOldTable[HUFF_TABLE_SIZE30]; + int UnpBlockType; + + // If we already read decoding tables for Unpack v2,v3,v5. + // We should not use a single variable for all algorithm versions, + // because we can have a corrupt archive with one algorithm file + // followed by another algorithm file with "solid" flag and we do not + // want to reuse tables from one algorithm in another. + bool TablesRead2,TablesRead3,TablesRead5; + + // Virtual machine to execute filters code. + RarVM VM; + + // Buffer to read VM filters code. We moved it here from AddVMCode + // function to reduce time spent in BitInput constructor. + BitInput VMCodeInp; + + // Filters code, one entry per filter. + Array<UnpackFilter30 *> Filters30; + + // Filters stack, several entrances of same filter are possible. + Array<UnpackFilter30 *> PrgStack; + + // Lengths of preceding data blocks, one length of one last block + // for every filter. Used to reduce the size required to write + // the data block length if lengths are repeating. + Array<int> OldFilterLengths; + + int LastFilter; +/***************************** Unpack v 3.0 *********************************/ + public: Unpack(ComprDataIO *DataIO); ~Unpack(); - void Init(byte *Window=NULL); - void DoUnpack(int Method,bool Solid); + void Init(size_t WinSize,bool Solid); + void DoUnpack(uint Method,bool Solid); bool IsFileExtracted() {return(FileExtracted);} - void SetDestSize(Int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} + void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} void SetSuspended(bool Suspended) {Unpack::Suspended=Suspended;} - unsigned int GetChar() +#ifdef RAR_SMP + void SetThreads(uint Threads); + void UnpackDecode(UnpackThreadData &D); +#endif + + size_t MaxWinSize; + size_t MaxWinMask; + + uint GetChar() { - if (InAddr>BitInput::MAX_SIZE-30) + if (Inp.InAddr>BitInput::MAX_SIZE-30) + { UnpReadBuf(); - return(InBuf[InAddr++]); + if (Inp.InAddr>=BitInput::MAX_SIZE) // If nothing was read. + return 0; + } + return Inp.InBuf[Inp.InAddr++]; } }; diff --git a/unrar/unrar/unpack15.cpp b/unrar/unrar/unpack15.cpp index 170085b..1e7cf76 100644 --- a/unrar/unrar/unpack15.cpp +++ b/unrar/unrar/unpack15.cpp @@ -39,22 +39,17 @@ static unsigned int PosHf4[]={0,0,0,0,0,0,0,0,0,255,0,0,0}; void Unpack::Unpack15(bool Solid) { - if (Suspended) - UnpPtr=WrPtr; - else + UnpInitData(Solid); + UnpInitData15(Solid); + UnpReadBuf(); + if (!Solid) { - UnpInitData(Solid); - OldUnpInitData(Solid); - UnpReadBuf(); - if (!Solid) - { - InitHuff(); - UnpPtr=0; - } - else - UnpPtr=WrPtr; - --DestUnpSize; + InitHuff(); + UnpPtr=0; } + else + UnpPtr=WrPtr; + --DestUnpSize; if (DestUnpSize>=0) { GetFlagsBuf(); @@ -63,16 +58,12 @@ void Unpack::Unpack15(bool Solid) while (DestUnpSize>=0) { - UnpPtr&=MAXWINMASK; + UnpPtr&=MaxWinMask; - if (InAddr>ReadTop-30 && !UnpReadBuf()) + if (Inp.InAddr>ReadTop-30 && !UnpReadBuf()) break; - if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr) - { - OldUnpWriteBuf(); - if (Suspended) - return; - } + if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) + UnpWriteBuf20(); if (StMode) { HuffDecode(); @@ -116,23 +107,7 @@ void Unpack::Unpack15(bool Solid) } } } - OldUnpWriteBuf(); -} - - -void Unpack::OldUnpWriteBuf() -{ - if (UnpPtr!=WrPtr) - UnpSomeRead=true; - if (UnpPtr<WrPtr) - { - UnpIO->UnpWrite(&Window[WrPtr],-WrPtr & MAXWINMASK); - UnpIO->UnpWrite(Window,UnpPtr); - UnpAllBuf=true; - } - else - UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); - WrPtr=UnpPtr; + UnpWriteBuf20(); } @@ -155,13 +130,13 @@ void Unpack::ShortLZ() int DistancePlace; NumHuf=0; - unsigned int BitField=fgetbits(); + unsigned int BitField=Inp.fgetbits(); if (LCount==2) { - faddbits(1); + Inp.faddbits(1); if (BitField >= 0x8000) { - OldCopyString((unsigned int)LastDist,LastLength); + CopyString15((unsigned int)LastDist,LastLength); return; } BitField <<= 1; @@ -178,14 +153,14 @@ void Unpack::ShortLZ() for (Length=0;;Length++) if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0) break; - faddbits(GetShortLen1(Length)); + Inp.faddbits(GetShortLen1(Length)); } else { for (Length=0;;Length++) if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0) break; - faddbits(GetShortLen2(Length)); + Inp.faddbits(GetShortLen2(Length)); } if (Length >= 9) @@ -193,25 +168,25 @@ void Unpack::ShortLZ() if (Length == 9) { LCount++; - OldCopyString((unsigned int)LastDist,LastLength); + CopyString15((unsigned int)LastDist,LastLength); return; } if (Length == 14) { LCount=0; - Length=DecodeNum(fgetbits(),STARTL2,DecL2,PosL2)+5; - Distance=(fgetbits()>>1) | 0x8000; - faddbits(15); + Length=DecodeNum(Inp.fgetbits(),STARTL2,DecL2,PosL2)+5; + Distance=(Inp.fgetbits()>>1) | 0x8000; + Inp.faddbits(15); LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); return; } LCount=0; SaveLength=Length; Distance=OldDist[(OldDistPtr-(Length-9)) & 3]; - Length=DecodeNum(fgetbits(),STARTL1,DecL1,PosL1)+2; + Length=DecodeNum(Inp.fgetbits(),STARTL1,DecL1,PosL1)+2; if (Length==0x101 && SaveLength==10) { Buf60 ^= 1; @@ -226,7 +201,7 @@ void Unpack::ShortLZ() OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); return; } @@ -234,13 +209,11 @@ void Unpack::ShortLZ() AvrLn1 += Length; AvrLn1 -= AvrLn1 >> 4; - DistancePlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; + DistancePlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; Distance=ChSetA[DistancePlace]; if (--DistancePlace != -1) { - PlaceA[Distance]--; LastDistance=ChSetA[DistancePlace]; - PlaceA[LastDistance]++; ChSetA[DistancePlace+1]=LastDistance; ChSetA[DistancePlace]=Distance; } @@ -249,7 +222,7 @@ void Unpack::ShortLZ() OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); } @@ -269,7 +242,7 @@ void Unpack::LongLZ() } OldAvr2=AvrLn2; - unsigned int BitField=fgetbits(); + unsigned int BitField=Inp.fgetbits(); if (AvrLn2 >= 122) Length=DecodeNum(BitField,STARTL2,DecL2,PosL2); else @@ -279,19 +252,19 @@ void Unpack::LongLZ() if (BitField < 0x100) { Length=BitField; - faddbits(16); + Inp.faddbits(16); } else { for (Length=0;((BitField<<Length)&0x8000)==0;Length++) ; - faddbits(Length+1); + Inp.faddbits(Length+1); } AvrLn2 += Length; AvrLn2 -= AvrLn2 >> 5; - BitField=fgetbits(); + BitField=Inp.fgetbits(); if (AvrPlcB > 0x28ff) DistancePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); else @@ -312,11 +285,11 @@ void Unpack::LongLZ() break; } - ChSetB[DistancePlace]=ChSetB[NewDistancePlace]; + ChSetB[DistancePlace & 0xff]=ChSetB[NewDistancePlace]; ChSetB[NewDistancePlace]=Distance; - Distance=((Distance & 0xff00) | (fgetbits() >> 8)) >> 1; - faddbits(7); + Distance=((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; + Inp.faddbits(7); OldAvr3=AvrLn3; if (Length!=1 && Length!=4) @@ -341,7 +314,7 @@ void Unpack::LongLZ() OldDistPtr = OldDistPtr & 3; LastLength=Length; LastDist=Distance; - OldCopyString(Distance,Length); + CopyString15(Distance,Length); } @@ -352,7 +325,7 @@ void Unpack::HuffDecode() unsigned int Distance; int BytePlace; - unsigned int BitField=fgetbits(); + unsigned int BitField=Inp.fgetbits(); if (AvrPlc > 0x75ff) BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); @@ -374,8 +347,8 @@ void Unpack::HuffDecode() BytePlace=0x100; if (--BytePlace==-1) { - BitField=fgetbits(); - faddbits(1); + BitField=Inp.fgetbits(); + Inp.faddbits(1); if (BitField & 0x8000) { NumHuf=StMode=0; @@ -384,11 +357,11 @@ void Unpack::HuffDecode() else { Length = (BitField & 0x4000) ? 4 : 3; - faddbits(1); - Distance=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2); - Distance = (Distance << 5) | (fgetbits() >> 11); - faddbits(5); - OldCopyString(Distance,Length); + Inp.faddbits(1); + Distance=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); + Distance = (Distance << 5) | (Inp.fgetbits() >> 11); + Inp.faddbits(5); + CopyString15(Distance,Length); return; } } @@ -426,7 +399,15 @@ void Unpack::HuffDecode() void Unpack::GetFlagsBuf() { unsigned int Flags,NewFlagsPlace; - unsigned int FlagsPlace=DecodeNum(fgetbits(),STARTHF2,DecHf2,PosHf2); + unsigned int FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); + + // Our Huffman table stores 257 items and needs all them in other parts + // of code such as when StMode is on, so the first item is control item. + // While normally we do not use the last item to code the flags byte here, + // we need to check for value 256 when unpacking in case we unpack + // a corrupt archive. + if (FlagsPlace>=sizeof(ChSetC)/sizeof(ChSetC[0])) + return; while (1) { @@ -443,7 +424,7 @@ void Unpack::GetFlagsBuf() } -void Unpack::OldUnpInitData(int Solid) +void Unpack::UnpInitData15(int Solid) { if (!Solid) { @@ -464,8 +445,6 @@ void Unpack::InitHuff() { for (unsigned int I=0;I<256;I++) { - Place[I]=PlaceA[I]=PlaceB[I]=I; - PlaceC[I]=(~I+1) & 0xff; ChSet[I]=ChSetB[I]=I<<8; ChSetA[I]=I; ChSetC[I]=((~I+1) & 0xff)<<8; @@ -477,7 +456,7 @@ void Unpack::InitHuff() } -void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace) +void Unpack::CorrHuff(ushort *CharSet,byte *NumToPlace) { int I,J; for (I=7;I>=0;I--) @@ -489,23 +468,22 @@ void Unpack::CorrHuff(unsigned int *CharSet,unsigned int *NumToPlace) } -void Unpack::OldCopyString(unsigned int Distance,unsigned int Length) +void Unpack::CopyString15(uint Distance,uint Length) { DestUnpSize-=Length; while (Length--) { - Window[UnpPtr]=Window[(UnpPtr-Distance) & MAXWINMASK]; - UnpPtr=(UnpPtr+1) & MAXWINMASK; + Window[UnpPtr]=Window[(UnpPtr-Distance) & MaxWinMask]; + UnpPtr=(UnpPtr+1) & MaxWinMask; } } -unsigned int Unpack::DecodeNum(int Num,unsigned int StartPos, - unsigned int *DecTab,unsigned int *PosTab) +uint Unpack::DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab) { int I; for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++) StartPos++; - faddbits(StartPos); + Inp.faddbits(StartPos); return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]); } diff --git a/unrar/unrar/unpack20.cpp b/unrar/unrar/unpack20.cpp index ce0ab1f..93c8ba0 100644 --- a/unrar/unrar/unpack20.cpp +++ b/unrar/unrar/unpack20.cpp @@ -1,28 +1,12 @@ #include "rar.hpp" -void Unpack::CopyString20(unsigned int Length,unsigned int Distance) +void Unpack::CopyString20(uint Length,uint Distance) { - LastDist=OldDist[OldDistPtr++ & 3]=Distance; + LastDist=OldDist[OldDistPtr++]=Distance; + OldDistPtr = OldDistPtr & 3; // Needed if RAR 1.5 file is called after RAR 2.0. LastLength=Length; DestUnpSize-=Length; - - unsigned int DestPtr=UnpPtr-Distance; - if (DestPtr<MAXWINSIZE-300 && UnpPtr<MAXWINSIZE-300) - { - Window[UnpPtr++]=Window[DestPtr++]; - Window[UnpPtr++]=Window[DestPtr++]; - while (Length>2) - { - Length--; - Window[UnpPtr++]=Window[DestPtr++]; - } - } - else - while (Length--) - { - Window[UnpPtr]=Window[DestPtr++ & MAXWINMASK]; - UnpPtr=(UnpPtr+1) & MAXWINMASK; - } + CopyString(Length,Distance); } @@ -30,11 +14,11 @@ void Unpack::Unpack20(bool Solid) { static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - static int DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; + static uint DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; static unsigned char DBits[]= {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; - unsigned int Bits; + uint Bits; if (Suspended) UnpPtr=WrPtr; @@ -43,28 +27,27 @@ void Unpack::Unpack20(bool Solid) UnpInitData(Solid); if (!UnpReadBuf()) return; - if (!Solid) - if (!ReadTables20()) - return; + if ((!Solid || !TablesRead2) && !ReadTables20()) + return; --DestUnpSize; } - while (is64plus(DestUnpSize)) + while (DestUnpSize>=0) { - UnpPtr&=MAXWINMASK; + UnpPtr&=MaxWinMask; - if (InAddr>ReadTop-30) + if (Inp.InAddr>ReadTop-30) if (!UnpReadBuf()) break; - if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr) + if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr) { - OldUnpWriteBuf(); + UnpWriteBuf20(); if (Suspended) return; } if (UnpAudioBlock) { - int AudioNumber=DecodeNumber((struct Decode *)&MD[UnpCurChannel]); + uint AudioNumber=DecodeNumber(Inp,&MD[UnpCurChannel]); if (AudioNumber==256) { @@ -72,14 +55,14 @@ void Unpack::Unpack20(bool Solid) break; continue; } - Window[UnpPtr++]=DecodeAudio(AudioNumber); + Window[UnpPtr++]=DecodeAudio((int)AudioNumber); if (++UnpCurChannel==UnpChannels) UnpCurChannel=0; --DestUnpSize; continue; } - int Number=DecodeNumber((struct Decode *)&LD); + uint Number=DecodeNumber(Inp,&BlockTables.LD); if (Number<256) { Window[UnpPtr++]=(byte)Number; @@ -88,19 +71,19 @@ void Unpack::Unpack20(bool Solid) } if (Number>269) { - int Length=LDecode[Number-=270]+3; + uint Length=LDecode[Number-=270]+3; if ((Bits=LBits[Number])>0) { - Length+=getbits()>>(16-Bits); - addbits(Bits); + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } - int DistNumber=DecodeNumber((struct Decode *)&DD); - unsigned int Distance=DDecode[DistNumber]+1; + uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { - Distance+=getbits()>>(16-Bits); - addbits(Bits); + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } if (Distance>=0x2000) @@ -126,13 +109,13 @@ void Unpack::Unpack20(bool Solid) } if (Number<261) { - unsigned int Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; - int LengthNumber=DecodeNumber((struct Decode *)&RD); - int Length=LDecode[LengthNumber]+2; + uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; + uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + uint Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { - Length+=getbits()>>(16-Bits); - addbits(Bits); + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } if (Distance>=0x101) { @@ -149,60 +132,75 @@ void Unpack::Unpack20(bool Solid) } if (Number<270) { - unsigned int Distance=SDDecode[Number-=261]+1; + uint Distance=SDDecode[Number-=261]+1; if ((Bits=SDBits[Number])>0) { - Distance+=getbits()>>(16-Bits); - addbits(Bits); + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); } CopyString20(2,Distance); continue; } } ReadLastTables(); - OldUnpWriteBuf(); + UnpWriteBuf20(); +} + + +void Unpack::UnpWriteBuf20() +{ + if (UnpPtr!=WrPtr) + UnpSomeRead=true; + if (UnpPtr<WrPtr) + { + UnpIO->UnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask); + UnpIO->UnpWrite(Window,UnpPtr); + UnpAllBuf=true; + } + else + UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); + WrPtr=UnpPtr; } bool Unpack::ReadTables20() { byte BitLength[BC20]; - unsigned char Table[MC20*4]; - int TableSize,N,I; - if (InAddr>ReadTop-25) + byte Table[MC20*4]; + if (Inp.InAddr>ReadTop-25) if (!UnpReadBuf()) - return(false); - unsigned int BitField=getbits(); - UnpAudioBlock=(BitField & 0x8000); + return false; + uint BitField=Inp.getbits(); + UnpAudioBlock=(BitField & 0x8000)!=0; if (!(BitField & 0x4000)) memset(UnpOldTable20,0,sizeof(UnpOldTable20)); - addbits(2); + Inp.addbits(2); + uint TableSize; if (UnpAudioBlock) { UnpChannels=((BitField>>12) & 3)+1; if (UnpCurChannel>=UnpChannels) UnpCurChannel=0; - addbits(2); + Inp.addbits(2); TableSize=MC20*UnpChannels; } else TableSize=NC20+DC20+RC20; - for (I=0;I<BC20;I++) + for (uint I=0;I<BC20;I++) { - BitLength[I]=(byte)(getbits() >> 12); - addbits(4); + BitLength[I]=(byte)(Inp.getbits() >> 12); + Inp.addbits(4); } - MakeDecodeTables(BitLength,(struct Decode *)&BD,BC20); - I=0; - while (I<TableSize) + MakeDecodeTables(BitLength,&BlockTables.BD,BC20); + for (uint I=0;I<TableSize;) { - if (InAddr>ReadTop-5) + if (Inp.InAddr>ReadTop-5) if (!UnpReadBuf()) - return(false); - int Number=DecodeNumber((struct Decode *)&BD); + return false; + uint Number=DecodeNumber(Inp,&BlockTables.BD); if (Number<16) { Table[I]=(Number+UnpOldTable20[I]) & 0xf; @@ -211,56 +209,61 @@ bool Unpack::ReadTables20() else if (Number==16) { - N=(getbits() >> 14)+3; - addbits(2); - while (N-- > 0 && I<TableSize) - { - Table[I]=Table[I-1]; - I++; - } + uint N=(Inp.getbits() >> 14)+3; + Inp.addbits(2); + if (I==0) + return false; // We cannot have "repeat previous" code at the first position. + else + while (N-- > 0 && I<TableSize) + { + Table[I]=Table[I-1]; + I++; + } } else { + uint N; if (Number==17) { - N=(getbits() >> 13)+3; - addbits(3); + N=(Inp.getbits() >> 13)+3; + Inp.addbits(3); } else { - N=(getbits() >> 9)+11; - addbits(7); + N=(Inp.getbits() >> 9)+11; + Inp.addbits(7); } while (N-- > 0 && I<TableSize) Table[I++]=0; } } - if (InAddr>ReadTop) - return(true); + TablesRead2=true; + if (Inp.InAddr>ReadTop) + return true; if (UnpAudioBlock) - for (I=0;I<UnpChannels;I++) - MakeDecodeTables(&Table[I*MC20],(struct Decode *)&MD[I],MC20); + for (uint I=0;I<UnpChannels;I++) + MakeDecodeTables(&Table[I*MC20],&MD[I],MC20); else { - MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC20); - MakeDecodeTables(&Table[NC20],(struct Decode *)&DD,DC20); - MakeDecodeTables(&Table[NC20+DC20],(struct Decode *)&RD,RC20); + MakeDecodeTables(&Table[0],&BlockTables.LD,NC20); + MakeDecodeTables(&Table[NC20],&BlockTables.DD,DC20); + MakeDecodeTables(&Table[NC20+DC20],&BlockTables.RD,RC20); } - memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); - return(true); + memcpy(UnpOldTable20,Table,TableSize); + return true; } void Unpack::ReadLastTables() { - if (ReadTop>=InAddr+5) + if (ReadTop>=Inp.InAddr+5) if (UnpAudioBlock) { - if (DecodeNumber((struct Decode *)&MD[UnpCurChannel])==256) + if (DecodeNumber(Inp,&MD[UnpCurChannel])==256) ReadTables20(); } else - if (DecodeNumber((struct Decode *)&LD)==269) + if (DecodeNumber(Inp,&BlockTables.LD)==269) ReadTables20(); } @@ -269,7 +272,10 @@ void Unpack::UnpInitData20(int Solid) { if (!Solid) { - UnpAudioBlock=UnpChannelDelta=UnpCurChannel=0; + TablesRead2=false; + UnpAudioBlock=false; + UnpChannelDelta=0; + UnpCurChannel=0; UnpChannels=1; memset(AudV,0,sizeof(AudV)); @@ -290,9 +296,12 @@ byte Unpack::DecodeAudio(int Delta) int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta; PCh=(PCh>>3) & 0xFF; - unsigned int Ch=PCh-Delta; + uint Ch=PCh-Delta; - int D=((signed char)Delta)<<3; + int D=(signed char)Delta; + // Left shift of negative value is undefined behavior in C++, + // so we cast it to unsigned to follow the standard. + D=(uint)D<<3; V->Dif[0]+=abs(D); V->Dif[1]+=abs(D-V->D1); @@ -311,9 +320,9 @@ byte Unpack::DecodeAudio(int Delta) if ((V->ByteCount & 0x1F)==0) { - unsigned int MinDif=V->Dif[0],NumMinDif=0; + uint MinDif=V->Dif[0],NumMinDif=0; V->Dif[0]=0; - for (int I=1;I<sizeof(V->Dif)/sizeof(V->Dif[0]);I++) + for (uint I=1;I<ASIZE(V->Dif);I++) { if (V->Dif[I]<MinDif) { @@ -366,5 +375,5 @@ byte Unpack::DecodeAudio(int Delta) break; } } - return((byte)Ch); + return (byte)Ch; } diff --git a/unrar/unrar/unpack30.cpp b/unrar/unrar/unpack30.cpp new file mode 100644 index 0000000..7c2adfa --- /dev/null +++ b/unrar/unrar/unpack30.cpp @@ -0,0 +1,765 @@ +// We use it instead of direct PPM.DecodeChar call to be sure that +// we reset PPM structures in case of corrupt data. It is important, +// because these structures can be invalid after PPM.DecodeChar returned -1. +inline int Unpack::SafePPMDecodeChar() +{ + int Ch=PPM.DecodeChar(); + if (Ch==-1) // Corrupt PPM data found. + { + PPM.CleanUp(); // Reset possibly corrupt PPM data structures. + UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. + } + return(Ch); +} + + +void Unpack::Unpack29(bool Solid) +{ + static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; + static unsigned char LBits[]= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; + static int DDecode[DC]; + static byte DBits[DC]; + static int DBitLengthCounts[]= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; + static unsigned char SDDecode[]={0,4,8,16,32,64,128,192}; + static unsigned char SDBits[]= {2,2,3, 4, 5, 6, 6, 6}; + unsigned int Bits; + + if (DDecode[1]==0) + { + int Dist=0,BitLength=0,Slot=0; + for (int I=0;I<ASIZE(DBitLengthCounts);I++,BitLength++) + for (int J=0;J<DBitLengthCounts[I];J++,Slot++,Dist+=(1<<BitLength)) + { + DDecode[Slot]=Dist; + DBits[Slot]=BitLength; + } + } + + FileExtracted=true; + + if (!Suspended) + { + UnpInitData(Solid); + if (!UnpReadBuf30()) + return; + if ((!Solid || !TablesRead3) && !ReadTables30()) + return; + } + + while (true) + { + UnpPtr&=MaxWinMask; + + if (Inp.InAddr>ReadBorder) + { + if (!UnpReadBuf30()) + break; + } + if (((WrPtr-UnpPtr) & MaxWinMask)<=MAX3_INC_LZ_MATCH && WrPtr!=UnpPtr) + { + UnpWriteBuf30(); + if (WrittenFileSize>DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + if (UnpBlockType==BLOCK_PPM) + { + // Here speed is critical, so we do not use SafePPMDecodeChar, + // because sometimes even the inline function can introduce + // some additional penalty. + int Ch=PPM.DecodeChar(); + if (Ch==-1) // Corrupt PPM data found. + { + PPM.CleanUp(); // Reset possibly corrupt PPM data structures. + UnpBlockType=BLOCK_LZ; // Set faster and more fail proof LZ mode. + break; + } + if (Ch==PPMEscChar) + { + int NextCh=SafePPMDecodeChar(); + if (NextCh==0) // End of PPM encoding. + { + if (!ReadTables30()) + break; + continue; + } + if (NextCh==-1) // Corrupt PPM data found. + break; + if (NextCh==2) // End of file in PPM mode. + break; + if (NextCh==3) // Read VM code. + { + if (!ReadVMCodePPM()) + break; + continue; + } + if (NextCh==4) // LZ inside of PPM. + { + unsigned int Distance=0,Length; + bool Failed=false; + for (int I=0;I<4 && !Failed;I++) + { + int Ch=SafePPMDecodeChar(); + if (Ch==-1) + Failed=true; + else + if (I==3) + Length=(byte)Ch; + else + Distance=(Distance<<8)+(byte)Ch; + } + if (Failed) + break; + + CopyString(Length+32,Distance+2); + continue; + } + if (NextCh==5) // One byte distance match (RLE) inside of PPM. + { + int Length=SafePPMDecodeChar(); + if (Length==-1) + break; + CopyString(Length+4,1); + continue; + } + // If we are here, NextCh must be 1, what means that current byte + // is equal to our 'escape' byte, so we just store it to Window. + } + Window[UnpPtr++]=Ch; + continue; + } + + uint Number=DecodeNumber(Inp,&BlockTables.LD); + if (Number<256) + { + Window[UnpPtr++]=(byte)Number; + continue; + } + if (Number>=271) + { + uint Length=LDecode[Number-=271]+3; + if ((Bits=LBits[Number])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + + uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint Distance=DDecode[DistNumber]+1; + if ((Bits=DBits[DistNumber])>0) + { + if (DistNumber>9) + { + if (Bits>4) + { + Distance+=((Inp.getbits()>>(20-Bits))<<4); + Inp.addbits(Bits-4); + } + if (LowDistRepCount>0) + { + LowDistRepCount--; + Distance+=PrevLowDist; + } + else + { + uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + if (LowDist==16) + { + LowDistRepCount=LOW_DIST_REP_COUNT-1; + Distance+=PrevLowDist; + } + else + { + Distance+=LowDist; + PrevLowDist=LowDist; + } + } + } + else + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + } + + if (Distance>=0x2000) + { + Length++; + if (Distance>=0x40000) + Length++; + } + + InsertOldDist(Distance); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (Number==256) + { + if (!ReadEndOfBlock()) + break; + continue; + } + if (Number==257) + { + if (!ReadVMCode()) + break; + continue; + } + if (Number==258) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + continue; + } + if (Number<263) + { + uint DistNum=Number-259; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + int Length=LDecode[LengthNumber]+2; + if ((Bits=LBits[LengthNumber])>0) + { + Length+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (Number<272) + { + uint Distance=SDDecode[Number-=263]+1; + if ((Bits=SDBits[Number])>0) + { + Distance+=Inp.getbits()>>(16-Bits); + Inp.addbits(Bits); + } + InsertOldDist(Distance); + LastLength=2; + CopyString(2,Distance); + continue; + } + } + UnpWriteBuf30(); +} + + +// Return 'false' to quit unpacking the current file or 'true' to continue. +bool Unpack::ReadEndOfBlock() +{ + uint BitField=Inp.getbits(); + bool NewTable,NewFile=false; + + // "1" - no new file, new table just here. + // "00" - new file, no new table. + // "01" - new file, new table (in beginning of next file). + + if ((BitField & 0x8000)!=0) + { + NewTable=true; + Inp.addbits(1); + } + else + { + NewFile=true; + NewTable=(BitField & 0x4000)!=0; + Inp.addbits(2); + } + TablesRead3=!NewTable; + + // Quit immediately if "new file" flag is set. If "new table" flag + // is present, we'll read the table in beginning of next file + // based on 'TablesRead3' 'false' value. + if (NewFile) + return false; + return ReadTables30(); // Quit only if we failed to read tables. +} + + +bool Unpack::ReadVMCode() +{ + // Entire VM code is guaranteed to fully present in block defined + // by current Huffman table. Compressor checks that VM code does not cross + // Huffman block boundaries. + uint FirstByte=Inp.getbits()>>8; + Inp.addbits(8); + uint Length=(FirstByte & 7)+1; + if (Length==7) + { + Length=(Inp.getbits()>>8)+7; + Inp.addbits(8); + } + else + if (Length==8) + { + Length=Inp.getbits(); + Inp.addbits(16); + } + if (Length==0) + return false; + Array<byte> VMCode(Length); + for (uint I=0;I<Length;I++) + { + // Try to read the new buffer if only one byte is left. + // But if we read all bytes except the last, one byte is enough. + if (Inp.InAddr>=ReadTop-1 && !UnpReadBuf30() && I<Length-1) + return false; + VMCode[I]=Inp.getbits()>>8; + Inp.addbits(8); + } + return AddVMCode(FirstByte,&VMCode[0],Length); +} + + +bool Unpack::ReadVMCodePPM() +{ + uint FirstByte=SafePPMDecodeChar(); + if ((int)FirstByte==-1) + return false; + uint Length=(FirstByte & 7)+1; + if (Length==7) + { + int B1=SafePPMDecodeChar(); + if (B1==-1) + return false; + Length=B1+7; + } + else + if (Length==8) + { + int B1=SafePPMDecodeChar(); + if (B1==-1) + return false; + int B2=SafePPMDecodeChar(); + if (B2==-1) + return false; + Length=B1*256+B2; + } + if (Length==0) + return false; + Array<byte> VMCode(Length); + for (uint I=0;I<Length;I++) + { + int Ch=SafePPMDecodeChar(); + if (Ch==-1) + return false; + VMCode[I]=Ch; + } + return AddVMCode(FirstByte,&VMCode[0],Length); +} + + +bool Unpack::AddVMCode(uint FirstByte,byte *Code,uint CodeSize) +{ + VMCodeInp.InitBitInput(); + memcpy(VMCodeInp.InBuf,Code,Min(BitInput::MAX_SIZE,CodeSize)); + VM.Init(); + + uint FiltPos; + if ((FirstByte & 0x80)!=0) + { + FiltPos=RarVM::ReadData(VMCodeInp); + if (FiltPos==0) + InitFilters30(false); + else + FiltPos--; + } + else + FiltPos=LastFilter; // Use the same filter as last time. + + if (FiltPos>Filters30.Size() || FiltPos>OldFilterLengths.Size()) + return false; + LastFilter=FiltPos; + bool NewFilter=(FiltPos==Filters30.Size()); + + UnpackFilter30 *StackFilter=new UnpackFilter30; // New filter for PrgStack. + + UnpackFilter30 *Filter; + if (NewFilter) // New filter code, never used before since VM reset. + { + if (FiltPos>MAX3_UNPACK_FILTERS) + { + // Too many different filters, corrupt archive. + delete StackFilter; + return false; + } + + Filters30.Add(1); + Filters30[Filters30.Size()-1]=Filter=new UnpackFilter30; + StackFilter->ParentFilter=(uint)(Filters30.Size()-1); + + // Reserve one item to store the data block length of our new filter + // entry. We'll set it to real block length below, after reading it. + // But we need to initialize it now, because when processing corrupt + // data, we can access this item even before we set it to real value. + OldFilterLengths.Push(0); + } + else // Filter was used in the past. + { + Filter=Filters30[FiltPos]; + StackFilter->ParentFilter=FiltPos; + } + + uint EmptyCount=0; + for (uint I=0;I<PrgStack.Size();I++) + { + PrgStack[I-EmptyCount]=PrgStack[I]; + if (PrgStack[I]==NULL) + EmptyCount++; + if (EmptyCount>0) + PrgStack[I]=NULL; + } + if (EmptyCount==0) + { + if (PrgStack.Size()>MAX3_UNPACK_FILTERS) + { + delete StackFilter; + return false; + } + PrgStack.Add(1); + EmptyCount=1; + } + size_t StackPos=PrgStack.Size()-EmptyCount; + PrgStack[StackPos]=StackFilter; + + uint BlockStart=RarVM::ReadData(VMCodeInp); + if ((FirstByte & 0x40)!=0) + BlockStart+=258; + StackFilter->BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask); + if ((FirstByte & 0x20)!=0) + { + StackFilter->BlockLength=RarVM::ReadData(VMCodeInp); + + // Store the last data block length for current filter. + OldFilterLengths[FiltPos]=StackFilter->BlockLength; + } + else + { + // Set the data block size to same value as the previous block size + // for same filter. It is possible for corrupt data to access a new + // and not filled yet item of OldFilterLengths array here. This is why + // we set new OldFilterLengths items to zero above. + StackFilter->BlockLength=FiltPos<OldFilterLengths.Size() ? OldFilterLengths[FiltPos]:0; + } + + StackFilter->NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; + +// DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); + + memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); + StackFilter->Prg.InitR[4]=StackFilter->BlockLength; + + if ((FirstByte & 0x10)!=0) // Set registers to optional parameters if any. + { + uint InitMask=VMCodeInp.fgetbits()>>9; + VMCodeInp.faddbits(7); + for (uint I=0;I<7;I++) + if (InitMask & (1<<I)) + StackFilter->Prg.InitR[I]=RarVM::ReadData(VMCodeInp); + } + + if (NewFilter) + { + uint VMCodeSize=RarVM::ReadData(VMCodeInp); + if (VMCodeSize>=0x10000 || VMCodeSize==0 || VMCodeInp.InAddr+VMCodeSize>CodeSize) + return false; + Array<byte> VMCode(VMCodeSize); + for (uint I=0;I<VMCodeSize;I++) + { + if (VMCodeInp.Overflow(3)) + return false; + VMCode[I]=VMCodeInp.fgetbits()>>8; + VMCodeInp.faddbits(8); + } + VM.Prepare(&VMCode[0],VMCodeSize,&Filter->Prg); + } + StackFilter->Prg.Type=Filter->Prg.Type; + + return true; +} + + +bool Unpack::UnpReadBuf30() +{ + int DataSize=ReadTop-Inp.InAddr; // Data left to process. + if (DataSize<0) + return false; + if (Inp.InAddr>BitInput::MAX_SIZE/2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize>0) + memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Inp.InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); + if (ReadCode>0) + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + return ReadCode!=-1; +} + + +void Unpack::UnpWriteBuf30() +{ + uint WrittenBorder=(uint)WrPtr; + uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); + for (size_t I=0;I<PrgStack.Size();I++) + { + // Here we apply filters to data which we need to write. + // We always copy data to virtual machine memory before processing. + // We cannot process them just in place in Window buffer, because + // these data can be used for future string matches, so we must + // preserve them in original form. + + UnpackFilter30 *flt=PrgStack[I]; + if (flt==NULL) + continue; + if (flt->NextWindow) + { + flt->NextWindow=false; + continue; + } + unsigned int BlockStart=flt->BlockStart; + unsigned int BlockLength=flt->BlockLength; + if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSize) + { + if (WrittenBorder!=BlockStart) + { + UnpWriteArea(WrittenBorder,BlockStart); + WrittenBorder=BlockStart; + WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); + } + if (BlockLength<=WriteSize) + { + uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; + if (BlockStart<BlockEnd || BlockEnd==0) + VM.SetMemory(0,Window+BlockStart,BlockLength); + else + { + uint FirstPartLength=uint(MaxWinSize-BlockStart); + VM.SetMemory(0,Window+BlockStart,FirstPartLength); + VM.SetMemory(FirstPartLength,Window,BlockEnd); + } + + VM_PreparedProgram *ParentPrg=&Filters30[flt->ParentFilter]->Prg; + VM_PreparedProgram *Prg=&flt->Prg; + + ExecuteCode(Prg); + + byte *FilteredData=Prg->FilteredData; + unsigned int FilteredDataSize=Prg->FilteredDataSize; + + delete PrgStack[I]; + PrgStack[I]=NULL; + while (I+1<PrgStack.Size()) + { + UnpackFilter30 *NextFilter=PrgStack[I+1]; + // It is required to check NextWindow here. + if (NextFilter==NULL || NextFilter->BlockStart!=BlockStart || + NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) + break; + + // Apply several filters to same data block. + + VM.SetMemory(0,FilteredData,FilteredDataSize); + + VM_PreparedProgram *ParentPrg=&Filters30[NextFilter->ParentFilter]->Prg; + VM_PreparedProgram *NextPrg=&NextFilter->Prg; + + ExecuteCode(NextPrg); + + FilteredData=NextPrg->FilteredData; + FilteredDataSize=NextPrg->FilteredDataSize; + I++; + delete PrgStack[I]; + PrgStack[I]=NULL; + } + UnpIO->UnpWrite(FilteredData,FilteredDataSize); + UnpSomeRead=true; + WrittenFileSize+=FilteredDataSize; + WrittenBorder=BlockEnd; + WriteSize=uint((UnpPtr-WrittenBorder)&MaxWinMask); + } + else + { + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + for (size_t J=I;J<PrgStack.Size();J++) + { + UnpackFilter30 *flt=PrgStack[J]; + if (flt!=NULL && flt->NextWindow) + flt->NextWindow=false; + } + WrPtr=WrittenBorder; + return; + } + } + } + + UnpWriteArea(WrittenBorder,UnpPtr); + WrPtr=UnpPtr; +} + + +void Unpack::ExecuteCode(VM_PreparedProgram *Prg) +{ + Prg->InitR[6]=(uint)WrittenFileSize; + VM.Execute(Prg); +} + + +bool Unpack::ReadTables30() +{ + byte BitLength[BC]; + byte Table[HUFF_TABLE_SIZE30]; + if (Inp.InAddr>ReadTop-25) + if (!UnpReadBuf30()) + return(false); + Inp.faddbits((8-Inp.InBit)&7); + uint BitField=Inp.fgetbits(); + if (BitField & 0x8000) + { + UnpBlockType=BLOCK_PPM; + return(PPM.DecodeInit(this,PPMEscChar)); + } + UnpBlockType=BLOCK_LZ; + + PrevLowDist=0; + LowDistRepCount=0; + + if (!(BitField & 0x4000)) + memset(UnpOldTable,0,sizeof(UnpOldTable)); + Inp.faddbits(2); + + for (uint I=0;I<BC;I++) + { + uint Length=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (Length==15) + { + uint ZeroCount=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (ZeroCount==0) + BitLength[I]=15; + else + { + ZeroCount+=2; + while (ZeroCount-- > 0 && I<ASIZE(BitLength)) + BitLength[I++]=0; + I--; + } + } + else + BitLength[I]=Length; + } + MakeDecodeTables(BitLength,&BlockTables.BD,BC30); + + const uint TableSize=HUFF_TABLE_SIZE30; + for (uint I=0;I<TableSize;) + { + if (Inp.InAddr>ReadTop-5) + if (!UnpReadBuf30()) + return(false); + uint Number=DecodeNumber(Inp,&BlockTables.BD); + if (Number<16) + { + Table[I]=(Number+UnpOldTable[I]) & 0xf; + I++; + } + else + if (Number<18) + { + uint N; + if (Number==16) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + if (I==0) + return false; // We cannot have "repeat previous" code at the first position. + else + while (N-- > 0 && I<TableSize) + { + Table[I]=Table[I-1]; + I++; + } + } + else + { + uint N; + if (Number==18) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + while (N-- > 0 && I<TableSize) + Table[I++]=0; + } + } + TablesRead3=true; + if (Inp.InAddr>ReadTop) + return false; + MakeDecodeTables(&Table[0],&BlockTables.LD,NC30); + MakeDecodeTables(&Table[NC30],&BlockTables.DD,DC30); + MakeDecodeTables(&Table[NC30+DC30],&BlockTables.LDD,LDC30); + MakeDecodeTables(&Table[NC30+DC30+LDC30],&BlockTables.RD,RC30); + memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); + return true; +} + + +void Unpack::UnpInitData30(bool Solid) +{ + if (!Solid) + { + TablesRead3=false; + memset(UnpOldTable,0,sizeof(UnpOldTable)); + PPMEscChar=2; + UnpBlockType=BLOCK_LZ; + } + InitFilters30(Solid); +} + + +void Unpack::InitFilters30(bool Solid) +{ + if (!Solid) + { + OldFilterLengths.SoftReset(); + LastFilter=0; + + for (size_t I=0;I<Filters30.Size();I++) + delete Filters30[I]; + Filters30.SoftReset(); + } + for (size_t I=0;I<PrgStack.Size();I++) + delete PrgStack[I]; + PrgStack.SoftReset(); +} diff --git a/unrar/unrar/unpack50.cpp b/unrar/unrar/unpack50.cpp new file mode 100644 index 0000000..e040907 --- /dev/null +++ b/unrar/unrar/unpack50.cpp @@ -0,0 +1,687 @@ +void Unpack::Unpack5(bool Solid) +{ + FileExtracted=true; + + if (!Suspended) + { + UnpInitData(Solid); + if (!UnpReadBuf()) + return; + + // Check TablesRead5 to be sure that we read tables at least once + // regardless of current block header TablePresent flag. + // So we can safefly use these tables below. + if (!ReadBlockHeader(Inp,BlockHeader) || + !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5) + return; + } + + while (true) + { + UnpPtr&=MaxWinMask; + + if (Inp.InAddr>=ReadBorder) + { + bool FileDone=false; + + // We use 'while', because for empty block containing only Huffman table, + // we'll be on the block border once again just after reading the table. + while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 || + Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 && + Inp.InBit>=BlockHeader.BlockBitSize) + { + if (BlockHeader.LastBlockInFile) + { + FileDone=true; + break; + } + if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables)) + return; + } + if (FileDone || !UnpReadBuf()) + break; + } + + if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + { + UnpWriteBuf(); + if (WrittenFileSize>DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + + uint MainSlot=DecodeNumber(Inp,&BlockTables.LD); + if (MainSlot<256) + { + if (Fragmented) + FragWindow[UnpPtr++]=(byte)MainSlot; + else + Window[UnpPtr++]=(byte)MainSlot; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((Inp.getbits32()>>(36-DBits))<<4); + Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=Inp.getbits()>>(16-DBits); + Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + InsertOldDist(Distance); + LastLength=Length; + if (Fragmented) + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + else + CopyString(Length,Distance); + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) + break; + continue; + } + if (MainSlot==257) + { + if (LastLength!=0) + if (Fragmented) + FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); + else + CopyString(LastLength,OldDist[0]); + continue; + } + if (MainSlot<262) + { + uint DistNum=MainSlot-258; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD); + uint Length=SlotToLength(Inp,LengthSlot); + LastLength=Length; + if (Fragmented) + FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); + else + CopyString(Length,Distance); + continue; + } + } + UnpWriteBuf(); +} + + +uint Unpack::ReadFilterData(BitInput &Inp) +{ + uint ByteCount=(Inp.fgetbits()>>14)+1; + Inp.addbits(2); + + uint Data=0; + for (uint I=0;I<ByteCount;I++) + { + Data+=(Inp.fgetbits()>>8)<<(I*8); + Inp.addbits(8); + } + return Data; +} + + +bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter) +{ + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) + if (!UnpReadBuf()) + return false; + + Filter.BlockStart=ReadFilterData(Inp); + Filter.BlockLength=ReadFilterData(Inp); + if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) + Filter.BlockLength=0; + + Filter.Type=Inp.fgetbits()>>13; + Inp.faddbits(3); + + if (Filter.Type==FILTER_DELTA) + { + Filter.Channels=(Inp.fgetbits()>>11)+1; + Inp.faddbits(5); + } + + return true; +} + + +bool Unpack::AddFilter(UnpackFilter &Filter) +{ + if (Filters.Size()>=MAX_UNPACK_FILTERS) + { + UnpWriteBuf(); // Write data, apply and flush filters. + if (Filters.Size()>=MAX_UNPACK_FILTERS) + InitFilters(); // Still too many filters, prevent excessive memory use. + } + + // If distance to filter start is that large that due to circular dictionary + // mode now it points to old not written yet data, then we set 'NextWindow' + // flag and process this filter only after processing that older data. + Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; + + Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); + Filters.Push(Filter); + return true; +} + + +bool Unpack::UnpReadBuf() +{ + int DataSize=ReadTop-Inp.InAddr; // Data left to process. + if (DataSize<0) + return false; + BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; + if (Inp.InAddr>BitInput::MAX_SIZE/2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize>0) + memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Inp.InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=0; + if (BitInput::MAX_SIZE!=DataSize) + ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); + if (ReadCode>0) // Can be also -1. + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + BlockHeader.BlockStart=Inp.InAddr; + if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet. + { + // We may need to quit from main extraction loop and read new block header + // and trees earlier than data in input buffer ends. + ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); + } + return ReadCode!=-1; +} + + +void Unpack::UnpWriteBuf() +{ + size_t WrittenBorder=WrPtr; + size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; + size_t WriteSizeLeft=FullWriteSize; + bool NotAllFiltersProcessed=false; + for (size_t I=0;I<Filters.Size();I++) + { + // Here we apply filters to data which we need to write. + // We always copy data to another memory block before processing. + // We cannot process them just in place in Window buffer, because + // these data can be used for future string matches, so we must + // preserve them in original form. + + UnpackFilter *flt=&Filters[I]; + if (flt->Type==FILTER_NONE) + continue; + if (flt->NextWindow) + { + // Here we skip filters which have block start in current data range + // due to address wrap around in circular dictionary, but actually + // belong to next dictionary block. If such filter start position + // is included to current write range, then we reset 'NextWindow' flag. + // In fact we can reset it even without such check, because current + // implementation seems to guarantee 'NextWindow' flag reset after + // buffer writing for all existing filters. But let's keep this check + // just in case. Compressor guarantees that distance between + // filter block start and filter storing position cannot exceed + // the dictionary size. So if we covered the filter block start with + // our write here, we can safely assume that filter is applicable + // to next block on no further wrap arounds is possible. + if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) + flt->NextWindow=false; + continue; + } + uint BlockStart=flt->BlockStart; + uint BlockLength=flt->BlockLength; + if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSizeLeft) + { + if (WrittenBorder!=BlockStart) + { + UnpWriteArea(WrittenBorder,BlockStart); + WrittenBorder=BlockStart; + WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; + } + if (BlockLength<=WriteSizeLeft) + { + if (BlockLength>0) // We set it to 0 also for invalid filters. + { + uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; + + FilterSrcMemory.Alloc(BlockLength); + byte *Mem=&FilterSrcMemory[0]; + if (BlockStart<BlockEnd || BlockEnd==0) + { + if (Fragmented) + FragWindow.CopyData(Mem,BlockStart,BlockLength); + else + memcpy(Mem,Window+BlockStart,BlockLength); + } + else + { + size_t FirstPartLength=size_t(MaxWinSize-BlockStart); + if (Fragmented) + { + FragWindow.CopyData(Mem,BlockStart,FirstPartLength); + FragWindow.CopyData(Mem+FirstPartLength,0,BlockEnd); + } + else + { + memcpy(Mem,Window+BlockStart,FirstPartLength); + memcpy(Mem+FirstPartLength,Window,BlockEnd); + } + } + + byte *OutMem=ApplyFilter(Mem,BlockLength,flt); + + Filters[I].Type=FILTER_NONE; + + if (OutMem!=NULL) + UnpIO->UnpWrite(OutMem,BlockLength); + + UnpSomeRead=true; + WrittenFileSize+=BlockLength; + WrittenBorder=BlockEnd; + WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; + } + } + else + { + // Current filter intersects the window write border, so we adjust + // the window border to process this filter next time, not now. + WrPtr=WrittenBorder; + + // Since Filter start position can only increase, we quit processing + // all following filters for this data block and reset 'NextWindow' + // flag for them. + for (size_t J=I;J<Filters.Size();J++) + { + UnpackFilter *flt=&Filters[J]; + if (flt->Type!=FILTER_NONE) + flt->NextWindow=false; + } + + // Do not write data left after current filter now. + NotAllFiltersProcessed=true; + break; + } + } + } + + // Remove processed filters from queue. + size_t EmptyCount=0; + for (size_t I=0;I<Filters.Size();I++) + { + if (EmptyCount>0) + Filters[I-EmptyCount]=Filters[I]; + if (Filters[I].Type==FILTER_NONE) + EmptyCount++; + } + if (EmptyCount>0) + Filters.Alloc(Filters.Size()-EmptyCount); + + if (!NotAllFiltersProcessed) // Only if all filters are processed. + { + // Write data left after last filter. + UnpWriteArea(WrittenBorder,UnpPtr); + WrPtr=UnpPtr; + } + + // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE + // instead of potentially huge MaxWinSize blocks. It also allows us + // to keep the size of Filters array reasonable. + WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; + + // Choose the nearest among WriteBorder and WrPtr actual written border. + // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. + if (WriteBorder==UnpPtr || + WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) + WriteBorder=WrPtr; +} + + +byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) +{ + byte *SrcData=Data; + switch(Flt->Type) + { + case FILTER_E8: + case FILTER_E8E9: + { + uint FileOffset=(uint)WrittenFileSize; + + const uint FileSize=0x1000000; + byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8; + // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" + // to avoid overflow for DataSize<4. + for (uint CurPos=0;CurPos+4<DataSize;) + { + byte CurByte=*(Data++); + CurPos++; + if (CurByte==0xe8 || CurByte==CmpByte2) + { + uint Offset=(CurPos+FileOffset)%FileSize; + uint Addr=RawGet4(Data); + + // We check 0x80000000 bit instead of '< 0' comparison + // not assuming int32 presence or uint size and endianness. + if ((Addr & 0x80000000)!=0) // Addr<0 + { + if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0 + RawPut4(Addr+FileSize,Data); + } + else + if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize + RawPut4(Addr-Offset,Data); + + Data+=4; + CurPos+=4; + } + } + } + return SrcData; + case FILTER_ARM: + // 2019-11-15: we turned off ARM filter by default when compressing, + // mostly because it is inefficient for modern 64 bit ARM binaries. + // It was turned on by default in 5.0 - 5.80b3 , so we still need it + // here for compatibility with some of previously created archives. + { + uint FileOffset=(uint)WrittenFileSize; + // DataSize is unsigned, so we use "CurPos+3" and not "DataSize-3" + // to avoid overflow for DataSize<3. + for (uint CurPos=0;CurPos+3<DataSize;CurPos+=4) + { + byte *D=Data+CurPos; + if (D[3]==0xeb) // BL command with '1110' (Always) condition. + { + uint Offset=D[0]+uint(D[1])*0x100+uint(D[2])*0x10000; + Offset-=(FileOffset+CurPos)/4; + D[0]=(byte)Offset; + D[1]=(byte)(Offset>>8); + D[2]=(byte)(Offset>>16); + } + } + } + return SrcData; + case FILTER_DELTA: + { + // Unlike RAR3, we do not need to reject excessive channel + // values here, since RAR5 uses only 5 bits to store channel. + uint Channels=Flt->Channels,SrcPos=0; + + FilterDstMemory.Alloc(DataSize); + byte *DstData=&FilterDstMemory[0]; + + // Bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions. + for (uint CurChannel=0;CurChannel<Channels;CurChannel++) + { + byte PrevByte=0; + for (uint DestPos=CurChannel;DestPos<DataSize;DestPos+=Channels) + DstData[DestPos]=(PrevByte-=Data[SrcPos++]); + } + return DstData; + } + + } + return NULL; +} + + +void Unpack::UnpWriteArea(size_t StartPtr,size_t EndPtr) +{ + if (EndPtr!=StartPtr) + UnpSomeRead=true; + if (EndPtr<StartPtr) + UnpAllBuf=true; + + if (Fragmented) + { + size_t SizeToWrite=(EndPtr-StartPtr) & MaxWinMask; + while (SizeToWrite>0) + { + size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); + UnpWriteData(&FragWindow[StartPtr],BlockSize); + SizeToWrite-=BlockSize; + StartPtr=(StartPtr+BlockSize) & MaxWinMask; + } + } + else + if (EndPtr<StartPtr) + { + UnpWriteData(Window+StartPtr,MaxWinSize-StartPtr); + UnpWriteData(Window,EndPtr); + } + else + UnpWriteData(Window+StartPtr,EndPtr-StartPtr); +} + + +void Unpack::UnpWriteData(byte *Data,size_t Size) +{ + if (WrittenFileSize>=DestUnpSize) + return; + size_t WriteSize=Size; + int64 LeftToWrite=DestUnpSize-WrittenFileSize; + if ((int64)WriteSize>LeftToWrite) + WriteSize=(size_t)LeftToWrite; + UnpIO->UnpWrite(Data,WriteSize); + WrittenFileSize+=Size; +} + + +void Unpack::UnpInitData50(bool Solid) +{ + if (!Solid) + TablesRead5=false; +} + + +bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) +{ + Header.HeaderSize=0; + + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) + if (!UnpReadBuf()) + return false; + Inp.faddbits((8-Inp.InBit)&7); + + byte BlockFlags=Inp.fgetbits()>>8; + Inp.faddbits(8); + uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. + + if (ByteCount==4) + return false; + + Header.HeaderSize=2+ByteCount; + + Header.BlockBitSize=(BlockFlags&7)+1; + + byte SavedCheckSum=Inp.fgetbits()>>8; + Inp.faddbits(8); + + int BlockSize=0; + for (uint I=0;I<ByteCount;I++) + { + BlockSize+=(Inp.fgetbits()>>8)<<(I*8); + Inp.addbits(8); + } + + Header.BlockSize=BlockSize; + byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); + if (CheckSum!=SavedCheckSum) + return false; + + Header.BlockStart=Inp.InAddr; + ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); + + Header.LastBlockInFile=(BlockFlags & 0x40)!=0; + Header.TablePresent=(BlockFlags & 0x80)!=0; + return true; +} + + +bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables) +{ + if (!Header.TablePresent) + return true; + + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) + if (!UnpReadBuf()) + return false; + + byte BitLength[BC]; + for (uint I=0;I<BC;I++) + { + uint Length=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (Length==15) + { + uint ZeroCount=(byte)(Inp.fgetbits() >> 12); + Inp.faddbits(4); + if (ZeroCount==0) + BitLength[I]=15; + else + { + ZeroCount+=2; + while (ZeroCount-- > 0 && I<ASIZE(BitLength)) + BitLength[I++]=0; + I--; + } + } + else + BitLength[I]=Length; + } + + MakeDecodeTables(BitLength,&Tables.BD,BC); + + byte Table[HUFF_TABLE_SIZE]; + const uint TableSize=HUFF_TABLE_SIZE; + for (uint I=0;I<TableSize;) + { + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-5) + if (!UnpReadBuf()) + return false; + uint Number=DecodeNumber(Inp,&Tables.BD); + if (Number<16) + { + Table[I]=Number; + I++; + } + else + if (Number<18) + { + uint N; + if (Number==16) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + if (I==0) + { + // We cannot have "repeat previous" code at the first position. + // Multiple such codes would shift Inp position without changing I, + // which can lead to reading beyond of Inp boundary in mutithreading + // mode, where Inp.ExternalBuffer disables bounds check and we just + // reserve a lot of buffer space to not need such check normally. + return false; + } + else + while (N-- > 0 && I<TableSize) + { + Table[I]=Table[I-1]; + I++; + } + } + else + { + uint N; + if (Number==18) + { + N=(Inp.fgetbits() >> 13)+3; + Inp.faddbits(3); + } + else + { + N=(Inp.fgetbits() >> 9)+11; + Inp.faddbits(7); + } + while (N-- > 0 && I<TableSize) + Table[I++]=0; + } + } + TablesRead5=true; + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop) + return false; + MakeDecodeTables(&Table[0],&Tables.LD,NC); + MakeDecodeTables(&Table[NC],&Tables.DD,DC); + MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); + MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); + return true; +} + + +void Unpack::InitFilters() +{ + Filters.SoftReset(); +} diff --git a/unrar/unrar/unpack50frag.cpp b/unrar/unrar/unpack50frag.cpp new file mode 100644 index 0000000..3c008ff --- /dev/null +++ b/unrar/unrar/unpack50frag.cpp @@ -0,0 +1,103 @@ +FragmentedWindow::FragmentedWindow() +{ + memset(Mem,0,sizeof(Mem)); + memset(MemSize,0,sizeof(MemSize)); +} + + +FragmentedWindow::~FragmentedWindow() +{ + Reset(); +} + + +void FragmentedWindow::Reset() +{ + for (uint I=0;I<ASIZE(Mem);I++) + if (Mem[I]!=NULL) + { + free(Mem[I]); + Mem[I]=NULL; + } +} + + +void FragmentedWindow::Init(size_t WinSize) +{ + Reset(); + + uint BlockNum=0; + size_t TotalSize=0; // Already allocated. + while (TotalSize<WinSize && BlockNum<ASIZE(Mem)) + { + size_t Size=WinSize-TotalSize; // Size needed to allocate. + + // Minimum still acceptable block size. Next allocations cannot be larger + // than current, so we do not need blocks if they are smaller than + // "size left / attempts left". Also we do not waste time to blocks + // smaller than some arbitrary constant. + size_t MinSize=Max(Size/(ASIZE(Mem)-BlockNum), 0x400000); + + byte *NewMem=NULL; + while (Size>=MinSize) + { + NewMem=(byte *)malloc(Size); + if (NewMem!=NULL) + break; + Size-=Size/32; + } + if (NewMem==NULL) + throw std::bad_alloc(); + + // Clean the window to generate the same output when unpacking corrupt + // RAR files, which may access to unused areas of sliding dictionary. + memset(NewMem,0,Size); + + Mem[BlockNum]=NewMem; + TotalSize+=Size; + MemSize[BlockNum]=TotalSize; + BlockNum++; + } + if (TotalSize<WinSize) // Not found enough free blocks. + throw std::bad_alloc(); +} + + +byte& FragmentedWindow::operator [](size_t Item) +{ + if (Item<MemSize[0]) + return Mem[0][Item]; + for (uint I=1;I<ASIZE(MemSize);I++) + if (Item<MemSize[I]) + return Mem[I][Item-MemSize[I-1]]; + return Mem[0][0]; // Must never happen; +} + + +void FragmentedWindow::CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask) +{ + size_t SrcPtr=UnpPtr-Distance; + while (Length-- > 0) + { + (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' + UnpPtr=(UnpPtr+1) & MaxWinMask; + } +} + + +void FragmentedWindow::CopyData(byte *Dest,size_t WinPos,size_t Size) +{ + for (size_t I=0;I<Size;I++) + Dest[I]=(*this)[WinPos+I]; +} + + +size_t FragmentedWindow::GetBlockSize(size_t StartPos,size_t RequiredSize) +{ + for (uint I=0;I<ASIZE(MemSize);I++) + if (StartPos<MemSize[I]) + return Min(MemSize[I]-StartPos,RequiredSize); + return 0; // Must never be here. +} diff --git a/unrar/unrar/unpack50mt.cpp b/unrar/unrar/unpack50mt.cpp new file mode 100644 index 0000000..82c9c4a --- /dev/null +++ b/unrar/unrar/unpack50mt.cpp @@ -0,0 +1,655 @@ +#define UNP_READ_SIZE_MT 0x400000 +#define UNP_BLOCKS_PER_THREAD 2 + + +struct UnpackThreadDataList +{ + UnpackThreadData *D; + uint BlockCount; +}; + + +THREAD_PROC(UnpackDecodeThread) +{ + UnpackThreadDataList *DL=(UnpackThreadDataList *)Data; + for (uint I=0;I<DL->BlockCount;I++) + DL->D->UnpackPtr->UnpackDecode(DL->D[I]); +} + + +void Unpack::InitMT() +{ + if (ReadBufMT==NULL) + { + // Even getbits32 can read up to 3 additional bytes after current + // and our block header and table reading code can look much further. + // Let's allocate the additional space here, so we do not need to check + // bounds for every bit field access. + const size_t Overflow=1024; + + ReadBufMT=new byte[UNP_READ_SIZE_MT+Overflow]; + memset(ReadBufMT,0,UNP_READ_SIZE_MT+Overflow); + } + if (UnpThreadData==NULL) + { + uint MaxItems=MaxUserThreads*UNP_BLOCKS_PER_THREAD; + UnpThreadData=new UnpackThreadData[MaxItems]; + memset(UnpThreadData,0,sizeof(UnpackThreadData)*MaxItems); + + for (uint I=0;I<MaxItems;I++) + { + UnpackThreadData *CurData=UnpThreadData+I; + if (CurData->Decoded==NULL) + { + // Typical number of items in RAR blocks does not exceed 0x4000. + CurData->DecodedAllocated=0x4100; + // It will be freed in the object destructor, not in this file. + CurData->Decoded=(UnpackDecodedItem *)malloc(CurData->DecodedAllocated*sizeof(UnpackDecodedItem)); + if (CurData->Decoded==NULL) + ErrHandler.MemoryError(); + } + } + } +} + + +void Unpack::Unpack5MT(bool Solid) +{ + InitMT(); + UnpInitData(Solid); + + for (uint I=0;I<MaxUserThreads*UNP_BLOCKS_PER_THREAD;I++) + { + UnpackThreadData *CurData=UnpThreadData+I; + CurData->LargeBlock=false; + CurData->Incomplete=false; + } + + UnpThreadData[0].BlockHeader=BlockHeader; + UnpThreadData[0].BlockTables=BlockTables; + uint LastBlockNum=0; + + int DataSize=0; + int BlockStart=0; + + + // 'true' if we found a block too large for multithreaded extraction, + // so we switched to single threaded mode until the end of file. + // Large blocks could cause too high memory use in multithreaded mode. + bool LargeBlock=false; + + bool Done=false; + while (!Done) + { + // Data amount, which is guaranteed to fit block header and tables, + // so we can safely read them without additional checks. + const int TooSmallToProcess=1024; + + int ReadSize=UnpIO->UnpRead(ReadBufMT+DataSize,(UNP_READ_SIZE_MT-DataSize)&~0xf); + if (ReadSize<0) + break; + DataSize+=ReadSize; + if (DataSize==0) + break; + + // First read chunk can be small if we are near the end of volume + // and we want it to fit block header and tables. + if (ReadSize>0 && DataSize<TooSmallToProcess) + continue; + + while (BlockStart<DataSize && !Done) + { + uint BlockNumber=0,BlockNumberMT=0; + while (BlockNumber<MaxUserThreads*UNP_BLOCKS_PER_THREAD) + { + UnpackThreadData *CurData=UnpThreadData+BlockNumber; + LastBlockNum=BlockNumber; + CurData->UnpackPtr=this; + + // 'Incomplete' thread is present. This is a thread processing block + // in the end of buffer, split between two read operations. + if (CurData->Incomplete) + CurData->DataSize=DataSize; + else + { + CurData->Inp.SetExternalBuffer(ReadBufMT+BlockStart); + CurData->Inp.InitBitInput(); + CurData->DataSize=DataSize-BlockStart; + if (CurData->DataSize==0) + break; + CurData->DamagedData=false; + CurData->HeaderRead=false; + CurData->TableRead=false; + } + + // We should not use 'last block in file' block flag here unless + // we'll check the block size, because even if block is last in file, + // it can exceed the current buffer and require more reading. + CurData->NoDataLeft=(ReadSize==0); + + CurData->Incomplete=false; + CurData->ThreadNumber=BlockNumber; + + if (!CurData->HeaderRead) + { + CurData->HeaderRead=true; + if (!ReadBlockHeader(CurData->Inp,CurData->BlockHeader) || + !CurData->BlockHeader.TablePresent && !TablesRead5) + { + Done=true; + break; + } + TablesRead5=true; + } + + // To prevent too high memory use we switch to single threaded mode + // if block exceeds this size. Typically RAR blocks do not exceed + // 64 KB, so this protection should not affect most of valid archives. + const int LargeBlockSize=0x20000; + if (LargeBlock || CurData->BlockHeader.BlockSize>LargeBlockSize) + LargeBlock=CurData->LargeBlock=true; + else + BlockNumberMT++; // Number of normal blocks processed in MT mode. + + BlockStart+=CurData->BlockHeader.HeaderSize+CurData->BlockHeader.BlockSize; + + BlockNumber++; + + int DataLeft=DataSize-BlockStart; + if (DataLeft>=0 && CurData->BlockHeader.LastBlockInFile) + break; + + // For second and following threads we move smaller blocks to buffer + // start to ensure that we have enough data to fit block header + // and tables. + if (DataLeft<TooSmallToProcess) + break; + } + +//#undef USE_THREADS + UnpackThreadDataList UTDArray[MaxPoolThreads]; + uint UTDArrayPos=0; + + uint MaxBlockPerThread=BlockNumberMT/MaxUserThreads; + if (BlockNumberMT%MaxUserThreads!=0) + MaxBlockPerThread++; + + // Decode all normal blocks until the first 'large' if any. + for (uint CurBlock=0;CurBlock<BlockNumberMT;CurBlock+=MaxBlockPerThread) + { + UnpackThreadDataList *UTD=UTDArray+UTDArrayPos++; + UTD->D=UnpThreadData+CurBlock; + UTD->BlockCount=Min(MaxBlockPerThread,BlockNumberMT-CurBlock); + +#ifdef USE_THREADS + if (BlockNumber==1) + UnpackDecode(*UTD->D); + else + UnpThreadPool->AddTask(UnpackDecodeThread,(void*)UTD); +#else + for (uint I=0;I<UTD->BlockCount;I++) + UnpackDecode(UTD->D[I]); +#endif + } + + if (BlockNumber==0) + break; + +#ifdef USE_THREADS + UnpThreadPool->WaitDone(); +#endif + + bool IncompleteThread=false; + + for (uint Block=0;Block<BlockNumber;Block++) + { + UnpackThreadData *CurData=UnpThreadData+Block; + if (!CurData->LargeBlock && !ProcessDecoded(*CurData) || + CurData->LargeBlock && !UnpackLargeBlock(*CurData) || + CurData->DamagedData) + { + Done=true; + break; + } + if (CurData->Incomplete) + { + int BufPos=int(CurData->Inp.InBuf+CurData->Inp.InAddr-ReadBufMT); + if (DataSize<=BufPos) // Thread exceeded input buffer boundary. + { + Done=true; + break; + } + IncompleteThread=true; + memmove(ReadBufMT,ReadBufMT+BufPos,DataSize-BufPos); + CurData->BlockHeader.BlockSize-=CurData->Inp.InAddr-CurData->BlockHeader.BlockStart; + CurData->BlockHeader.HeaderSize=0; + CurData->BlockHeader.BlockStart=0; + CurData->Inp.InBuf=ReadBufMT; + CurData->Inp.InAddr=0; + + if (Block!=0) + { + // Move the incomplete thread entry to the first position, + // so we'll start processing from it. Preserve the original + // buffer for decoded data. + UnpackDecodedItem *Decoded=UnpThreadData[0].Decoded; + uint DecodedAllocated=UnpThreadData[0].DecodedAllocated; + UnpThreadData[0]=*CurData; + UnpThreadData[0].Decoded=Decoded; + UnpThreadData[0].DecodedAllocated=DecodedAllocated; + CurData->Incomplete=false; + } + + BlockStart=0; + DataSize-=BufPos; + break; + } + else + if (CurData->BlockHeader.LastBlockInFile) + { + Done=true; + break; + } + } + + if (IncompleteThread || Done) + break; // Current buffer is done, read more data or quit. + else + { + int DataLeft=DataSize-BlockStart; + if (DataLeft<TooSmallToProcess) + { + if (DataLeft<0) // Invalid data, must not happen in valid archive. + { + Done=true; + break; + } + + // If we do not have incomplete thread and have some data + // in the end of buffer, too small for single thread, + // let's move it to beginning of next buffer. + if (DataLeft>0) + memmove(ReadBufMT,ReadBufMT+BlockStart,DataLeft); + DataSize=DataLeft; + BlockStart=0; + break; // Current buffer is done, try to read more data. + } + } + } + } + UnpPtr&=MaxWinMask; // ProcessDecoded and maybe others can leave UnpPtr > MaxWinMask here. + UnpWriteBuf(); + + BlockHeader=UnpThreadData[LastBlockNum].BlockHeader; + BlockTables=UnpThreadData[LastBlockNum].BlockTables; +} + + +// Decode Huffman block and save decoded data to memory. +void Unpack::UnpackDecode(UnpackThreadData &D) +{ + if (!D.TableRead) + { + D.TableRead=true; + if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) + { + D.DamagedData=true; + return; + } + } + + if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) + { + D.DamagedData=true; + return; + } + + D.DecodedSize=0; + int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; + + // Reserve enough space even for filter entry. + int DataBorder=D.DataSize-16; + int ReadBorder=Min(BlockBorder,DataBorder); + + while (true) + { + if (D.Inp.InAddr>=ReadBorder) + { + if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && + D.Inp.InBit>=D.BlockHeader.BlockBitSize) + break; + + // If we do not have any more data in file to read, we must process + // what we have until last byte. Otherwise we can return and append + // more data to unprocessed few bytes. + if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) + { + D.Incomplete=true; + break; + } + } + if (D.DecodedSize>D.DecodedAllocated-8) // Filter can use several slots. + { + D.DecodedAllocated=D.DecodedAllocated*2; + void *Decoded=realloc(D.Decoded,D.DecodedAllocated*sizeof(UnpackDecodedItem)); + if (Decoded==NULL) + ErrHandler.MemoryError(); // D.Decoded will be freed in the destructor. + D.Decoded=(UnpackDecodedItem *)Decoded; + } + + UnpackDecodedItem *CurItem=D.Decoded+D.DecodedSize++; + + uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); + if (MainSlot<256) + { + if (D.DecodedSize>1) + { + UnpackDecodedItem *PrevItem=CurItem-1; + if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<ASIZE(PrevItem->Literal)-1) + { + PrevItem->Length++; + PrevItem->Literal[PrevItem->Length]=(byte)MainSlot; + D.DecodedSize--; + continue; + } + } + CurItem->Type=UNPDT_LITERAL; + CurItem->Literal[0]=(byte)MainSlot; + CurItem->Length=0; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(D.Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + D.Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=D.Inp.getbits()>>(16-DBits); + D.Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + CurItem->Type=UNPDT_MATCH; + CurItem->Length=(ushort)Length; + CurItem->Distance=Distance; + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + ReadFilter(D.Inp,Filter); + + CurItem->Type=UNPDT_FILTER; + CurItem->Length=Filter.Type; + CurItem->Distance=Filter.BlockStart; + + CurItem=D.Decoded+D.DecodedSize++; + + CurItem->Type=UNPDT_FILTER; + CurItem->Length=Filter.Channels; + CurItem->Distance=Filter.BlockLength; + + continue; + } + if (MainSlot==257) + { + CurItem->Type=UNPDT_FULLREP; + continue; + } + if (MainSlot<262) + { + CurItem->Type=UNPDT_REP; + CurItem->Distance=MainSlot-258; + uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); + uint Length=SlotToLength(D.Inp,LengthSlot); + CurItem->Length=(ushort)Length; + continue; + } + } +} + + +// Process decoded Huffman block data. +bool Unpack::ProcessDecoded(UnpackThreadData &D) +{ + UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize; + while (Item<Border) + { + UnpPtr&=MaxWinMask; + if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + { + UnpWriteBuf(); + if (WrittenFileSize>DestUnpSize) + return false; + } + + if (Item->Type==UNPDT_LITERAL) + { +#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) + if (Item->Length==7 && UnpPtr<MaxWinSize-8) + { + *(uint64 *)(Window+UnpPtr)=*(uint64 *)(Item->Literal); + UnpPtr+=8; + } + else +#endif + for (uint I=0;I<=Item->Length;I++) + Window[UnpPtr++ & MaxWinMask]=Item->Literal[I]; + } + else + if (Item->Type==UNPDT_MATCH) + { + InsertOldDist(Item->Distance); + LastLength=Item->Length; + CopyString(Item->Length,Item->Distance); + } + else + if (Item->Type==UNPDT_REP) + { + uint Distance=OldDist[Item->Distance]; + for (uint I=Item->Distance;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + LastLength=Item->Length; + CopyString(Item->Length,Distance); + } + else + if (Item->Type==UNPDT_FULLREP) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + } + else + if (Item->Type==UNPDT_FILTER) + { + UnpackFilter Filter; + + Filter.Type=(byte)Item->Length; + Filter.BlockStart=Item->Distance; + + Item++; + + Filter.Channels=(byte)Item->Length; + Filter.BlockLength=Item->Distance; + + AddFilter(Filter); + } + Item++; + } + return true; +} + + +// For large blocks we decode and process in same function in single threaded +// mode, so we do not need to store intermediate data in memory. +bool Unpack::UnpackLargeBlock(UnpackThreadData &D) +{ + if (!D.TableRead) + { + D.TableRead=true; + if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) + { + D.DamagedData=true; + return false; + } + } + + if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) + { + D.DamagedData=true; + return false; + } + + int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; + + // Reserve enough space even for filter entry. + int DataBorder=D.DataSize-16; + int ReadBorder=Min(BlockBorder,DataBorder); + + while (true) + { + UnpPtr&=MaxWinMask; + if (D.Inp.InAddr>=ReadBorder) + { + if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && + D.Inp.InBit>=D.BlockHeader.BlockBitSize) + break; + + // If we do not have any more data in file to read, we must process + // what we have until last byte. Otherwise we can return and append + // more data to unprocessed few bytes. + if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) + { + D.Incomplete=true; + break; + } + } + if (((WriteBorder-UnpPtr) & MaxWinMask)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) + { + UnpWriteBuf(); + if (WrittenFileSize>DestUnpSize) + return false; + } + + uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); + if (MainSlot<256) + { + Window[UnpPtr++]=(byte)MainSlot; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(D.Inp,MainSlot-262); + + uint DBits,Distance=1,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + DBits=DistSlot/2 - 1; + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((D.Inp.getbits32()>>(36-DBits))<<4); + D.Inp.addbits(DBits-4); + } + uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); + Distance+=LowDist; + } + else + { + Distance+=D.Inp.getbits32()>>(32-DBits); + D.Inp.addbits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + InsertOldDist(Distance); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter; + if (!ReadFilter(D.Inp,Filter) || !AddFilter(Filter)) + break; + continue; + } + if (MainSlot==257) + { + if (LastLength!=0) + CopyString(LastLength,OldDist[0]); + continue; + } + if (MainSlot<262) + { + uint DistNum=MainSlot-258; + uint Distance=OldDist[DistNum]; + for (uint I=DistNum;I>0;I--) + OldDist[I]=OldDist[I-1]; + OldDist[0]=Distance; + + uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); + uint Length=SlotToLength(D.Inp,LengthSlot); + LastLength=Length; + CopyString(Length,Distance); + continue; + } + } + return true; +} diff --git a/unrar/unrar/unpackinline.cpp b/unrar/unrar/unpackinline.cpp new file mode 100644 index 0000000..04c3d1f --- /dev/null +++ b/unrar/unrar/unpackinline.cpp @@ -0,0 +1,147 @@ +_forceinline void Unpack::InsertOldDist(uint Distance) +{ + OldDist[3]=OldDist[2]; + OldDist[2]=OldDist[1]; + OldDist[1]=OldDist[0]; + OldDist[0]=Distance; +} + +#ifdef _MSC_VER +#define FAST_MEMCPY +#endif + +_forceinline void Unpack::CopyString(uint Length,uint Distance) +{ + size_t SrcPtr=UnpPtr-Distance; + if (SrcPtr<MaxWinSize-MAX_INC_LZ_MATCH && UnpPtr<MaxWinSize-MAX_INC_LZ_MATCH) + { + // If we are not close to end of window, we do not need to waste time + // to "& MaxWinMask" pointer protection. + + byte *Src=Window+SrcPtr; + byte *Dest=Window+UnpPtr; + UnpPtr+=Length; + +#ifdef FAST_MEMCPY + if (Distance<Length) // Overlapping strings +#endif + while (Length>=8) + { + Dest[0]=Src[0]; + Dest[1]=Src[1]; + Dest[2]=Src[2]; + Dest[3]=Src[3]; + Dest[4]=Src[4]; + Dest[5]=Src[5]; + Dest[6]=Src[6]; + Dest[7]=Src[7]; + + Src+=8; + Dest+=8; + Length-=8; + } +#ifdef FAST_MEMCPY + else + while (Length>=8) + { + // In theory we still could overlap here. + // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). + // But for real RAR archives Distance <= MaxWinSize - MAX_INC_LZ_MATCH + // always, so overlap here is impossible. + + // This memcpy expanded inline by MSVC. We could also use uint64 + // assignment, which seems to provide about the same speed. + memcpy(Dest,Src,8); + + Src+=8; + Dest+=8; + Length-=8; + } +#endif + + // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. + if (Length>0) { Dest[0]=Src[0]; + if (Length>1) { Dest[1]=Src[1]; + if (Length>2) { Dest[2]=Src[2]; + if (Length>3) { Dest[3]=Src[3]; + if (Length>4) { Dest[4]=Src[4]; + if (Length>5) { Dest[5]=Src[5]; + if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. + } + else + while (Length-- > 0) // Slow copying with all possible precautions. + { + Window[UnpPtr]=Window[SrcPtr++ & MaxWinMask]; + // We need to have masked UnpPtr after quit from loop, so it must not + // be replaced with 'Window[UnpPtr++ & MaxWinMask]' + UnpPtr=(UnpPtr+1) & MaxWinMask; + } +} + + +_forceinline uint Unpack::DecodeNumber(BitInput &Inp,DecodeTable *Dec) +{ + // Left aligned 15 bit length raw bit field. + uint BitField=Inp.getbits() & 0xfffe; + + if (BitField<Dec->DecodeLen[Dec->QuickBits]) + { + uint Code=BitField>>(16-Dec->QuickBits); + Inp.addbits(Dec->QuickLen[Code]); + return Dec->QuickNum[Code]; + } + + // Detect the real bit length for current code. + uint Bits=15; + for (uint I=Dec->QuickBits+1;I<15;I++) + if (BitField<Dec->DecodeLen[I]) + { + Bits=I; + break; + } + + Inp.addbits(Bits); + + // Calculate the distance from the start code for current bit length. + uint Dist=BitField-Dec->DecodeLen[Bits-1]; + + // Start codes are left aligned, but we need the normal right aligned + // number. So we shift the distance to the right. + Dist>>=(16-Bits); + + // Now we can calculate the position in the code list. It is the sum + // of first position for current bit length and right aligned distance + // between our bit field and start code for current bit length. + uint Pos=Dec->DecodePos[Bits]+Dist; + + // Out of bounds safety check required for damaged archives. + if (Pos>=Dec->MaxNum) + Pos=0; + + // Convert the position in the code list to position in alphabet + // and return it. + return Dec->DecodeNum[Pos]; +} + + +_forceinline uint Unpack::SlotToLength(BitInput &Inp,uint Slot) +{ + uint LBits,Length=2; + if (Slot<8) + { + LBits=0; + Length+=Slot; + } + else + { + LBits=Slot/4-1; + Length+=(4 | (Slot & 3)) << LBits; + } + + if (LBits>0) + { + Length+=Inp.getbits()>>(16-LBits); + Inp.addbits(LBits); + } + return Length; +} diff --git a/unrar/unrar/uowners.cpp b/unrar/unrar/uowners.cpp index 38471e0..5eb1279 100644 --- a/unrar/unrar/uowners.cpp +++ b/unrar/unrar/uowners.cpp @@ -1,80 +1,97 @@ -void ExtractUnixOwner(Archive &Arc,char *FileName) +void ExtractUnixOwner30(Archive &Arc,const wchar *FileName) { - if (Arc.HeaderCRC!=Arc.UOHead.HeadCRC) - { - Log(Arc.FileName,St(MOwnersBroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); + + if (memchr(&Arc.SubHead.SubData[0],0,Arc.SubHead.SubData.Size())==NULL) return; - } + + char *OwnerName=(char *)&Arc.SubHead.SubData[0]; + int OwnerSize=strlen(OwnerName)+1; + int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize; + char GroupName[NM]; + strncpy(GroupName,(char *)&Arc.SubHead.SubData[OwnerSize],GroupSize); + GroupName[GroupSize]=0; struct passwd *pw; - if ((pw=getpwnam(Arc.UOHead.OwnerName))==NULL) + if ((pw=getpwnam(OwnerName))==NULL) { - Log(Arc.FileName,St(MErrGetOwnerID),Arc.UOHead.OwnerName); - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(OwnerName)); + ErrHandler.SetErrorCode(RARX_WARNING); return; } uid_t OwnerID=pw->pw_uid; struct group *gr; - if ((gr=getgrnam(Arc.UOHead.GroupName))==NULL) + if ((gr=getgrnam(GroupName))==NULL) { - Log(Arc.FileName,St(MErrGetGroupID),Arc.UOHead.GroupName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(GroupName)); + ErrHandler.SetErrorCode(RARX_WARNING); return; } - uint Attr=GetFileAttr(FileName,NULL); + uint Attr=GetFileAttr(FileName); gid_t GroupID=gr->gr_gid; #if defined(SAVE_LINKS) && !defined(_APPLE) - if (lchown(FileName,OwnerID,GroupID)!=0) + if (lchown(NameA,OwnerID,GroupID)!=0) #else - if (chown(FileName,OwnerID,GroupID)!=0) + if (chown(NameA,OwnerID,GroupID)!=0) #endif { - Log(Arc.FileName,St(MSetOwnersError),FileName); - ErrHandler.SetErrorCode(CREATE_ERROR); + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); } - SetFileAttr(FileName,NULL,Attr); + SetFileAttr(FileName,Attr); } -void ExtractUnixOwnerNew(Archive &Arc,char *FileName) +void SetUnixOwner(Archive &Arc,const wchar *FileName) { - char *OwnerName=(char *)&Arc.SubHead.SubData[0]; - int OwnerSize=strlen(OwnerName)+1; - int GroupSize=Arc.SubHead.SubData.Size()-OwnerSize; - char GroupName[NM]; - strncpy(GroupName,(char *)&Arc.SubHead.SubData[OwnerSize],GroupSize); - GroupName[GroupSize]=0; + char NameA[NM]; + WideToChar(FileName,NameA,ASIZE(NameA)); - struct passwd *pw; - if ((pw=getpwnam(OwnerName))==NULL) + // First, we try to resolve symbolic names. If they are missing or cannot + // be resolved, we try to use numeric values if any. If numeric values + // are missing too, function fails. + FileHeader &hd=Arc.FileHead; + if (*hd.UnixOwnerName!=0) { - Log(Arc.FileName,St(MErrGetOwnerID),OwnerName); - ErrHandler.SetErrorCode(WARNING); - return; + struct passwd *pw; + if ((pw=getpwnam(hd.UnixOwnerName))==NULL) + { + if (!hd.UnixOwnerNumeric) + { + uiMsg(UIERROR_UOWNERGETOWNERID,Arc.FileName,GetWide(hd.UnixOwnerName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + } + else + hd.UnixOwnerID=pw->pw_uid; } - uid_t OwnerID=pw->pw_uid; - - struct group *gr; - if ((gr=getgrnam(GroupName))==NULL) + if (*hd.UnixGroupName!=0) { - Log(Arc.FileName,St(MErrGetGroupID),GroupName); - ErrHandler.SetErrorCode(CRC_ERROR); - return; + struct group *gr; + if ((gr=getgrnam(hd.UnixGroupName))==NULL) + { + if (!hd.UnixGroupNumeric) + { + uiMsg(UIERROR_UOWNERGETGROUPID,Arc.FileName,GetWide(hd.UnixGroupName)); + ErrHandler.SetErrorCode(RARX_WARNING); + return; + } + } + else + hd.UnixGroupID=gr->gr_gid; } - uint Attr=GetFileAttr(FileName,NULL); - gid_t GroupID=gr->gr_gid; #if defined(SAVE_LINKS) && !defined(_APPLE) - if (lchown(FileName,OwnerID,GroupID)!=0) + if (lchown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) #else - if (chown(FileName,OwnerID,GroupID)!=0) + if (chown(NameA,hd.UnixOwnerID,hd.UnixGroupID)!=0) #endif { - Log(Arc.FileName,St(MSetOwnersError),FileName); - ErrHandler.SetErrorCode(CREATE_ERROR); + uiMsg(UIERROR_UOWNERSET,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CREATE); } - SetFileAttr(FileName,NULL,Attr); } diff --git a/unrar/unrar/version.hpp b/unrar/unrar/version.hpp index 616d34d..c9fffca 100644 --- a/unrar/unrar/version.hpp +++ b/unrar/unrar/version.hpp @@ -1,6 +1,6 @@ -#define RARVER_MAJOR 3 -#define RARVER_MINOR 80 -#define RARVER_BETA 2 -#define RARVER_DAY 15 -#define RARVER_MONTH 6 -#define RARVER_YEAR 2008 +#define RARVER_MAJOR 6 +#define RARVER_MINOR 24 +#define RARVER_BETA 0 +#define RARVER_DAY 3 +#define RARVER_MONTH 10 +#define RARVER_YEAR 2023 diff --git a/unrar/unrar/volume.cpp b/unrar/unrar/volume.cpp index 4c138db..4924d8d 100644 --- a/unrar/unrar/volume.cpp +++ b/unrar/unrar/volume.cpp @@ -1,203 +1,187 @@ #include "rar.hpp" +#ifdef RARDLL +static bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize); +static bool DllVolNotify(CommandData *Cmd,wchar *NextName); +#endif -bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Command) +bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Command) { - RAROptions *Cmd=Arc.GetRAROptions(); + CommandData *Cmd=Arc.GetCommandData(); - int HeaderType=Arc.GetHeaderType(); - FileHeader *hd=HeaderType==NEWSUB_HEAD ? &Arc.SubHead:&Arc.NewLhd; - bool SplitHeader=(HeaderType==FILE_HEAD || HeaderType==NEWSUB_HEAD) && - (hd->Flags & LHD_SPLIT_AFTER)!=0; + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + FileHeader *hd=HeaderType==HEAD_SERVICE ? &Arc.SubHead:&Arc.FileHead; + bool SplitHeader=(HeaderType==HEAD_FILE || HeaderType==HEAD_SERVICE) && + hd->SplitAfter; - if (DataIO!=NULL && SplitHeader && hd->UnpVer>=20 && - hd->FileCRC!=0xffffffff && DataIO->PackedCRC!=~hd->FileCRC) + if (DataIO!=NULL && SplitHeader) { - Log(Arc.FileName,St(MDataBadCRC),hd->FileName,Arc.FileName); + bool PackedHashPresent=Arc.Format==RARFMT50 || + hd->UnpVer>=20 && hd->FileHash.CRC32!=0xffffffff; + if (PackedHashPresent && + !DataIO->PackedDataHash.Cmp(&hd->FileHash,hd->UseHashKey ? hd->HashKey:NULL)) + uiMsg(UIERROR_CHECKSUMPACKED, Arc.FileName, hd->FileName); } - Int64 PosBeforeClose=Arc.Tell(); + bool PrevVolEncrypted=Arc.Encrypted; + + int64 PosBeforeClose=Arc.Tell(); if (DataIO!=NULL) - DataIO->ProcessedArcSize+=Arc.FileLength(); + DataIO->ProcessedArcSize+=DataIO->LastArcSize; + Arc.Close(); - char NextName[NM]; - wchar NextNameW[NM]; - strcpy(NextName,Arc.FileName); - strcpyw(NextNameW,Arc.FileNameW); - NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat); + wchar NextName[NM]; + wcsncpyz(NextName,Arc.FileName,ASIZE(NextName)); + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); #if !defined(SFX_MODULE) && !defined(RARDLL) bool RecoveryDone=false; #endif - bool FailedOpen=false,OldSchemeTested=false; + bool OldSchemeTested=false; - while (!Arc.Open(NextName,NextNameW)) - { - // We need to open a new volume which size was not calculated - // in total size before, so we cannot calculate the total progress - // anymore. Let's reset the total size to zero and stop - // the total progress. - if (DataIO!=NULL) - DataIO->TotalArcSize=0; - - if (!OldSchemeTested) + bool FailedOpen=false; // No more next volume open attempts if true. +#if !defined(SILENT) + // In -vp mode we force the pause before next volume even if it is present + // and even if we are on the hard disk. It is important when user does not + // want to process partially downloaded volumes preliminary. + // 2022.01.11: In WinRAR 6.10 beta versions we tried to ignore VolumePause + // if we could open the next volume with FMF_OPENEXCLUSIVE. But another + // developer asked us to return the previous behavior and always prompt + // for confirmation. They want to control when unrar continues, because + // the next file might not be fully decoded yet. They write chunks of data + // and then close the file again until the next chunk comes in. + + if (Cmd->VolumePause && !uiAskNextVolume(NextName,ASIZE(NextName))) + FailedOpen=true; +#endif + + uint OpenMode = Cmd->OpenShared ? FMF_OPENSHARED : 0; + + if (!FailedOpen) + while (!Arc.Open(NextName,OpenMode)) { - // Checking for new style volumes renamed by user to old style - // name format. Some users did it for unknown reason. - char AltNextName[NM]; - wchar AltNextNameW[NM]; - strcpy(AltNextName,Arc.FileName); - strcpyw(AltNextNameW,Arc.FileNameW); - NextVolumeName(AltNextName,AltNextNameW,ASIZE(AltNextName),true); - OldSchemeTested=true; - if (Arc.Open(AltNextName,AltNextNameW)) + // We need to open a new volume which size was not calculated + // in total size before, so we cannot calculate the total progress + // anymore. Let's reset the total size to zero and stop + // the total progress. + if (DataIO!=NULL) + DataIO->TotalArcSize=0; + + if (!OldSchemeTested) { - strcpy(NextName,AltNextName); - strcpyw(NextNameW,AltNextNameW); - break; + // Checking for new style volumes renamed by user to old style + // name format. Some users did it for unknown reason. + wchar AltNextName[NM]; + wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName)); + NextVolumeName(AltNextName,ASIZE(AltNextName),true); + OldSchemeTested=true; + if (Arc.Open(AltNextName,OpenMode)) + { + wcsncpyz(NextName,AltNextName,ASIZE(NextName)); + break; + } } - } #ifdef RARDLL - if (Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL || - Cmd->Callback!=NULL && Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1) - { - Cmd->DllError=ERAR_EOPEN; - FailedOpen=true; - break; - } - if (Cmd->ChangeVolProc!=NULL) - { -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _EBX=_ESP; -#endif - int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_ASK); -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _ESP=_EBX; -#endif - if (RetCode==0) + if (!DllVolChange(Cmd,NextName,ASIZE(NextName))) { - Cmd->DllError=ERAR_EOPEN; FailedOpen=true; break; } - } -#else // RARDLL +#else // !RARDLL -#if !defined(SFX_MODULE) && !defined(_WIN_CE) - if (!RecoveryDone) - { - RecVolumes RecVol; - RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true); - RecoveryDone=true; - continue; - } +#ifndef SFX_MODULE + if (!RecoveryDone) + { + RecVolumesRestore(Cmd,Arc.FileName,true); + RecoveryDone=true; + continue; + } #endif -#ifndef GUI - if (!Cmd->VolumePause && !IsRemovable(NextName)) - { - FailedOpen=true; - break; - } -#endif + if (!Cmd->VolumePause && !IsRemovable(NextName)) + { + FailedOpen=true; + break; + } #ifndef SILENT - if (Cmd->AllYes || !AskNextVol(NextName)) + if (Cmd->AllYes || !uiAskNextVolume(NextName,ASIZE(NextName))) #endif - { - FailedOpen=true; - break; - } + { + FailedOpen=true; + break; + } #endif // RARDLL - *NextNameW=0; - } + } + if (FailedOpen) { -#if !defined(SILENT) && !defined(_WIN_CE) - Log(Arc.FileName,St(MAbsNextVol),NextName); -#endif - Arc.Open(Arc.FileName,Arc.FileNameW); + uiMsg(UIERROR_MISSINGVOL,NextName); + Arc.Open(Arc.FileName,OpenMode); Arc.Seek(PosBeforeClose,SEEK_SET); - return(false); + return false; } + + if (Command=='T' || Command=='X' || Command=='E') + mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName); + + Arc.CheckArc(true); #ifdef RARDLL - if (Cmd->Callback!=NULL && - Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1) - return(false); - if (Cmd->ChangeVolProc!=NULL) - { -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _EBX=_ESP; -#endif - int RetCode=Cmd->ChangeVolProc(NextName,RAR_VOL_NOTIFY); -#if defined(_WIN_32) && !defined(_MSC_VER) && !defined(__MINGW32__) - _ESP=_EBX; + if (!DllVolNotify(Cmd,NextName)) + return false; #endif - if (RetCode==0) - return(false); + + if (Arc.Encrypted!=PrevVolEncrypted) + { + // There is no legitimate reason for encrypted header state to be + // changed in the middle of volume sequence. So we abort here to prevent + // replacing an encrypted header volume to unencrypted and adding + // unexpected files by third party to encrypted extraction. + uiMsg(UIERROR_BADARCHIVE,Arc.FileName); + ErrHandler.Exit(RARX_FATAL); } -#endif - if (Command=='T' || Command=='X' || Command=='E') - mprintf(St(Command=='T' ? MTestVol:MExtrVol),Arc.FileName); if (SplitHeader) Arc.SearchBlock(HeaderType); else Arc.ReadHeader(); - if (Arc.GetHeaderType()==FILE_HEAD) + if (Arc.GetHeaderType()==HEAD_FILE) { Arc.ConvertAttributes(); - Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET); + Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); } -#ifndef GUI - if (ShowFileName) + if (ShowFileName && !Cmd->DisableNames) { - char OutName[NM]; - IntToExt(Arc.NewLhd.FileName,OutName); -#ifdef UNICODE_SUPPORTED - bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled(); - if (WideName) - { - wchar NameW[NM]; - ConvertPath(Arc.NewLhd.FileNameW,NameW); - char Name[NM]; - if (WideToChar(NameW,Name) && IsNameUsable(Name)) - strcpy(OutName,Name); - } -#endif - mprintf(St(MExtrPoints),OutName); + mprintf(St(MExtrPoints),Arc.FileHead.FileName); if (!Cmd->DisablePercentage) - mprintf(" "); + mprintf(L" "); } -#endif if (DataIO!=NULL) { - if (HeaderType==ENDARC_HEAD) + if (HeaderType==HEAD_ENDARC) DataIO->UnpVolume=false; else { - DataIO->UnpVolume=(hd->Flags & LHD_SPLIT_AFTER); - DataIO->SetPackedSizeToRead(hd->FullPackSize); + DataIO->UnpVolume=hd->SplitAfter; + DataIO->SetPackedSizeToRead(hd->PackSize); } -#ifdef SFX_MODULE - DataIO->UnpArcSize=Arc.FileLength(); -#endif - + + DataIO->AdjustTotalArcSize(&Arc); + // Reset the size of packed data read from current volume. It is used // to display the total progress and preceding volumes are already // compensated with ProcessedArcSize, so we need to reset this variable. DataIO->CurUnpRead=0; - DataIO->PackedCRC=0xffffffff; -// DataIO->SetFiles(&Arc,NULL); + DataIO->PackedDataHash.Init(hd->FileHash.Type,Cmd->Threads); } - return(true); + return true; } @@ -205,12 +189,80 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,char Comman -#ifndef SILENT -bool AskNextVol(char *ArcName) +#ifdef RARDLL +bool DllVolChange(CommandData *Cmd,wchar *NextName,size_t NameSize) +{ + bool DllVolChanged=false,DllVolAborted=false; + + if (Cmd->Callback!=NULL) + { + wchar OrgNextName[NM]; + wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName)); + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1) + DllVolAborted=true; + else + if (wcscmp(OrgNextName,NextName)!=0) + DllVolChanged=true; + else + { + char NextNameA[NM],OrgNextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA)); + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1) + DllVolAborted=true; + else + if (strcmp(OrgNextNameA,NextNameA)!=0) + { + // We can damage some Unicode characters by U->A->U conversion, + // so set Unicode name only if we see that ANSI name is changed. + CharToWide(NextNameA,NextName,NameSize); + DllVolChanged=true; + } + } + } + if (!DllVolChanged && Cmd->ChangeVolProc!=NULL) + { + char NextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_ASK); + if (RetCode==0) + DllVolAborted=true; + else + CharToWide(NextNameA,NextName,NameSize); + } + + // We quit only on 'abort' condition, but not on 'name not changed'. + // It is legitimate for program to return the same name when waiting + // for currently non-existent volume. + // Also we quit to prevent an infinite loop if no callback is defined. + if (DllVolAborted || Cmd->Callback==NULL && Cmd->ChangeVolProc==NULL) + { + Cmd->DllError=ERAR_EOPEN; + return false; + } + return true; +} +#endif + + +#ifdef RARDLL +bool DllVolNotify(CommandData *Cmd,wchar *NextName) { - eprintf(St(MAskNextVol),ArcName); - if (Ask(St(MContinueQuit))==2) - return(false); - return(true); + char NextNameA[NM]; + WideToChar(NextName,NextNameA,ASIZE(NextNameA)); + if (Cmd->Callback!=NULL) + { + if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_NOTIFY)==-1) + return false; + if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_NOTIFY)==-1) + return false; + } + if (Cmd->ChangeVolProc!=NULL) + { + int RetCode=Cmd->ChangeVolProc(NextNameA,RAR_VOL_NOTIFY); + if (RetCode==0) + return false; + } + return true; } #endif diff --git a/unrar/unrar/volume.hpp b/unrar/unrar/volume.hpp index 6465340..4ada109 100644 --- a/unrar/unrar/volume.hpp +++ b/unrar/unrar/volume.hpp @@ -1,11 +1,7 @@ #ifndef _RAR_VOLUME_ #define _RAR_VOLUME_ -void SplitArchive(Archive &Arc,FileHeader *fh,Int64 *HeaderPos, - ComprDataIO *DataIO); bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName, - char Command); -void SetVolWrite(Archive &Dest,Int64 VolSize); -bool AskNextVol(char *ArcName); + wchar Command); #endif diff --git a/unrar/unrar/win32acl.cpp b/unrar/unrar/win32acl.cpp index 37fb22b..d4797bd 100644 --- a/unrar/unrar/win32acl.cpp +++ b/unrar/unrar/win32acl.cpp @@ -1,47 +1,45 @@ -static void SetPrivileges(); +static void SetACLPrivileges(); static bool ReadSacl=false; #ifndef SFX_MODULE -void ExtractACL(Archive &Arc,char *FileName,wchar *FileNameW) +void ExtractACL20(Archive &Arc,const wchar *FileName) { - if (!WinNT()) - return; - - SetPrivileges(); + SetACLPrivileges(); - if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC) + if (Arc.BrokenHeader) { - Log(Arc.FileName,St(MACLBroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER) + if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>VER_PACK) { - Log(Arc.FileName,St(MACLUnknown),FileName); - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_ACLUNKNOWN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); return; } ComprDataIO DataIO; Unpack Unpack(&DataIO); - Unpack.Init(); + Unpack.Init(0x10000,false); - Array<unsigned char> UnpData(Arc.EAHead.UnpSize); + Array<byte> UnpData(Arc.EAHead.UnpSize); DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize); DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize); DataIO.EnableShowProgress(false); DataIO.SetFiles(&Arc,NULL); + DataIO.UnpHash.Init(HASH_CRC32,1); Unpack.SetDestSize(Arc.EAHead.UnpSize); Unpack.DoUnpack(Arc.EAHead.UnpVer,false); - if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC) + if (Arc.EAHead.EACRC!=DataIO.UnpHash.GetCRC32()) { - Log(Arc.FileName,St(MACLBroken),FileName); - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_ACLBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } @@ -51,77 +49,87 @@ void ExtractACL(Archive &Arc,char *FileName,wchar *FileNameW) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&UnpData[0]; - int SetCode; - if (FileNameW!=NULL) - SetCode=SetFileSecurityW(FileNameW,si,sd); - else - SetCode=SetFileSecurity(FileName,si,sd); + int SetCode=SetFileSecurity(FileName,si,sd); if (!SetCode) { - Log(Arc.FileName,St(MACLSetError),FileName); + uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); + DWORD LastError=GetLastError(); ErrHandler.SysErrMsg(); - ErrHandler.SetErrorCode(WARNING); + if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SetErrorCode(RARX_WARNING); } } #endif -void ExtractACLNew(Archive &Arc,char *FileName,wchar *FileNameW) +void ExtractACL(Archive &Arc,const wchar *FileName) { - if (!WinNT()) - return; - Array<byte> SubData; - if (!Arc.ReadSubData(&SubData,NULL)) + if (!Arc.ReadSubData(&SubData,NULL,false)) return; - SetPrivileges(); + SetACLPrivileges(); - SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| - DACL_SECURITY_INFORMATION; + SECURITY_INFORMATION si=OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION| + DACL_SECURITY_INFORMATION; if (ReadSacl) si|=SACL_SECURITY_INFORMATION; SECURITY_DESCRIPTOR *sd=(SECURITY_DESCRIPTOR *)&SubData[0]; - int SetCode; - if (FileNameW!=NULL) - SetCode=SetFileSecurityW(FileNameW,si,sd); - else - SetCode=SetFileSecurity(FileName,si,sd); + int SetCode=SetFileSecurity(FileName,si,sd); + if (!SetCode) + { + wchar LongName[NM]; + if (GetWinLongPath(FileName,LongName,ASIZE(LongName))) + SetCode=SetFileSecurity(LongName,si,sd); + } if (!SetCode) { - Log(Arc.FileName,St(MACLSetError),FileName); + uiMsg(UIERROR_ACLSET,Arc.FileName,FileName); + DWORD LastError=GetLastError(); ErrHandler.SysErrMsg(); - ErrHandler.SetErrorCode(WARNING); + if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SetErrorCode(RARX_WARNING); } } -void SetPrivileges() +void SetACLPrivileges() { static bool InitDone=false; if (InitDone) return; + + if (SetPrivilege(SE_SECURITY_NAME)) + ReadSacl=true; + SetPrivilege(SE_RESTORE_NAME); + InitDone=true; +} - HANDLE hToken; - if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) - return; +bool SetPrivilege(LPCTSTR PrivName) +{ + bool Success=false; - TOKEN_PRIVILEGES tp; - tp.PrivilegeCount = 1; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + HANDLE hToken; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (LookupPrivilegeValue(NULL,SE_SECURITY_NAME,&tp.Privileges[0].Luid)) - if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) && + if (LookupPrivilegeValue(NULL,PrivName,&tp.Privileges[0].Luid) && + AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) && GetLastError() == ERROR_SUCCESS) - ReadSacl=true; + Success=true; - if (LookupPrivilegeValue(NULL,SE_RESTORE_NAME,&tp.Privileges[0].Luid)) - AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL); + CloseHandle(hToken); + } - CloseHandle(hToken); + return Success; } diff --git a/unrar/unrar/win32lnk.cpp b/unrar/unrar/win32lnk.cpp new file mode 100644 index 0000000..759c490 --- /dev/null +++ b/unrar/unrar/win32lnk.cpp @@ -0,0 +1,194 @@ +#define SYMLINK_FLAG_RELATIVE 1 + +typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + + + + +bool CreateReparsePoint(CommandData *Cmd,const wchar *Name,FileHeader *hd) +{ + static bool PrivSet=false; + if (!PrivSet) + { + SetPrivilege(SE_RESTORE_NAME); + // Not sure if we really need it, but let's request anyway. + SetPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME); + PrivSet=true; + } + + const DWORD BufSize=sizeof(REPARSE_DATA_BUFFER)+2*NM*sizeof(wchar)+1024; + Array<byte> Buf(BufSize); + REPARSE_DATA_BUFFER *rdb=(REPARSE_DATA_BUFFER *)&Buf[0]; + + wchar SubstName[NM]; + wcsncpyz(SubstName,hd->RedirName,ASIZE(SubstName)); + size_t SubstLength=wcslen(SubstName); + + wchar PrintName[NM],*PrintNameSrc=SubstName,*PrintNameDst=PrintName; + bool WinPrefix=wcsncmp(PrintNameSrc,L"\\??\\",4)==0; + if (WinPrefix) + PrintNameSrc+=4; + if (WinPrefix && wcsncmp(PrintNameSrc,L"UNC\\",4)==0) + { + *(PrintNameDst++)='\\'; // Insert second \ in beginning of share name. + PrintNameSrc+=3; + } + wcscpy(PrintNameDst,PrintNameSrc); + + size_t PrintLength=wcslen(PrintName); + + bool AbsPath=WinPrefix; + // IsFullPath is not really needed here, AbsPath check is enough. + // We added it just for extra safety, in case some Windows version would + // allow to create absolute targets with SYMLINK_FLAG_RELATIVE. + // Use hd->FileName instead of Name, since Name can include the destination + // path as a prefix, which can confuse IsRelativeSymlinkSafe algorithm. + if (!Cmd->AbsoluteLinks && (AbsPath || IsFullPath(hd->RedirName) || + !IsRelativeSymlinkSafe(Cmd,hd->FileName,Name,hd->RedirName))) + return false; + + CreatePath(Name,true,Cmd->DisableNames); + + // Overwrite prompt was already issued and confirmed earlier, so we can + // remove existing symlink or regular file here. PrepareToDelete was also + // called earlier inside of uiAskReplaceEx. + if (FileExist(Name)) + if (IsDir(GetFileAttr(Name))) + DelDir(Name); + else + DelFile(Name); + + // 'DirTarget' check is important for Unix symlinks to directories. + // Unix symlinks do not have their own 'directory' attribute. + if (hd->Dir || hd->DirTarget) + { + if (!CreateDirectory(Name,NULL)) + { + uiMsg(UIERROR_DIRCREATE,UINULL,Name); + ErrHandler.SetErrorCode(RARX_CREATE); + return false; + } + } + else + { + HANDLE hFile=CreateFile(Name,GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + ErrHandler.CreateErrorMsg(Name); + return false; + } + CloseHandle(hFile); + } + + + if (hd->RedirType==FSREDIR_JUNCTION) + { + rdb->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT; + rdb->ReparseDataLength=USHORT( + sizeof(rdb->MountPointReparseBuffer.SubstituteNameOffset)+ + sizeof(rdb->MountPointReparseBuffer.SubstituteNameLength)+ + sizeof(rdb->MountPointReparseBuffer.PrintNameOffset)+ + sizeof(rdb->MountPointReparseBuffer.PrintNameLength)+ + (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); + rdb->Reserved=0; + + rdb->MountPointReparseBuffer.SubstituteNameOffset=0; + rdb->MountPointReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer,SubstName); + + rdb->MountPointReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); + rdb->MountPointReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); + wcscpy(rdb->MountPointReparseBuffer.PathBuffer+SubstLength+1,PrintName); + } + else + if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_UNIXSYMLINK) + { + rdb->ReparseTag=IO_REPARSE_TAG_SYMLINK; + rdb->ReparseDataLength=USHORT( + sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset)+ + sizeof(rdb->SymbolicLinkReparseBuffer.SubstituteNameLength)+ + sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameOffset)+ + sizeof(rdb->SymbolicLinkReparseBuffer.PrintNameLength)+ + sizeof(rdb->SymbolicLinkReparseBuffer.Flags)+ + (SubstLength+1)*sizeof(WCHAR)+(PrintLength+1)*sizeof(WCHAR)); + rdb->Reserved=0; + + rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset=0; + rdb->SymbolicLinkReparseBuffer.SubstituteNameLength=USHORT(SubstLength*sizeof(WCHAR)); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer,SubstName); + + rdb->SymbolicLinkReparseBuffer.PrintNameOffset=USHORT((SubstLength+1)*sizeof(WCHAR)); + rdb->SymbolicLinkReparseBuffer.PrintNameLength=USHORT(PrintLength*sizeof(WCHAR)); + wcscpy(rdb->SymbolicLinkReparseBuffer.PathBuffer+SubstLength+1,PrintName); + + rdb->SymbolicLinkReparseBuffer.Flags=AbsPath ? 0:SYMLINK_FLAG_RELATIVE; + } + else + return false; + + HANDLE hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,0,NULL, + OPEN_EXISTING,FILE_FLAG_OPEN_REPARSE_POINT| + FILE_FLAG_BACKUP_SEMANTICS,NULL); + if (hFile==INVALID_HANDLE_VALUE) + { + ErrHandler.CreateErrorMsg(Name); + ErrHandler.SetErrorCode(RARX_CREATE); + return false; + } + + DWORD Returned; + if (!DeviceIoControl(hFile,FSCTL_SET_REPARSE_POINT,rdb, + FIELD_OFFSET(REPARSE_DATA_BUFFER,GenericReparseBuffer)+ + rdb->ReparseDataLength,NULL,0,&Returned,NULL)) + { + CloseHandle(hFile); + uiMsg(UIERROR_SLINKCREATE,UINULL,Name); + + DWORD LastError=GetLastError(); + if ((LastError==ERROR_ACCESS_DENIED || LastError==ERROR_PRIVILEGE_NOT_HELD) && + !IsUserAdmin()) + uiMsg(UIERROR_NEEDADMIN); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + + if (hd->Dir) + RemoveDirectory(Name); + else + DeleteFile(Name); + return false; + } + File LinkFile; + LinkFile.SetHandle(hFile); + LinkFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&hd->mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&hd->ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&hd->atime); + LinkFile.Close(); + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(Name,hd->FileAttr); + return true; +} diff --git a/unrar/unrar/win32stm.cpp b/unrar/unrar/win32stm.cpp index 7909291..3b77d2a 100644 --- a/unrar/unrar/win32stm.cpp +++ b/unrar/unrar/win32stm.cpp @@ -1,154 +1,184 @@ - -#ifndef SFX_MODULE -void ExtractStreams(Archive &Arc,char *FileName,wchar *FileNameW) +#ifdef _WIN_ALL +// StreamName must include the leading ':'. +static bool IsNtfsReservedStream(const wchar *StreamName) { - if (!WinNT()) - return; + const wchar *Reserved[]{ + L"::$ATTRIBUTE_LIST",L"::$BITMAP",L"::$DATA",L"::$EA",L"::$EA_INFORMATION", + L"::$FILE_NAME",L"::$INDEX_ALLOCATION",L":$I30:$INDEX_ALLOCATION", + L"::$INDEX_ROOT",L"::$LOGGED_UTILITY_STREAM",L":$EFS:$LOGGED_UTILITY_STREAM", + L":$TXF_DATA:$LOGGED_UTILITY_STREAM",L"::$OBJECT_ID",L"::$REPARSE_POINT" + }; + for (const wchar *Name : Reserved) + if (wcsicomp(StreamName,Name)==0) + return true; + return false; +} +#endif + - if (Arc.HeaderCRC!=Arc.StreamHead.HeadCRC) +#if !defined(SFX_MODULE) && defined(_WIN_ALL) +void ExtractStreams20(Archive &Arc,const wchar *FileName) +{ + if (Arc.BrokenHeader) { -#ifndef SILENT - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>PACK_VER) + if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>VER_PACK) { -#ifndef SILENT - Log(Arc.FileName,St(MStreamUnknown),FileName); -#endif - ErrHandler.SetErrorCode(WARNING); + uiMsg(UIERROR_STREAMUNKNOWN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); return; } - char StreamName[NM+2]; + wchar StreamName[NM+2]; if (FileName[0]!=0 && FileName[1]==0) { - strcpy(StreamName,".\\"); - strcpy(StreamName+2,FileName); + // Convert single character names like f:stream to .\f:stream to + // resolve the ambiguity with drive letters. + wcsncpyz(StreamName,L".\\",ASIZE(StreamName)); + wcsncatz(StreamName,FileName,ASIZE(StreamName)); } else - strcpy(StreamName,FileName); - if (strlen(StreamName)+strlen((char *)Arc.StreamHead.StreamName)>=sizeof(StreamName) || + wcsncpyz(StreamName,FileName,ASIZE(StreamName)); + if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) || Arc.StreamHead.StreamName[0]!=':') { -#ifndef SILENT - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - ConvertPath((char *)Arc.StreamHead.StreamName+1,(char *)Arc.StreamHead.StreamName+1); + wchar StoredName[NM]; + CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName)); + ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1); + - strcat(StreamName,(char *)Arc.StreamHead.StreamName); + if (IsNtfsReservedStream(StoredName)) + return; + + wcsncatz(StreamName,StoredName,ASIZE(StreamName)); FindData fd; - bool Found=FindFile::FastFind(FileName,FileNameW,&fd); + bool Found=FindFile::FastFind(FileName,&fd); - if (fd.FileAttr & FILE_ATTRIBUTE_READONLY) - SetFileAttr(FileName,FileNameW,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; if (CurFile.WCreate(StreamName)) { ComprDataIO DataIO; Unpack Unpack(&DataIO); - Unpack.Init(); + Unpack.Init(0x10000,false); - Array<unsigned char> UnpData(Arc.StreamHead.UnpSize); DataIO.SetPackedSizeToRead(Arc.StreamHead.DataSize); DataIO.EnableShowProgress(false); DataIO.SetFiles(&Arc,&CurFile); + DataIO.UnpHash.Init(HASH_CRC32,1); Unpack.SetDestSize(Arc.StreamHead.UnpSize); Unpack.DoUnpack(Arc.StreamHead.UnpVer,false); - if (Arc.StreamHead.StreamCRC!=~DataIO.UnpFileCRC) + if (Arc.StreamHead.StreamCRC!=DataIO.UnpHash.GetCRC32()) { -#ifndef SILENT - Log(Arc.FileName,St(MStreamBroken),StreamName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,StreamName); + ErrHandler.SetErrorCode(RARX_CRC); } else CurFile.Close(); } File HostFile; - if (Found && HostFile.Open(FileName,FileNameW,true,true)) + if (Found && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); - if (fd.FileAttr & FILE_ATTRIBUTE_READONLY) - SetFileAttr(FileName,FileNameW,fd.FileAttr); + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr); } #endif -void ExtractStreamsNew(Archive &Arc,char *FileName,wchar *FileNameW) +#ifdef _WIN_ALL +void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode) { - if (!WinNT()) - return; - - wchar NameW[NM]; - if (FileNameW!=NULL && *FileNameW!=0) - strcpyw(NameW,FileNameW); - else - CharToWide(FileName,NameW); - wchar StreamNameW[NM+2]; - if (NameW[0]!=0 && NameW[1]==0) + wchar FullName[NM+2]; + if (FileName[0]!=0 && FileName[1]==0) { - strcpyw(StreamNameW,L".\\"); - strcpyw(StreamNameW+2,NameW); + // Convert single character names like f:stream to .\f:stream to + // resolve the ambiguity with drive letters. + wcsncpyz(FullName,L".\\",ASIZE(FullName)); + wcsncatz(FullName,FileName,ASIZE(FullName)); } else - strcpyw(StreamNameW,NameW); - - wchar *DestName=StreamNameW+strlenw(StreamNameW); - byte *SrcName=&Arc.SubHead.SubData[0]; - int DestSize=Arc.SubHead.SubData.Size()/2; + wcsncpyz(FullName,FileName,ASIZE(FullName)); - if (strlenw(StreamNameW)+DestSize>=sizeof(StreamNameW)/sizeof(StreamNameW[0])) + wchar StreamName[NM]; + GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); + if (*StreamName!=':') { -#if !defined(SILENT) && !defined(SFX_MODULE) - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); + ErrHandler.SetErrorCode(RARX_CRC); return; } - RawToWide(SrcName,DestName,DestSize); - DestName[DestSize]=0; - - if (*DestName!=':') + if (TestMode) { -#if !defined(SILENT) && !defined(SFX_MODULE) - Log(Arc.FileName,St(MStreamBroken),FileName); -#endif - ErrHandler.SetErrorCode(CRC_ERROR); + File CurFile; + Arc.ReadSubData(NULL,&CurFile,true); return; } - ConvertPath(DestName+1,DestName+1); + wcsncatz(FullName,StreamName,ASIZE(FullName)); + + + if (IsNtfsReservedStream(StreamName)) + return; FindData fd; - bool Found=FindFile::FastFind(FileName,FileNameW,&fd); + bool HostFound=FindFile::FastFind(FileName,&fd); - if (fd.FileAttr & FILE_ATTRIBUTE_READONLY) - SetFileAttr(FileName,FileNameW,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); - char StreamName[NM]; - WideToChar(StreamNameW,StreamName); + if ((fd.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) + SetFileAttr(FileName,fd.FileAttr & ~FILE_ATTRIBUTE_READONLY); File CurFile; - if (CurFile.WCreate(StreamName,StreamNameW) && Arc.ReadSubData(NULL,&CurFile)) - CurFile.Close(); + + if (CurFile.WCreate(FullName)) + { + if (Arc.ReadSubData(NULL,&CurFile,false)) + CurFile.Close(); + } + + // Restoring original file timestamps. File HostFile; - if (Found && HostFile.Open(FileName,FileNameW,true,true)) + if (HostFound && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) SetFileTime(HostFile.GetHandle(),&fd.ftCreationTime,&fd.ftLastAccessTime, &fd.ftLastWriteTime); // Restoring original file attributes. Important if file was read only // or did not have "Archive" attribute - SetFileAttr(FileName,FileNameW,fd.FileAttr); + SetFileAttr(FileName,fd.FileAttr); +} +#endif + + +void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize) +{ + byte *Data=&Arc.SubHead.SubData[0]; + size_t DataSize=Arc.SubHead.SubData.Size(); + if (Arc.Format==RARFMT15) + { + size_t DestSize=Min(DataSize/2,MaxSize-1); + RawToWide(Data,StreamName,DestSize); + StreamName[DestSize]=0; + } + else + { + char UtfString[NM*4]; + size_t DestSize=Min(DataSize,ASIZE(UtfString)-1); + memcpy(UtfString,Data,DestSize); + UtfString[DestSize]=0; + UtfToWide(UtfString,StreamName,MaxSize); + } } |
