Advertisement
aveyo

build_ambient_sound_qol_dota_mod

Apr 27th, 2019
934
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Batch 95.01 KB | None | 0 0
  1. /* 2>nul || @title VPKMOD by AveYo v2019.04.27 - ambient sound qol request
  2. @echo off & pushd %~dp0
  3.  
  4. :: Export bundled Source?Replacement pair list used by VPKMOD tool to generate the modded DOTA 2 pak01_dir.vpk  
  5.  >Mod.lst cd.
  6. ::------------------------------------------------------------------------------------------------------------------------------
  7. >>Mod.lst echo/sounds/ambient/ambient.vsnd_c?sounds/null.vsnd_c
  8. >>Mod.lst echo/sounds/ambient/morning.vsnd_c?sounds/null.vsnd_c
  9. >>Mod.lst echo/sounds/ambient/morning_cavern.vsnd_c?sounds/null.vsnd_c
  10. >>Mod.lst echo/sounds/ambient/morning_reef.vsnd_c?sounds/null.vsnd_c
  11. >>Mod.lst echo/sounds/ambient/night.vsnd_c?sounds/null.vsnd_c
  12. >>Mod.lst echo/sounds/ambient/night_cavern.vsnd_c?sounds/null.vsnd_c
  13. >>Mod.lst echo/sounds/ambient/night_reef.vsnd_c?sounds/null.vsnd_c
  14. >>Mod.lst echo/sounds/ambient/soundscapes/abyss01.vsnd_c?sounds/null.vsnd_c
  15. >>Mod.lst echo/sounds/ambient/soundscapes/abyss02.vsnd_c?sounds/null.vsnd_c
  16. >>Mod.lst echo/sounds/ambient/soundscapes/abyss03.vsnd_c?sounds/null.vsnd_c
  17. >>Mod.lst echo/sounds/ambient/soundscapes/abyss04.vsnd_c?sounds/null.vsnd_c
  18. >>Mod.lst echo/sounds/ambient/soundscapes/bats01.vsnd_c?sounds/null.vsnd_c
  19. >>Mod.lst echo/sounds/ambient/soundscapes/bats02.vsnd_c?sounds/null.vsnd_c
  20. >>Mod.lst echo/sounds/ambient/soundscapes/bats03.vsnd_c?sounds/null.vsnd_c
  21. >>Mod.lst echo/sounds/ambient/soundscapes/birds01.vsnd_c?sounds/null.vsnd_c
  22. >>Mod.lst echo/sounds/ambient/soundscapes/birds02.vsnd_c?sounds/null.vsnd_c
  23. >>Mod.lst echo/sounds/ambient/soundscapes/birds03.vsnd_c?sounds/null.vsnd_c
  24. >>Mod.lst echo/sounds/ambient/soundscapes/breeze01.vsnd_c?sounds/null.vsnd_c
  25. >>Mod.lst echo/sounds/ambient/soundscapes/breeze02.vsnd_c?sounds/null.vsnd_c
  26. >>Mod.lst echo/sounds/ambient/soundscapes/breeze03.vsnd_c?sounds/null.vsnd_c
  27. >>Mod.lst echo/sounds/ambient/soundscapes/cavern/drill.vsnd_c?sounds/null.vsnd_c
  28. >>Mod.lst echo/sounds/ambient/soundscapes/cavern/lp/day.vsnd_c?sounds/null.vsnd_c
  29. >>Mod.lst echo/sounds/ambient/soundscapes/cavern/lp/night.vsnd_c?sounds/null.vsnd_c
  30. >>Mod.lst echo/sounds/ambient/soundscapes/cetaceans01.vsnd_c?sounds/null.vsnd_c
  31. >>Mod.lst echo/sounds/ambient/soundscapes/cetaceans02.vsnd_c?sounds/null.vsnd_c
  32. >>Mod.lst echo/sounds/ambient/soundscapes/cetaceans03.vsnd_c?sounds/null.vsnd_c
  33. >>Mod.lst echo/sounds/ambient/soundscapes/cetaceans04.vsnd_c?sounds/null.vsnd_c
  34. >>Mod.lst echo/sounds/ambient/soundscapes/cetaceans05.vsnd_c?sounds/null.vsnd_c
  35. >>Mod.lst echo/sounds/ambient/soundscapes/cetaceans06.vsnd_c?sounds/null.vsnd_c
  36. >>Mod.lst echo/sounds/ambient/soundscapes/darkforest_drone.vsnd_c?sounds/null.vsnd_c
  37. >>Mod.lst echo/sounds/ambient/soundscapes/deathmaze_drone.vsnd_c?sounds/null.vsnd_c
  38. >>Mod.lst echo/sounds/ambient/soundscapes/deathmaze_lp01.vsnd_c?sounds/null.vsnd_c
  39. >>Mod.lst echo/sounds/ambient/soundscapes/deathmaze_lp02.vsnd_c?sounds/null.vsnd_c
  40. >>Mod.lst echo/sounds/ambient/soundscapes/default_day_lp.vsnd_c?sounds/null.vsnd_c
  41. >>Mod.lst echo/sounds/ambient/soundscapes/default_night_lp.vsnd_c?sounds/null.vsnd_c
  42. >>Mod.lst echo/sounds/ambient/soundscapes/dire_ancient_lp_01.vsnd_c?sounds/null.vsnd_c
  43. >>Mod.lst echo/sounds/ambient/soundscapes/dire_base_loop_01.vsnd_c?sounds/null.vsnd_c
  44. >>Mod.lst echo/sounds/ambient/soundscapes/dire_pit_loop_01_l.vsnd_c?sounds/null.vsnd_c
  45. >>Mod.lst echo/sounds/ambient/soundscapes/dire_random_unstableness_01.vsnd_c?sounds/null.vsnd_c
  46. >>Mod.lst echo/sounds/ambient/soundscapes/dire_random_unstableness_02.vsnd_c?sounds/null.vsnd_c
  47. >>Mod.lst echo/sounds/ambient/soundscapes/dire_random_unstableness_03.vsnd_c?sounds/null.vsnd_c
  48. >>Mod.lst echo/sounds/ambient/soundscapes/dire_random_unstableness_04.vsnd_c?sounds/null.vsnd_c
  49. >>Mod.lst echo/sounds/ambient/soundscapes/dire_spawn_loop_01.vsnd_c?sounds/null.vsnd_c
  50. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_bat_01.vsnd_c?sounds/null.vsnd_c
  51. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_bat_02.vsnd_c?sounds/null.vsnd_c
  52. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_bat_03.vsnd_c?sounds/null.vsnd_c
  53. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_batflaps_01.vsnd_c?sounds/null.vsnd_c
  54. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_batflaps_02.vsnd_c?sounds/null.vsnd_c
  55. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_batflaps_03.vsnd_c?sounds/null.vsnd_c
  56. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_batflaps_04.vsnd_c?sounds/null.vsnd_c
  57. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_batflaps_05.vsnd_c?sounds/null.vsnd_c
  58. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_owl_01.vsnd_c?sounds/null.vsnd_c
  59. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_owl_02.vsnd_c?sounds/null.vsnd_c
  60. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_owl_03.vsnd_c?sounds/null.vsnd_c
  61. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_spirit_vx_01.vsnd_c?sounds/null.vsnd_c
  62. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_spirit_vx_02.vsnd_c?sounds/null.vsnd_c
  63. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_spirit_vx_03.vsnd_c?sounds/null.vsnd_c
  64. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_spirit_vx_04.vsnd_c?sounds/null.vsnd_c
  65. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_spirit_vx_05.vsnd_c?sounds/null.vsnd_c
  66. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_wolf_01.vsnd_c?sounds/null.vsnd_c
  67. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_wolf_02.vsnd_c?sounds/null.vsnd_c
  68. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_wolf_03.vsnd_c?sounds/null.vsnd_c
  69. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/diretide_wolf_04.vsnd_c?sounds/null.vsnd_c
  70. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/dt_blood_river_lp.vsnd_c?sounds/null.vsnd_c
  71. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/dt_leaves_rustle01.vsnd_c?sounds/null.vsnd_c
  72. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/dt_leaves_rustle02.vsnd_c?sounds/null.vsnd_c
  73. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/dt_leaves_rustle03.vsnd_c?sounds/null.vsnd_c
  74. >>Mod.lst echo/sounds/ambient/soundscapes/diretide/dt_radiant_day_lp.vsnd_c?sounds/null.vsnd_c
  75. >>Mod.lst echo/sounds/ambient/soundscapes/echo_clicks01.vsnd_c?sounds/null.vsnd_c
  76. >>Mod.lst echo/sounds/ambient/soundscapes/echo_clicks02.vsnd_c?sounds/null.vsnd_c
  77. >>Mod.lst echo/sounds/ambient/soundscapes/echo_clicks03.vsnd_c?sounds/null.vsnd_c
  78. >>Mod.lst echo/sounds/ambient/soundscapes/echo_clicks04.vsnd_c?sounds/null.vsnd_c
  79. >>Mod.lst echo/sounds/ambient/soundscapes/echo_clicks05.vsnd_c?sounds/null.vsnd_c
  80. ::>>Mod.lst echo/sounds/ambient/soundscapes/heavy_rain_lp.vsnd_c?sounds/null.vsnd_c
  81. ::>>Mod.lst echo/sounds/ambient/soundscapes/light_rain_lp.vsnd_c?sounds/null.vsnd_c
  82. >>Mod.lst echo/sounds/ambient/soundscapes/magic_dire_int01.vsnd_c?sounds/null.vsnd_c
  83. >>Mod.lst echo/sounds/ambient/soundscapes/magic_dire_int02.vsnd_c?sounds/null.vsnd_c
  84. >>Mod.lst echo/sounds/ambient/soundscapes/magic_dire_int03.vsnd_c?sounds/null.vsnd_c
  85. >>Mod.lst echo/sounds/ambient/soundscapes/magic_dire_lp.vsnd_c?sounds/null.vsnd_c
  86. >>Mod.lst echo/sounds/ambient/soundscapes/magic_radiant_int01.vsnd_c?sounds/null.vsnd_c
  87. >>Mod.lst echo/sounds/ambient/soundscapes/magic_radiant_int02.vsnd_c?sounds/null.vsnd_c
  88. >>Mod.lst echo/sounds/ambient/soundscapes/magic_radiant_int03.vsnd_c?sounds/null.vsnd_c
  89. >>Mod.lst echo/sounds/ambient/soundscapes/magic_radiant_int04.vsnd_c?sounds/null.vsnd_c
  90. >>Mod.lst echo/sounds/ambient/soundscapes/magic_radiant_int05.vsnd_c?sounds/null.vsnd_c
  91. >>Mod.lst echo/sounds/ambient/soundscapes/magic_radiant_int06.vsnd_c?sounds/null.vsnd_c
  92. >>Mod.lst echo/sounds/ambient/soundscapes/magic_radiant_lp.vsnd_c?sounds/null.vsnd_c
  93. >>Mod.lst echo/sounds/ambient/soundscapes/radiant_ancient_lp_01.vsnd_c?sounds/null.vsnd_c
  94. >>Mod.lst echo/sounds/ambient/soundscapes/radiant_day_base_loop_01.vsnd_c?sounds/null.vsnd_c
  95. >>Mod.lst echo/sounds/ambient/soundscapes/radiant_night_base_loop_01.vsnd_c?sounds/null.vsnd_c
  96. >>Mod.lst echo/sounds/ambient/soundscapes/radiant_spawn_loop_02.vsnd_c?sounds/null.vsnd_c
  97. ::>>Mod.lst echo/sounds/ambient/soundscapes/rain_metal_lp.vsnd_c?sounds/null.vsnd_c
  98. ::>>Mod.lst echo/sounds/ambient/soundscapes/rain_thunder01.vsnd_c?sounds/null.vsnd_c
  99. ::>>Mod.lst echo/sounds/ambient/soundscapes/rain_thunder02.vsnd_c?sounds/null.vsnd_c
  100. ::>>Mod.lst echo/sounds/ambient/soundscapes/rain_thunder03.vsnd_c?sounds/null.vsnd_c
  101. ::>>Mod.lst echo/sounds/ambient/soundscapes/rain_thunder04.vsnd_c?sounds/null.vsnd_c
  102. ::>>Mod.lst echo/sounds/ambient/soundscapes/rain_thunder05.vsnd_c?sounds/null.vsnd_c
  103. >>Mod.lst echo/sounds/ambient/soundscapes/reef_day_lp.vsnd_c?sounds/null.vsnd_c
  104. >>Mod.lst echo/sounds/ambient/soundscapes/reef_night_lp.vsnd_c?sounds/null.vsnd_c
  105. >>Mod.lst echo/sounds/ambient/soundscapes/reefs_edge_lp.vsnd_c?sounds/null.vsnd_c
  106. >>Mod.lst echo/sounds/ambient/soundscapes/river_light_lp_03.vsnd_c?sounds/null.vsnd_c
  107. >>Mod.lst echo/sounds/ambient/soundscapes/river_med_lp_01.vsnd_c?sounds/null.vsnd_c
  108. >>Mod.lst echo/sounds/ambient/soundscapes/snowy_gust01.vsnd_c?sounds/null.vsnd_c
  109. >>Mod.lst echo/sounds/ambient/soundscapes/snowy_gust02.vsnd_c?sounds/null.vsnd_c
  110. >>Mod.lst echo/sounds/ambient/soundscapes/snowy_gust03.vsnd_c?sounds/null.vsnd_c
  111. >>Mod.lst echo/sounds/ambient/soundscapes/snowy_gust04.vsnd_c?sounds/null.vsnd_c
  112. >>Mod.lst echo/sounds/ambient/soundscapes/snowy_wind_lp.vsnd_c?sounds/null.vsnd_c
  113. >>Mod.lst echo/sounds/ambient/soundscapes/summer/bees01.vsnd_c?sounds/null.vsnd_c
  114. >>Mod.lst echo/sounds/ambient/soundscapes/summer/bees02.vsnd_c?sounds/null.vsnd_c
  115. >>Mod.lst echo/sounds/ambient/soundscapes/summer/bees03.vsnd_c?sounds/null.vsnd_c
  116. >>Mod.lst echo/sounds/ambient/soundscapes/summer/cicada01.vsnd_c?sounds/null.vsnd_c
  117. >>Mod.lst echo/sounds/ambient/soundscapes/summer/cicada02.vsnd_c?sounds/null.vsnd_c
  118. >>Mod.lst echo/sounds/ambient/soundscapes/summer/cicada03.vsnd_c?sounds/null.vsnd_c
  119. >>Mod.lst echo/sounds/ambient/soundscapes/summer/night_fauna_lp.vsnd_c?sounds/null.vsnd_c
  120. >>Mod.lst echo/sounds/ambient/soundscapes/temple_drone.vsnd_c?sounds/null.vsnd_c
  121. >>Mod.lst echo/sounds/ambient/soundscapes/temple_lp.vsnd_c?sounds/null.vsnd_c
  122. >>Mod.lst echo/sounds/ambient/soundscapes/thunder_dist_01.vsnd_c?sounds/null.vsnd_c
  123. >>Mod.lst echo/sounds/ambient/soundscapes/thunder_dist_02.vsnd_c?sounds/null.vsnd_c
  124. >>Mod.lst echo/sounds/ambient/soundscapes/thunder_dist_03.vsnd_c?sounds/null.vsnd_c
  125. >>Mod.lst echo/sounds/ambient/soundscapes/thunder_dist_04.vsnd_c?sounds/null.vsnd_c
  126. >>Mod.lst echo/sounds/ambient/soundscapes/thunder_dist_05.vsnd_c?sounds/null.vsnd_c
  127. >>Mod.lst echo/sounds/ambient/soundscapes/waterfall_loop_01.vsnd_c?sounds/null.vsnd_c
  128. >>Mod.lst echo/sounds/ambient/soundscapes/waterfowl_01.vsnd_c?sounds/null.vsnd_c
  129. >>Mod.lst echo/sounds/ambient/soundscapes/waterfowl_02.vsnd_c?sounds/null.vsnd_c
  130. >>Mod.lst echo/sounds/ambient/soundscapes/waterfowl_03.vsnd_c?sounds/null.vsnd_c
  131. >>Mod.lst echo/sounds/ambient/soundscapes/waterfowl_04.vsnd_c?sounds/null.vsnd_c
  132. >>Mod.lst echo/sounds/ambient/soundscapes/waves_close_lp_1.vsnd_c?sounds/null.vsnd_c
  133. >>Mod.lst echo/sounds/ambient/soundscapes/waves_coast_lp_1.vsnd_c?sounds/null.vsnd_c
  134. >>Mod.lst echo/sounds/ambient/soundscapes/waves_coast_lp_2.vsnd_c?sounds/null.vsnd_c
  135. >>Mod.lst echo/sounds/ambient/soundscapes/winter_lp.vsnd_c?sounds/null.vsnd_c
  136. >>Mod.lst echo/sounds/ambient/special/brood_web_loop.vsnd_c?sounds/null.vsnd_c
  137. >>Mod.lst echo/sounds/ambient/test/disquiet01.vsnd_c?sounds/null.vsnd_c
  138. >>Mod.lst echo/sounds/ambient/test/disquiet02.vsnd_c?sounds/null.vsnd_c
  139. >>Mod.lst echo/sounds/ambient/test/disquiet03.vsnd_c?sounds/null.vsnd_c
  140. >>Mod.lst echo/sounds/ambient/test/icy_wind01.vsnd_c?sounds/null.vsnd_c
  141. >>Mod.lst echo/sounds/ambient/test/radiant_loop.vsnd_c?sounds/null.vsnd_c
  142. ::------------------------------------------------------------------------------------------------------------------------------
  143.  
  144. :: Detect DOTA 2 path
  145. call :reg_query STEAMPATH "HKCU\SOFTWARE\Valve\Steam" "SteamPath"
  146. set "STEAMDATA=" & if defined STEAMPATH for %%# in ("%STEAMPATH%") do set "STEAMPATH=%%~dpnx#"
  147. if not exist "%STEAMPATH%\Steam.exe" call :end ! Cannot find SteamPath in registry
  148. call :reg_query ACTIVEUSER "HKCU\SOFTWARE\Valve\Steam\ActiveProcess" "ActiveUser" & set/a "STEAMID=ACTIVEUSER" >nul 2>nul
  149. if exist "%STEAMPATH%\userdata\%STEAMID%\config\localconfig.vdf" set "STEAMDATA=%STEAMPATH%\userdata\%STEAMID%"
  150. if not defined STEAMDATA for /f "delims=" %%# in ('dir "%STEAMPATH%\userdata" /b/o:d/t:w/s 2^>nul') do set "ACTIVEUSER=%%~dp#"
  151. if not defined STEAMDATA for /f "delims=\" %%# in ("%ACTIVEUSER:*\userdata\=%") do set "STEAMID=%%#"
  152. if exist "%STEAMPATH%\userdata\%STEAMID%\config\localconfig.vdf" set "STEAMDATA=%STEAMPATH%\userdata\%STEAMID%"
  153. set "libfilter=LibraryFolders { TimeNextStatsReport ContentStatsID }"
  154. if not exist "%STEAMPATH%\SteamApps\libraryfolders.vdf" call :end ! Cannot find "%STEAMPATH%\SteamApps\libraryfolders.vdf"
  155. for /f usebackq^ delims^=^"^ tokens^=4 %%s in (`findstr /v "%libfilter%" "%STEAMPATH%\SteamApps\libraryfolders.vdf"`) do (
  156. if exist "%%s\steamapps\appmanifest_570.acf" if exist "%%s\steamapps\common\dota 2 beta\game\dota\dota.fgd" set "libfs=%%s")
  157. set "STEAMAPPS=%STEAMPATH%\steamapps" & if defined libfs set "STEAMAPPS=%libfs:\\=\%\steamapps"
  158. if not exist "%STEAMAPPS%\common\dota 2 beta\game\dota\maps\dota.vpk" call :end ! Missing "%STEAMAPPS%\common\dota 2 beta\game"
  159. set "DOTA=%STEAMAPPS%\common\dota 2 beta\game" & set "CONTENT=%STEAMAPPS%\common\dota 2 beta\content"
  160.  
  161. :: Compile VPKMOD tool if not already present (needs net framework 3.5+ or 4.0+ on Windows 7)
  162. for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%Windir%\Microsoft.NET\Framework\*csc.exe"') do set "csc=%%v"
  163. if not exist vpkmod.exe "%csc%" /out:vpkmod.exe /target:exe /platform:anycpu /optimize /nologo "%~f0"
  164. if not exist vpkmod.exe echo [ERROR] Failed compiling c# snippet! .net framework 3.5+ compiler needed &timeout /t -1 & exit /b
  165.  
  166. : Clear previous mod
  167. del /f /q "%DOTA%\dota_MOD\pak01_dir.vpk" >nul 2>nul
  168.  
  169. : Force-close dota2.exe
  170. taskkill /f /im dota2.exe /t >nul 2>nul & timeout /t 2 >nul
  171.  
  172. :: In-memory file replacement mod using nothing but unaltered Valve authored files (custom vpkmod tool exclusive feature)
  173. vpkmod -i "%DOTA%\dota\pak01_dir.vpk" -o "%DOTA%\dota_MOD\pak01_dir.vpk" -m "Mod.lst" &rem -s
  174.  
  175. : Done!
  176. if not exist "%DOTA%\dota_MOD\pak01_dir.vpk" (echo ERROR :^() else echo SUCCESS! To use, add launch option: -language MOD
  177. timeout /t -1
  178. exit/b
  179.  
  180. :reg_query [USAGE] call :reg_query ResultVar "HKCU\KeyName" "ValueName"
  181. (for /f "skip=2 delims=" %%s in ('reg query "%~2" /v "%~3" /z 2^>nul') do set ".=%%s" & call set "%~1=%%.:*)    =%%") & exit/b
  182.  
  183. :end Message[prefix with ! to signal failure]
  184. (echo. & echo %* & if "%~1"=="!" color 0c) & pause>nul & exit
  185.  
  186. exit/b VPKMOD C# source */
  187. using System;
  188. using System.Collections.Generic;
  189. using System.Diagnostics;
  190. using System.Globalization;
  191. using System.IO;
  192. using System.Linq;
  193. using System.Reflection;
  194. using System.Runtime.Serialization;
  195. using System.Security.Cryptography;
  196. using System.Security.Permissions;
  197. using System.Text;
  198. using SteamDatabase.ValvePak;
  199. using VPKMOD;
  200.  
  201. [assembly:AssemblyTitle("VPKMOD")]
  202. [assembly:AssemblyCompanyAttribute("AveYo")]
  203. [assembly:AssemblyCopyright("AveYo / SteamDatabase")]
  204. [assembly:AssemblyVersionAttribute("2019.03.19")]
  205.  
  206. namespace SteamDatabase.ValvePak
  207. {
  208.     // [assembly: AssemblyTitle("Valve Pak Library")]
  209.     // [assembly: AssemblyDescription("Library to work with Valve Pak archives")]
  210.     // [assembly: AssemblyCompany("SteamDatabase")]
  211.     // [assembly: AssemblyProduct("ValvePak")]
  212.     // [assembly: AssemblyCopyright("Copyright ┬« SteamDatabase 2016")]
  213.     // <auto-generated/>
  214.     // Not actually auto generated, but this makes StyleCop ignore this file
  215.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  216.     // PackageEntry.cs
  217.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  218.     public class PackageEntry
  219.     {
  220.         /// <summary>
  221.         /// Gets or sets file name of this entry.
  222.         /// </summary>
  223.         public string FileName { get; set; }
  224.  
  225.         /// <summary>
  226.         /// Gets or sets the name of the directory this file is in.
  227.         /// '/' is always used as a dictionary separator in Valve's implementation.
  228.         /// Directory names are also always lower cased in Valve's implementation.
  229.         /// </summary>
  230.         public string DirectoryName { get; set; }
  231.  
  232.         /// <summary>
  233.         /// Gets or sets the file extension.
  234.         /// If the file has no extension, this is an empty string.
  235.         /// </summary>
  236.         public string TypeName { get; set; }
  237.  
  238.         /// <summary>
  239.         /// Gets or sets the CRC32 checksum of this entry.
  240.         /// </summary>
  241.         public uint CRC32 { get; set; }
  242.  
  243.         /// <summary>
  244.         /// Gets or sets the length in bytes.
  245.         /// </summary>
  246.         public uint Length { get; set; }
  247.  
  248.         /// <summary>
  249.         /// Gets or sets the offset in the package.
  250.         /// </summary>
  251.         public uint Offset { get; set; }
  252.  
  253.         /// <summary>
  254.         /// Gets or sets which archive this entry is in.
  255.         /// </summary>
  256.         public ushort ArchiveIndex { get; set; }
  257.  
  258.         /// <summary>
  259.         /// Gets the length in bytes by adding Length and length of SmallData.
  260.         /// </summary>
  261.         public uint TotalLength
  262.         {
  263.             get
  264.             {
  265.                 var totalLength = Length;
  266.  
  267.                 if (SmallData != null)
  268.                 {
  269.                     totalLength += (uint)SmallData.Length;
  270.                 }
  271.  
  272.                 return totalLength;
  273.             }
  274.         }
  275.  
  276.         /// <summary>
  277.         /// Gets or sets the preloaded bytes.
  278.         /// </summary>
  279.         public byte[] SmallData { get; set; }
  280.  
  281.         /// <summary>
  282.         /// Returns the file name and extension.
  283.         /// </summary>
  284.         /// <returns>File name and extension.</returns>
  285.         public string GetFileName()
  286.         {
  287.             var fileName = FileName;
  288.  
  289.             if (TypeName != string.Empty)
  290.             {
  291.                 fileName += "." + TypeName;
  292.             }
  293.  
  294.             return fileName;
  295.         }
  296.  
  297.         /// <summary>
  298.         /// Returns the absolute path of the file in the package.
  299.         /// </summary>
  300.         /// <returns>Absolute path.</returns>
  301.         public string GetFullPath()
  302.         {
  303.             if (DirectoryName == null)
  304.             {
  305.                 return GetFileName();
  306.             }
  307.  
  308.             return DirectoryName + Package.DirectorySeparatorChar + GetFileName();
  309.         }
  310.  
  311.         public override string ToString()
  312.         {
  313.             //return $"{GetFullPath()} crc=0x{CRC32:x2} metadatasz={SmallData.Length} fnumber={ArchiveIndex} ofs=0x{Offset:x2} sz={Length}";
  314.             return String.Format("{0} crc=0x{1:x2} metadatasz={2} fnumber={3} ofs=0x{4:x2} sz={5}", GetFullPath(), CRC32, SmallData.Length, ArchiveIndex, Offset, Length);
  315.             //return ""+GetFullPath()+" crc=0x"+CRC32+" metadatasz="+SmallData.Length+" fnumber="+ArchiveIndex+" ofs=0x"+Offset+" sz="+Length;
  316.         }
  317.     }
  318.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  319.     // ArchiveMD5SectionEntry.cs
  320.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  321.     /// <summary>
  322.     /// VPK_ArchiveMD5SectionEntry
  323.     /// </summary>
  324.     public class ArchiveMD5SectionEntry
  325.     {
  326.         /// <summary>
  327.         /// Gets or sets the CRC32 checksum of this entry.
  328.         /// </summary>
  329.         public uint ArchiveIndex { get; set; }
  330.  
  331.         /// <summary>
  332.         /// Gets or sets the offset in the package.
  333.         /// </summary>
  334.         public uint Offset { get; set; }
  335.  
  336.         /// <summary>
  337.         /// Gets or sets the length in bytes.
  338.         /// </summary>
  339.         public uint Length { get; set; }
  340.  
  341.         /// <summary>
  342.         /// Gets or sets the expected Checksum checksum.
  343.         /// </summary>
  344.         public byte[] Checksum { get; set; }
  345.     }
  346.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  347.     // StreamHelpers.cs
  348.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  349.     internal static class StreamHelpers
  350.     {
  351.         /// <summary>
  352.         /// Reads a null terminated string.
  353.         /// </summary>
  354.         /// <returns>String.</returns>
  355.         /// <param name="stream">Stream.</param>
  356.         /// <param name="encoding">Encoding.</param>
  357.         public static string ReadNullTermString(this BinaryReader stream, Encoding encoding)
  358.         {
  359.             var characterSize = encoding.GetByteCount("e");
  360.  
  361.             using (var ms = new MemoryStream())
  362.             {
  363.                 while (true)
  364.                 {
  365.                     var data = new byte[characterSize];
  366.                     stream.Read(data, 0, characterSize);
  367.  
  368.                     if (encoding.GetString(data, 0, characterSize) == "\0")
  369.                     {
  370.                         break;
  371.                     }
  372.  
  373.                     ms.Write(data, 0, data.Length);
  374.                 }
  375.  
  376.                 return encoding.GetString(ms.ToArray());
  377.             }
  378.         }
  379.     }
  380.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  381.     // Crc32.cs
  382.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  383.     /// <summary>
  384.     /// A utility class to compute CRC32.
  385.     /// </summary>
  386.     internal static class Crc32
  387.     {
  388.         /// <summary>
  389.         /// CRC polynomial 0xEDB88320.
  390.         /// </summary>
  391.         private static readonly uint[] Table =
  392.         {
  393.             0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
  394.             0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
  395.             0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
  396.             0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
  397.             0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
  398.             0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
  399.             0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
  400.             0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
  401.             0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
  402.             0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
  403.             0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
  404.             0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
  405.             0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
  406.             0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
  407.             0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
  408.             0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
  409.             0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
  410.             0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
  411.             0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
  412.             0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
  413.             0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
  414.             0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
  415.             0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
  416.             0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
  417.             0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
  418.             0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
  419.             0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
  420.             0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
  421.             0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
  422.             0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
  423.             0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
  424.             0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
  425.             0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
  426.             0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
  427.             0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
  428.             0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
  429.             0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
  430.             0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
  431.             0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
  432.             0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
  433.             0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
  434.             0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
  435.             0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
  436.             0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
  437.             0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
  438.             0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
  439.             0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
  440.             0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
  441.             0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
  442.             0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
  443.             0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
  444.             0x2D02EF8D
  445.         };
  446.  
  447.         /// <summary>
  448.         /// Compute a checksum for a given array of bytes.
  449.         /// </summary>
  450.         /// <param name="buffer">The array of bytes to compute the checksum for.</param>
  451.         /// <returns>The computed checksum.</returns>
  452.         public static uint Compute(byte[] buffer)
  453.         {
  454.             uint crc = 0xFFFFFFFF;
  455.  
  456.             for (var i = 0; i < buffer.Length; i++)
  457.             {
  458.                 crc = (crc >> 8) ^ Table[buffer[i] ^ crc & 0xff];
  459.             }
  460.  
  461.             return ~crc;
  462.         }
  463.     }
  464.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  465.     // AsnKeyParser.cs
  466.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  467.     // <auto-generated/>
  468.     // Not actually auto generated, but this makes StyleCop ignore this file
  469.  
  470.     /*
  471.     This code is licenced under MIT
  472.  
  473.     // The MIT License
  474.     //
  475.     // Copyright (c) 2006-2008 TinyVine Software Limited.
  476.     //
  477.     // Permission is hereby granted, free of charge, to any person obtaining a copy
  478.     // of this software and associated documentation files (the "Software"), to deal
  479.     // in the Software without restriction, including without limitation the rights
  480.     // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  481.     // copies of the Software, and to permit persons to whom the Software is
  482.     // furnished to do so, subject to the following conditions:
  483.     //
  484.     // The above copyright notice and this permission notice shall be included in
  485.     // all copies or substantial portions of the Software.
  486.     //
  487.     // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  488.     // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  489.     // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  490.     // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  491.     // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  492.     // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  493.     // THE SOFTWARE.
  494.  
  495.     Portions of this software are Copyright of Simone Chiaretta
  496.     Portions of this software are Copyright of Nate Kohari
  497.     Portions of this software are Copyright of Alex Henderson
  498.     */
  499.     [Serializable]
  500.     internal sealed class BerDecodeException : Exception
  501.     {
  502.         private readonly int _position;
  503.  
  504.         public BerDecodeException(string message, int position)
  505.             : base(message)
  506.         {
  507.             _position = position;
  508.         }
  509.  
  510.         public BerDecodeException(string message, int position, Exception ex)
  511.             : base(message, ex)
  512.         {
  513.             _position = position;
  514.         }
  515.  
  516.         private BerDecodeException(SerializationInfo info, StreamingContext context)
  517.             : base(info, context)
  518.         {
  519.             _position = info.GetInt32("Position");
  520.         }
  521.  
  522.         public override string Message
  523.         {
  524.             get
  525.             {
  526.                 var sb = new StringBuilder(base.Message);
  527.  
  528.                 sb.AppendFormat(" (Position {0}){1}",
  529.                                 _position, Environment.NewLine);
  530.  
  531.                 return sb.ToString();
  532.             }
  533.         }
  534.  
  535.         [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
  536.         public override void GetObjectData(SerializationInfo info, StreamingContext context)
  537.         {
  538.             base.GetObjectData(info, context);
  539.             info.AddValue("Position", _position);
  540.         }
  541.     }
  542.  
  543.     internal sealed class AsnKeyParser
  544.     {
  545.         private readonly AsnParser _parser;
  546.  
  547.         public AsnKeyParser(ICollection<byte> contents)
  548.         {
  549.             _parser = new AsnParser(contents);
  550.         }
  551.  
  552.         public static byte[] TrimLeadingZero(byte[] values)
  553.         {
  554.             byte[] r;
  555.             if ((0x00 == values[0]) && (values.Length > 1))
  556.             {
  557.                 r = new byte[values.Length - 1];
  558.                 Array.Copy(values, 1, r, 0, values.Length - 1);
  559.             }
  560.             else
  561.             {
  562.                 r = new byte[values.Length];
  563.                 Array.Copy(values, r, values.Length);
  564.             }
  565.  
  566.             return r;
  567.         }
  568.  
  569.         public static bool EqualOid(byte[] first, byte[] second)
  570.         {
  571.             if (first.Length != second.Length)
  572.             {
  573.                 return false;
  574.             }
  575.  
  576.             for (int i = 0; i < first.Length; i++)
  577.             {
  578.                 if (first[i] != second[i])
  579.                 {
  580.                     return false;
  581.                 }
  582.             }
  583.  
  584.             return true;
  585.         }
  586.  
  587.         public RSAParameters ParseRSAPublicKey()
  588.         {
  589.             var parameters = new RSAParameters();
  590.  
  591.             // Current value
  592.  
  593.             // Sanity Check
  594.  
  595.             // Checkpoint
  596.             int position = _parser.CurrentPosition();
  597.  
  598.             // Ignore Sequence - PublicKeyInfo
  599.             int length = _parser.NextSequence();
  600.             if (length != _parser.RemainingBytes())
  601.             {
  602.                 var sb = new StringBuilder("Incorrect Sequence Size. ");
  603.                 sb.AppendFormat("Specified: {0}, Remaining: {1}",
  604.                                 length.ToString(CultureInfo.InvariantCulture),
  605.                                 _parser.RemainingBytes().ToString(CultureInfo.InvariantCulture));
  606.                 throw new BerDecodeException(sb.ToString(), position);
  607.             }
  608.  
  609.             // Checkpoint
  610.             position = _parser.CurrentPosition();
  611.  
  612.             // Ignore Sequence - AlgorithmIdentifier
  613.             length = _parser.NextSequence();
  614.             if (length > _parser.RemainingBytes())
  615.             {
  616.                 var sb = new StringBuilder("Incorrect AlgorithmIdentifier Size. ");
  617.                 sb.AppendFormat("Specified: {0}, Remaining: {1}",
  618.                                 length.ToString(CultureInfo.InvariantCulture),
  619.                                 _parser.RemainingBytes().ToString(CultureInfo.InvariantCulture));
  620.                 throw new BerDecodeException(sb.ToString(), position);
  621.             }
  622.  
  623.             // Checkpoint
  624.             position = _parser.CurrentPosition();
  625.             // Grab the OID
  626.             byte[] value = _parser.NextOID();
  627.             byte[] oid = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
  628.             if (!EqualOid(value, oid))
  629.             {
  630.                 throw new BerDecodeException("Expected OID 1.2.840.113549.1.1.1", position);
  631.             }
  632.  
  633.             // Optional Parameters
  634.             if (_parser.IsNextNull())
  635.             {
  636.                 _parser.NextNull();
  637.                 // Also OK: value = _parser.Next();
  638.             }
  639.             else
  640.             {
  641.                 // Gracefully skip the optional data
  642.                 _parser.Next();
  643.             }
  644.  
  645.             // Checkpoint
  646.             position = _parser.CurrentPosition();
  647.  
  648.             // Ignore BitString - PublicKey
  649.             length = _parser.NextBitString();
  650.             if (length > _parser.RemainingBytes())
  651.             {
  652.                 var sb = new StringBuilder("Incorrect PublicKey Size. ");
  653.                 sb.AppendFormat("Specified: {0}, Remaining: {1}",
  654.                                 length.ToString(CultureInfo.InvariantCulture),
  655.                                 _parser.RemainingBytes().ToString(CultureInfo.InvariantCulture));
  656.                 throw new BerDecodeException(sb.ToString(), position);
  657.             }
  658.  
  659.             // Checkpoint
  660.             position = _parser.CurrentPosition();
  661.  
  662.             // Ignore Sequence - RSAPublicKey
  663.             length = _parser.NextSequence();
  664.             if (length < _parser.RemainingBytes())
  665.             {
  666.                 var sb = new StringBuilder("Incorrect RSAPublicKey Size. ");
  667.                 sb.AppendFormat("Specified: {0}, Remaining: {1}",
  668.                                 length.ToString(CultureInfo.InvariantCulture),
  669.                                 _parser.RemainingBytes().ToString(CultureInfo.InvariantCulture));
  670.                 throw new BerDecodeException(sb.ToString(), position);
  671.             }
  672.  
  673.             parameters.Modulus = TrimLeadingZero(_parser.NextInteger());
  674.             parameters.Exponent = TrimLeadingZero(_parser.NextInteger());
  675.  
  676.             return parameters;
  677.         }
  678.     }
  679.  
  680.     internal sealed class AsnParser
  681.     {
  682.         private readonly int _initialCount;
  683.         private readonly List<byte> _octets;
  684.  
  685.         public AsnParser(ICollection<byte> values)
  686.         {
  687.             _octets = new List<byte>(values.Count);
  688.             _octets.AddRange(values);
  689.  
  690.             _initialCount = _octets.Count;
  691.         }
  692.  
  693.         public int CurrentPosition()
  694.         {
  695.             return _initialCount - _octets.Count;
  696.         }
  697.  
  698.         public int RemainingBytes()
  699.         {
  700.             return _octets.Count;
  701.         }
  702.  
  703.         private int GetLength()
  704.         {
  705.             int length = 0;
  706.  
  707.             // Checkpoint
  708.             int position = CurrentPosition();
  709.  
  710.             try
  711.             {
  712.                 byte b = GetNextOctet();
  713.  
  714.                 if (b == (b & 0x7f))
  715.                 {
  716.                     return b;
  717.                 }
  718.  
  719.                 int i = b & 0x7f;
  720.  
  721.                 if (i > 4)
  722.                 {
  723.                     var sb = new StringBuilder("Invalid Length Encoding. ");
  724.                     sb.AppendFormat("Length uses {0} _octets",
  725.                                     i.ToString(CultureInfo.InvariantCulture));
  726.                     throw new BerDecodeException(sb.ToString(), position);
  727.                 }
  728.  
  729.                 while (0 != i--)
  730.                 {
  731.                     // shift left
  732.                     length <<= 8;
  733.  
  734.                     length |= GetNextOctet();
  735.                 }
  736.             }
  737.             catch (ArgumentOutOfRangeException ex)
  738.             {
  739.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  740.             }
  741.  
  742.             return length;
  743.         }
  744.  
  745.         public byte[] Next()
  746.         {
  747.             int position = CurrentPosition();
  748.  
  749.             try
  750.             {
  751. #pragma warning disable 168
  752. #pragma warning disable 219
  753.                 byte b = GetNextOctet();
  754. #pragma warning restore 219
  755. #pragma warning restore 168
  756.  
  757.                 int length = GetLength();
  758.                 if (length > RemainingBytes())
  759.                 {
  760.                     var sb = new StringBuilder("Incorrect Size. ");
  761.                     sb.AppendFormat("Specified: {0}, Remaining: {1}",
  762.                                     length.ToString(CultureInfo.InvariantCulture),
  763.                                     RemainingBytes().ToString(CultureInfo.InvariantCulture));
  764.                     throw new BerDecodeException(sb.ToString(), position);
  765.                 }
  766.  
  767.                 return GetOctets(length);
  768.             }
  769.             catch (ArgumentOutOfRangeException ex)
  770.             {
  771.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  772.             }
  773.         }
  774.  
  775.         private byte GetNextOctet()
  776.         {
  777.             int position = CurrentPosition();
  778.  
  779.             if (0 == RemainingBytes())
  780.             {
  781.                 var sb = new StringBuilder("Incorrect Size. ");
  782.                 sb.AppendFormat("Specified: {0}, Remaining: {1}",
  783.                                 1.ToString(CultureInfo.InvariantCulture),
  784.                                 RemainingBytes().ToString(CultureInfo.InvariantCulture));
  785.                 throw new BerDecodeException(sb.ToString(), position);
  786.             }
  787.  
  788.             byte b = GetOctets(1)[0];
  789.  
  790.             return b;
  791.         }
  792.  
  793.         private byte[] GetOctets(int octetCount)
  794.         {
  795.             int position = CurrentPosition();
  796.  
  797.             if (octetCount > RemainingBytes())
  798.             {
  799.                 var sb = new StringBuilder("Incorrect Size. ");
  800.                 sb.AppendFormat("Specified: {0}, Remaining: {1}",
  801.                                 octetCount.ToString(CultureInfo.InvariantCulture),
  802.                                 RemainingBytes().ToString(CultureInfo.InvariantCulture));
  803.                 throw new BerDecodeException(sb.ToString(), position);
  804.             }
  805.  
  806.             var values = new byte[octetCount];
  807.  
  808.             try
  809.             {
  810.                 _octets.CopyTo(0, values, 0, octetCount);
  811.                 _octets.RemoveRange(0, octetCount);
  812.             }
  813.             catch (ArgumentOutOfRangeException ex)
  814.             {
  815.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  816.             }
  817.  
  818.             return values;
  819.         }
  820.  
  821.         public bool IsNextNull()
  822.         {
  823.             return _octets[0] == 0x05;
  824.         }
  825.  
  826.         public int NextNull()
  827.         {
  828.             int position = CurrentPosition();
  829.  
  830.             try
  831.             {
  832.                 byte b = GetNextOctet();
  833.                 if (0x05 != b)
  834.                 {
  835.                     var sb = new StringBuilder("Expected Null. ");
  836.                     sb.AppendFormat("Specified Identifier: {0}", b.ToString(CultureInfo.InvariantCulture));
  837.                     throw new BerDecodeException(sb.ToString(), position);
  838.                 }
  839.  
  840.                 // Next octet must be 0
  841.                 b = GetNextOctet();
  842.                 if (0x00 != b)
  843.                 {
  844.                     var sb = new StringBuilder("Null has non-zero size. ");
  845.                     sb.AppendFormat("Size: {0}", b.ToString(CultureInfo.InvariantCulture));
  846.                     throw new BerDecodeException(sb.ToString(), position);
  847.                 }
  848.  
  849.                 return 0;
  850.             }
  851.             catch (ArgumentOutOfRangeException ex)
  852.             {
  853.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  854.             }
  855.         }
  856.  
  857.         public int NextSequence()
  858.         {
  859.             int position = CurrentPosition();
  860.  
  861.             try
  862.             {
  863.                 byte b = GetNextOctet();
  864.                 if (0x30 != b)
  865.                 {
  866.                     var sb = new StringBuilder("Expected Sequence. ");
  867.                     sb.AppendFormat("Specified Identifier: {0}",
  868.                                     b.ToString(CultureInfo.InvariantCulture));
  869.                     throw new BerDecodeException(sb.ToString(), position);
  870.                 }
  871.  
  872.                 int length = GetLength();
  873.                 if (length > RemainingBytes())
  874.                 {
  875.                     var sb = new StringBuilder("Incorrect Sequence Size. ");
  876.                     sb.AppendFormat("Specified: {0}, Remaining: {1}",
  877.                                     length.ToString(CultureInfo.InvariantCulture),
  878.                                     RemainingBytes().ToString(CultureInfo.InvariantCulture));
  879.                     throw new BerDecodeException(sb.ToString(), position);
  880.                 }
  881.  
  882.                 return length;
  883.             }
  884.             catch (ArgumentOutOfRangeException ex)
  885.             {
  886.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  887.             }
  888.         }
  889.  
  890.         public int NextBitString()
  891.         {
  892.             int position = CurrentPosition();
  893.  
  894.             try
  895.             {
  896.                 byte b = GetNextOctet();
  897.                 if (0x03 != b)
  898.                 {
  899.                     var sb = new StringBuilder("Expected Bit String. ");
  900.                     sb.AppendFormat("Specified Identifier: {0}", b.ToString(CultureInfo.InvariantCulture));
  901.                     throw new BerDecodeException(sb.ToString(), position);
  902.                 }
  903.  
  904.                 int length = GetLength();
  905.  
  906.                 // We need to consume unused bits, which is the first
  907.                 //   octet of the remaing values
  908.                 b = _octets[0];
  909.                 _octets.RemoveAt(0);
  910.                 length--;
  911.  
  912.                 if (0x00 != b)
  913.                 {
  914.                     throw new BerDecodeException("The first octet of BitString must be 0", position);
  915.                 }
  916.  
  917.                 return length;
  918.             }
  919.             catch (ArgumentOutOfRangeException ex)
  920.             {
  921.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  922.             }
  923.         }
  924.  
  925.         public byte[] NextInteger()
  926.         {
  927.             int position = CurrentPosition();
  928.  
  929.             try
  930.             {
  931.                 byte b = GetNextOctet();
  932.                 if (0x02 != b)
  933.                 {
  934.                     var sb = new StringBuilder("Expected Integer. ");
  935.                     sb.AppendFormat("Specified Identifier: {0}", b.ToString(CultureInfo.InvariantCulture));
  936.                     throw new BerDecodeException(sb.ToString(), position);
  937.                 }
  938.  
  939.                 int length = GetLength();
  940.                 if (length > RemainingBytes())
  941.                 {
  942.                     var sb = new StringBuilder("Incorrect Integer Size. ");
  943.                     sb.AppendFormat("Specified: {0}, Remaining: {1}",
  944.                                     length.ToString(CultureInfo.InvariantCulture),
  945.                                     RemainingBytes().ToString(CultureInfo.InvariantCulture));
  946.                     throw new BerDecodeException(sb.ToString(), position);
  947.                 }
  948.  
  949.                 return GetOctets(length);
  950.             }
  951.             catch (ArgumentOutOfRangeException ex)
  952.             {
  953.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  954.             }
  955.         }
  956.  
  957.         public byte[] NextOID()
  958.         {
  959.             int position = CurrentPosition();
  960.  
  961.             try
  962.             {
  963.                 byte b = GetNextOctet();
  964.                 if (0x06 != b)
  965.                 {
  966.                     var sb = new StringBuilder("Expected Object Identifier. ");
  967.                     sb.AppendFormat("Specified Identifier: {0}",
  968.                                     b.ToString(CultureInfo.InvariantCulture));
  969.                     throw new BerDecodeException(sb.ToString(), position);
  970.                 }
  971.  
  972.                 int length = GetLength();
  973.                 if (length > RemainingBytes())
  974.                 {
  975.                     var sb = new StringBuilder("Incorrect Object Identifier Size. ");
  976.                     sb.AppendFormat("Specified: {0}, Remaining: {1}",
  977.                                     length.ToString(CultureInfo.InvariantCulture),
  978.                                     RemainingBytes().ToString(CultureInfo.InvariantCulture));
  979.                     throw new BerDecodeException(sb.ToString(), position);
  980.                 }
  981.  
  982.                 var values = new byte[length];
  983.  
  984.                 for (int i = 0; i < length; i++)
  985.                 {
  986.                     values[i] = _octets[0];
  987.                     _octets.RemoveAt(0);
  988.                 }
  989.  
  990.                 return values;
  991.             }
  992.             catch (ArgumentOutOfRangeException ex)
  993.             {
  994.                 throw new BerDecodeException("Error Parsing Key", position, ex);
  995.             }
  996.         }
  997.     }
  998.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  999.     // Package.cs
  1000.     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1001.     /*
  1002.      * Read() function was mostly taken from Rick's Gibbed.Valve.FileFormats,
  1003.      * which is subject to this license:
  1004.      *
  1005.      * Copyright (c) 2008 Rick (rick 'at' gibbed 'dot' us)
  1006.      *
  1007.      * This software is provided 'as-is', without any express or implied
  1008.      * warranty. In no event will the authors be held liable for any damages
  1009.      * arising from the use of this software.
  1010.      *
  1011.      * Permission is granted to anyone to use this software for any purpose,
  1012.      * including commercial applications, and to alter it and redistribute it
  1013.      * freely, subject to the following restrictions:
  1014.      *
  1015.      * 1. The origin of this software must not be misrepresented; you must not
  1016.      * claim that you wrote the original software. If you use this software
  1017.      * in a product, an acknowledgment in the product documentation would be
  1018.      * appreciated but is not required.
  1019.      *
  1020.      * 2. Altered source versions must be plainly marked as such, and must not be
  1021.      * misrepresented as being the original software.
  1022.      *
  1023.      * 3. This notice may not be removed or altered from any source
  1024.      * distribution.
  1025.      */
  1026.     public class Package : IDisposable
  1027.     {
  1028.         public const int MAGIC = 0x55AA1234;
  1029.  
  1030.         /// <summary>
  1031.         /// Always '/' as per Valve's vpk implementation.
  1032.         /// </summary>
  1033.         public const char DirectorySeparatorChar = '/';
  1034.  
  1035.         private BinaryReader Reader;
  1036.         private bool IsDirVPK;
  1037.         private uint HeaderSize;
  1038.  
  1039.         /// <summary>
  1040.         /// Gets the File Name
  1041.         /// </summary>
  1042.         public string FileName { get; private set; }
  1043.  
  1044.         /// <summary>
  1045.         /// Gets the VPK version.
  1046.         /// </summary>
  1047.         public uint Version { get; private set; }
  1048.  
  1049.         /// <summary>
  1050.         /// Gets the size in bytes of the directory tree.
  1051.         /// </summary>
  1052.         public uint TreeSize { get; private set; }
  1053.  
  1054.         /// <summary>
  1055.         /// Gets how many bytes of file content are stored in this VPK file (0 in CSGO).
  1056.         /// </summary>
  1057.         public uint FileDataSectionSize { get; private set; }
  1058.  
  1059.         /// <summary>
  1060.         /// Gets the size in bytes of the section containing MD5 checksums for external archive content.
  1061.         /// </summary>
  1062.         public uint ArchiveMD5SectionSize { get; private set; }
  1063.  
  1064.         /// <summary>
  1065.         /// Gets the size in bytes of the section containing MD5 checksums for content in this file.
  1066.         /// </summary>
  1067.         public uint OtherMD5SectionSize { get; private set; }
  1068.  
  1069.         /// <summary>
  1070.         /// Gets the size in bytes of the section containing the public key and signature.
  1071.         /// </summary>
  1072.         public uint SignatureSectionSize { get; private set; }
  1073.  
  1074.         /// <summary>
  1075.         /// Gets the MD5 checksum of the file tree.
  1076.         /// </summary>
  1077.         public byte[] TreeChecksum { get; private set; }
  1078.  
  1079.         /// <summary>
  1080.         /// Gets the MD5 checksum of the archive MD5 checksum section entries.
  1081.         /// </summary>
  1082.         public byte[] ArchiveMD5EntriesChecksum { get; private set; }
  1083.  
  1084.         /// <summary>
  1085.         /// Gets the MD5 checksum of the complete package until the signature structure.
  1086.         /// </summary>
  1087.         public byte[] WholeFileChecksum { get; private set; }
  1088.  
  1089.         /// <summary>
  1090.         /// Gets the public key.
  1091.         /// </summary>
  1092.         public byte[] PublicKey { get; private set; }
  1093.  
  1094.         /// <summary>
  1095.         /// Gets the signature.
  1096.         /// </summary>
  1097.         public byte[] Signature { get; private set; }
  1098.  
  1099.         /// <summary>
  1100.         /// Gets the package entries.
  1101.         /// </summary>
  1102.         public Dictionary<string, List<PackageEntry>> Entries { get; private set; }
  1103.  
  1104.         /// <summary>
  1105.         /// Gets the archive MD5 checksum section entries. Also known as cache line hashes.
  1106.         /// </summary>
  1107.         public List<ArchiveMD5SectionEntry> ArchiveMD5Entries { get; private set; }
  1108.  
  1109.         /// <summary>
  1110.         /// Releases binary reader.
  1111.         /// </summary>
  1112.         public void Dispose()
  1113.         {
  1114.             Dispose(true);
  1115.             GC.SuppressFinalize(this);
  1116.         }
  1117.  
  1118.         protected virtual void Dispose(bool disposing)
  1119.         {
  1120.             if (disposing && Reader != null)
  1121.             {
  1122.                 ((IDisposable)Reader).Dispose();
  1123.                 Reader = null;
  1124.             }
  1125.         }
  1126.  
  1127.         /// <summary>
  1128.         /// Sets the file name.
  1129.         /// </summary>
  1130.         /// <param name="fileName">Filename.</param>
  1131.         public void SetFileName(string fileName)
  1132.         {
  1133.             if (fileName.EndsWith(".vpk", StringComparison.Ordinal))
  1134.             {
  1135.                 fileName = fileName.Substring(0, fileName.Length - 4);
  1136.             }
  1137.  
  1138.             if (fileName.EndsWith("_dir", StringComparison.Ordinal))
  1139.             {
  1140.                 IsDirVPK = true;
  1141.  
  1142.                 fileName = fileName.Substring(0, fileName.Length - 4);
  1143.             }
  1144.  
  1145.             FileName = fileName;
  1146.         }
  1147.  
  1148.         /// <summary>
  1149.         /// Opens and reads the given filename.
  1150.         /// The file is held open until the object is disposed.
  1151.         /// </summary>
  1152.         /// <param name="filename">The file to open and read.</param>
  1153.         public void Read(string filename)
  1154.         {
  1155.             SetFileName(filename);
  1156.  
  1157.             var fs = new FileStream(String.Format("{0}{1}.vpk", FileName,(IsDirVPK ? "_dir" : string.Empty)), FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  1158.             //var fs = new FileStream($"{FileName}{(IsDirVPK ? "_dir" : string.Empty)}.vpk", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  1159.  
  1160.             Read(fs);
  1161.         }
  1162.  
  1163.         /// <summary>
  1164.         /// Reads the given <see cref="Stream"/>.
  1165.         /// </summary>
  1166.         /// <param name="input">The input <see cref="Stream"/> to read from.</param>
  1167.         public void Read(Stream input)
  1168.         {
  1169.             if (FileName == null)
  1170.             {
  1171.                 throw new InvalidOperationException("If you call Read() directly with a stream, you must call SetFileName() first.");
  1172.             }
  1173.  
  1174.             Reader = new BinaryReader(input);
  1175.  
  1176.             if (Reader.ReadUInt32() != MAGIC)
  1177.             {
  1178.                 throw new InvalidDataException("Given file is not a VPK.");
  1179.             }
  1180.  
  1181.             Version = Reader.ReadUInt32();
  1182.             TreeSize = Reader.ReadUInt32();
  1183.  
  1184.             if (Version == 1)
  1185.             {
  1186.                 // Nothing else
  1187.             }
  1188.             else if (Version == 2)
  1189.             {
  1190.                 FileDataSectionSize = Reader.ReadUInt32();
  1191.                 ArchiveMD5SectionSize = Reader.ReadUInt32();
  1192.                 OtherMD5SectionSize = Reader.ReadUInt32();
  1193.                 SignatureSectionSize = Reader.ReadUInt32();
  1194.             }
  1195.             else
  1196.             {
  1197.                 throw new InvalidDataException(string.Format("Bad VPK version. ({0})", Version));
  1198.             }
  1199.  
  1200.             HeaderSize = (uint)input.Position;
  1201.  
  1202.             ReadEntries();
  1203.  
  1204.             if (Version == 2)
  1205.             {
  1206.                 // Skip over file data, if any
  1207.                 input.Position += FileDataSectionSize;
  1208.  
  1209.                 ReadArchiveMD5Section();
  1210.                 ReadOtherMD5Section();
  1211.                 ReadSignatureSection();
  1212.             }
  1213.         }
  1214.  
  1215.         /// <summary>
  1216.         /// Searches for a given file entry in the file list.
  1217.         /// </summary>
  1218.         /// <param name="filePath">Full path to the file to find.</param>
  1219.         public PackageEntry FindEntry(string filePath)
  1220.         {
  1221.             filePath = filePath.Replace('\\', DirectorySeparatorChar);
  1222.  
  1223.             // Even though technically we are passing in full path as file name, relevant functions in next overload fix it
  1224.             return FindEntry(Path.GetDirectoryName(filePath), filePath);
  1225.         }
  1226.  
  1227.         /// <summary>
  1228.         /// Searches for a given file entry in the file list.
  1229.         /// </summary>
  1230.         /// <param name="directory">Directory to search in.</param>
  1231.         /// <param name="fileName">File name to find.</param>
  1232.         public PackageEntry FindEntry(string directory, string fileName)
  1233.         {
  1234.             fileName = fileName.Replace('\\', DirectorySeparatorChar);
  1235.  
  1236.             return FindEntry(directory, Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName).TrimStart('.'));
  1237.         }
  1238.  
  1239.         /// <summary>
  1240.         /// Searches for a given file entry in the file list.
  1241.         /// </summary>
  1242.         /// <param name="directory">Directory to search in.</param>
  1243.         /// <param name="fileName">File name to find, without the extension.</param>
  1244.         /// <param name="extension">File extension, without the leading dot.</param>
  1245.         public PackageEntry FindEntry(string directory, string fileName, string extension)
  1246.         {
  1247.             // Assume no extension
  1248.             if (extension == null)
  1249.             {
  1250.                 extension = string.Empty;
  1251.             }
  1252.  
  1253.             if (!Entries.ContainsKey(extension))
  1254.             {
  1255.                 return null;
  1256.             }
  1257.  
  1258.             // We normalize path separators when reading the file list
  1259.             directory = directory.Replace('\\', DirectorySeparatorChar).Trim(DirectorySeparatorChar);
  1260.  
  1261.             // If the directory is empty after trimming, set it to null
  1262.             if (directory == string.Empty)
  1263.             {
  1264.                 directory = null;
  1265.             }
  1266.  
  1267.           //return Entries[extension].FirstOrDefault(x => x.DirectoryName == directory && x.FileName == fileName);
  1268.             foreach (PackageEntry x in Entries[extension])
  1269.             {
  1270.                 if (x.DirectoryName == directory && x.FileName == fileName)
  1271.                 {
  1272.                     return x;
  1273.                 }
  1274.             }
  1275.             return null;
  1276.         }
  1277.  
  1278.         /// <summary>
  1279.         /// Reads the entry from the VPK package.
  1280.         /// </summary>
  1281.         /// <param name="entry">Package entry.</param>
  1282.         /// <param name="output">Output buffer.</param>
  1283.         /// <param name="validateCrc">If true, CRC32 will be calculated and verified for read data.</param>
  1284.         public void ReadEntry(PackageEntry entry, out byte[] output)
  1285.         {
  1286.             ReadEntry(entry, out output, true);
  1287.         }
  1288.         public void ReadEntry(PackageEntry entry, out byte[] output, bool validateCrc)
  1289.         {
  1290.             output = new byte[entry.SmallData.Length + entry.Length];
  1291.  
  1292.             if (entry.SmallData.Length > 0)
  1293.             {
  1294.                 entry.SmallData.CopyTo(output, 0);
  1295.             }
  1296.  
  1297.             if (entry.Length > 0)
  1298.             {
  1299.                 Stream fs = null;
  1300.  
  1301.                 try
  1302.                 {
  1303.                     var offset = entry.Offset;
  1304.  
  1305.                     if (entry.ArchiveIndex != 0x7FFF)
  1306.                     {
  1307.                         if (!IsDirVPK)
  1308.                         {
  1309.                             throw new InvalidOperationException("Given VPK is not a _dir, but entry is referencing an external archive.");
  1310.                         }
  1311.  
  1312.                         //var fileName = FileName+"_"+entry.ArchiveIndex+".vpk";
  1313.                         var fileName = String.Format("{0}_{1:d3}.vpk",FileName,entry.ArchiveIndex);
  1314.  
  1315.                         fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
  1316.                     }
  1317.                     else
  1318.                     {
  1319.                         fs = Reader.BaseStream;
  1320.  
  1321.                         offset += HeaderSize + TreeSize;
  1322.                     }
  1323.  
  1324.                     fs.Seek(offset, SeekOrigin.Begin);
  1325.                     fs.Read(output, entry.SmallData.Length, (int)entry.Length);
  1326.                 }
  1327.                 finally
  1328.                 {
  1329.                     if (entry.ArchiveIndex != 0x7FFF)
  1330.                     {
  1331.                         fs.Close();
  1332.                     }
  1333.                 }
  1334.             }
  1335.  
  1336.             if (validateCrc && entry.CRC32 != Crc32.Compute(output))
  1337.             {
  1338.                 throw new InvalidDataException("CRC32 mismatch for read data.");
  1339.             }
  1340.         }
  1341.  
  1342.         private void ReadEntries()
  1343.         {
  1344.             var typeEntries = new Dictionary<string, List<PackageEntry>>();
  1345.  
  1346.             // Types
  1347.             while (true)
  1348.             {
  1349.                 var typeName = Reader.ReadNullTermString(Encoding.UTF8);
  1350.  
  1351.                 if (typeName == string.Empty)
  1352.                 {
  1353.                     break;
  1354.                 }
  1355.  
  1356.                 // Valve uses a space for missing extensions,
  1357.                 // we replace it with an empty string to match how System.IO.Path deals with it.
  1358.                 if (typeName == " ")
  1359.                 {
  1360.                     typeName = string.Empty;
  1361.                 }
  1362.  
  1363.                 var entries = new List<PackageEntry>();
  1364.  
  1365.                 // Directories
  1366.                 while (true)
  1367.                 {
  1368.                     var directoryName = Reader.ReadNullTermString(Encoding.UTF8);
  1369.  
  1370.                     if (directoryName == string.Empty)
  1371.                     {
  1372.                         break;
  1373.                     }
  1374.  
  1375.                     // Valve uses a space for blank directory names,
  1376.                     // we replace it with a null to match how System.IO.Path deals with root paths.
  1377.                     if (directoryName == " ")
  1378.                     {
  1379.                         directoryName = null;
  1380.                     }
  1381.  
  1382.                     // Files
  1383.                     while (true)
  1384.                     {
  1385.                         var fileName = Reader.ReadNullTermString(Encoding.UTF8);
  1386.  
  1387.                         if (fileName == string.Empty)
  1388.                         {
  1389.                             break;
  1390.                         }
  1391.  
  1392.                         var entry = new PackageEntry
  1393.                         {
  1394.                             FileName = fileName,
  1395.                             DirectoryName = directoryName,
  1396.                             TypeName = typeName,
  1397.                             CRC32 = Reader.ReadUInt32(),
  1398.                             SmallData = new byte[Reader.ReadUInt16()],
  1399.                             ArchiveIndex = Reader.ReadUInt16(),
  1400.                             Offset = Reader.ReadUInt32(),
  1401.                             Length = Reader.ReadUInt32()
  1402.                         };
  1403.  
  1404.                         if (Reader.ReadUInt16() != 0xFFFF)
  1405.                         {
  1406.                             throw new FormatException("Invalid terminator.");
  1407.                         }
  1408.  
  1409.                         if (entry.SmallData.Length > 0)
  1410.                         {
  1411.                             Reader.Read(entry.SmallData, 0, entry.SmallData.Length);
  1412.                         }
  1413.  
  1414.                         entries.Add(entry);
  1415.                     }
  1416.                 }
  1417.  
  1418.                 typeEntries.Add(typeName, entries);
  1419.             }
  1420.  
  1421.             Entries = typeEntries;
  1422.         }
  1423.  
  1424.         /// <summary>
  1425.         /// Verify checksums and signatures provided in the VPK
  1426.         /// </summary>
  1427.         public void VerifyHashes()
  1428.         {
  1429.             if (Version != 2)
  1430.             {
  1431.                 throw new InvalidDataException("Only version 2 is supported.");
  1432.             }
  1433.  
  1434.             using (var md5 = MD5.Create())
  1435.             {
  1436.                 Reader.BaseStream.Position = 0;
  1437.  
  1438.                 var hash = md5.ComputeHash(Reader.ReadBytes((int)(HeaderSize + TreeSize + FileDataSectionSize + ArchiveMD5SectionSize + 32)));
  1439.  
  1440.                 if (!hash.SequenceEqual(WholeFileChecksum))
  1441.                 {
  1442.                     //throw new InvalidDataException($"Package checksum mismatch ({BitConverter.ToString(hash)} != expected {BitConverter.ToString(WholeFileChecksum)})");
  1443.                     throw new InvalidDataException(String.Format("Package checksum mismatch ({0} != expected {1})", BitConverter.ToString(hash), BitConverter.ToString(WholeFileChecksum)));
  1444.                 }
  1445.  
  1446.                 Reader.BaseStream.Position = HeaderSize;
  1447.  
  1448.                 hash = md5.ComputeHash(Reader.ReadBytes((int)TreeSize));
  1449.  
  1450.                 if (!hash.SequenceEqual(TreeChecksum))
  1451.                 {
  1452.                     //throw new InvalidDataException($"File tree checksum mismatch ({BitConverter.ToString(hash)} != expected {BitConverter.ToString(TreeChecksum)})");
  1453.                     throw new InvalidDataException(String.Format("File tree checksum mismatch ({0} != expected {1})", BitConverter.ToString(hash), BitConverter.ToString(TreeChecksum)));
  1454.                 }
  1455.  
  1456.                 Reader.BaseStream.Position = HeaderSize + TreeSize + FileDataSectionSize;
  1457.  
  1458.                 hash = md5.ComputeHash(Reader.ReadBytes((int)ArchiveMD5SectionSize));
  1459.  
  1460.                 if (!hash.SequenceEqual(ArchiveMD5EntriesChecksum))
  1461.                 {
  1462.                     //throw new InvalidDataException($"Archive MD5 entries checksum mismatch ({BitConverter.ToString(hash)} != expected {BitConverter.ToString(ArchiveMD5EntriesChecksum)})");
  1463.                     throw new InvalidDataException(String.Format("Archive MD5 entries checksum mismatch ({0} != expected {1})", BitConverter.ToString(hash), BitConverter.ToString(ArchiveMD5EntriesChecksum)));
  1464.                 }
  1465.  
  1466.                 // TODO: verify archive checksums
  1467.             }
  1468.  
  1469.             if (PublicKey == null || Signature == null)
  1470.             {
  1471.                 return;
  1472.             }
  1473.  
  1474.             if (!IsSignatureValid())
  1475.             {
  1476.                 throw new InvalidDataException("VPK signature is not valid.");
  1477.             }
  1478.         }
  1479.  
  1480.         /// <summary>
  1481.         /// Verifies the RSA signature.
  1482.         /// </summary>
  1483.         /// <returns>True if signature is valid, false otherwise.</returns>
  1484.         public bool IsSignatureValid()
  1485.         {
  1486.             Reader.BaseStream.Position = 0;
  1487.  
  1488.             var keyParser = new AsnKeyParser(PublicKey);
  1489.  
  1490.             var rsa = new RSACryptoServiceProvider();
  1491.             rsa.ImportParameters(keyParser.ParseRSAPublicKey());
  1492.  
  1493.             var deformatter = new RSAPKCS1SignatureDeformatter(rsa);
  1494.             deformatter.SetHashAlgorithm("SHA256");
  1495.  
  1496.             var hash = new SHA256Managed().ComputeHash(Reader.ReadBytes((int)(HeaderSize + TreeSize + FileDataSectionSize + ArchiveMD5SectionSize + OtherMD5SectionSize)));
  1497.  
  1498.             return deformatter.VerifySignature(hash, Signature);
  1499.         }
  1500.  
  1501.         private void ReadArchiveMD5Section()
  1502.         {
  1503.             ArchiveMD5Entries = new List<ArchiveMD5SectionEntry>();
  1504.  
  1505.             if (ArchiveMD5SectionSize == 0)
  1506.             {
  1507.                 return;
  1508.             }
  1509.  
  1510.             var entries = ArchiveMD5SectionSize / 28; // 28 is sizeof(VPK_MD5SectionEntry), which is int + int + int + 16 chars
  1511.  
  1512.             for (var i = 0; i < entries; i++)
  1513.             {
  1514.                 ArchiveMD5Entries.Add(new ArchiveMD5SectionEntry
  1515.                 {
  1516.                     ArchiveIndex = Reader.ReadUInt32(),
  1517.                     Offset = Reader.ReadUInt32(),
  1518.                     Length = Reader.ReadUInt32(),
  1519.                     Checksum = Reader.ReadBytes(16)
  1520.                 });
  1521.             }
  1522.         }
  1523.  
  1524.         private void ReadOtherMD5Section()
  1525.         {
  1526.             if (OtherMD5SectionSize != 48)
  1527.             {
  1528.                 //throw new InvalidDataException($"Encountered OtherMD5Section with size of {OtherMD5SectionSize} (should be 48)");
  1529.                 throw new InvalidDataException(String.Format("Encountered OtherMD5Section with size of {0} (should be 48)", OtherMD5SectionSize));
  1530.             }
  1531.  
  1532.             TreeChecksum = Reader.ReadBytes(16);
  1533.             ArchiveMD5EntriesChecksum = Reader.ReadBytes(16);
  1534.             WholeFileChecksum = Reader.ReadBytes(16);
  1535.         }
  1536.  
  1537.         private void ReadSignatureSection()
  1538.         {
  1539.             if (SignatureSectionSize == 0)
  1540.             {
  1541.                 return;
  1542.             }
  1543.  
  1544.             var publicKeySize = Reader.ReadInt32();
  1545.             PublicKey = Reader.ReadBytes(publicKeySize);
  1546.  
  1547.             var signatureSize = Reader.ReadInt32();
  1548.             Signature = Reader.ReadBytes(signatureSize);
  1549.         }
  1550.     }
  1551. }
  1552.  
  1553. namespace VPKMOD
  1554. {
  1555.     public class Options
  1556.     {
  1557.         public string Input = string.Empty;
  1558.         public bool Recursive = false;
  1559.         public string Output = string.Empty;
  1560.         public List<string> ExtFilter = new List<string>();
  1561.         public List<string> PathFilter = new List<string>();
  1562.         public string FilterList = string.Empty;
  1563.         public string ModList = string.Empty;
  1564.         public bool OutputVPKDir = false;
  1565.         public bool CachedManifest = false;
  1566.         public bool VerifyVPKChecksums = false;
  1567.         public bool Silent = false;
  1568.         public bool Help = false;
  1569.         public Options()
  1570.         {
  1571.             Parsed = new Dictionary< string, List<string> >();
  1572.         }
  1573.         public IDictionary< string, List<string> > Parsed { get; private set; }
  1574.         public bool Find(string key)
  1575.         {
  1576.             return Find(key, false);
  1577.         }
  1578.         public bool Find(string key, bool keyonly)
  1579.         {
  1580.             if (Parsed.ContainsKey(key))
  1581.             {
  1582.                 if (keyonly) return true;
  1583.                 if (Parsed[key].Count != 0) return true;
  1584.                 Console.Error.WriteLine("VPKMOD ERROR! Missing argument for: -{0}", key);
  1585.             }
  1586.             return false;
  1587.         }
  1588.         public void Parse(string[] args)
  1589.         {
  1590.             var key = "";
  1591.             List<string> values = new List<string>();
  1592.             foreach (string arg in args)
  1593.             {
  1594.                 if (arg.StartsWith("-"))
  1595.                 {
  1596.                     if (key != "") Parsed[key] = values;
  1597.                     values =  new List<string>();
  1598.                     key = arg.Substring(1);
  1599.                 }
  1600.                 else if (key == "") Parsed[arg] = new List<string>();
  1601.                 else values = new List<string>(arg.Split(','));
  1602.             }
  1603.             if (key != "") Parsed[key] = values;
  1604.  
  1605.             if (Find("i"))
  1606.             {
  1607.                 Input = Parsed["i"].FirstOrDefault();
  1608.                 Console.WriteLine("VPKMOD -i Input:      {0}", Input);
  1609.             }
  1610.             if (Find("r", true))
  1611.             {
  1612.                 Recursive = true;
  1613.                 Console.WriteLine("VPKMOD -r Recursive:  {0} (if input is a folder)", Recursive);
  1614.             }
  1615.             if (Find("o"))
  1616.             {
  1617.                 Output = Parsed["o"].FirstOrDefault();
  1618.                 Console.WriteLine("VPKMOD -o Output:     {0}", Output);
  1619.             }
  1620.             if (Find("e"))
  1621.             {
  1622.                 ExtFilter = Parsed["e"];
  1623.                 Console.WriteLine("VPKMOD -e ExtFilter:  {0}", string.Join(", ", ExtFilter.ToArray()));
  1624.             }
  1625.             if (Find("p"))
  1626.             {
  1627.                 PathFilter = Parsed["p"];
  1628.                 Console.WriteLine("VPKMOD -p PathFilter: {0}", string.Join(", ", PathFilter.ToArray()));
  1629.             }
  1630.             if (Find("l"))
  1631.             {
  1632.                 FilterList = Parsed["l"].FirstOrDefault();
  1633.                 Console.WriteLine("VPKMOD -l Filter.lst: {0}", FilterList);
  1634.             }
  1635.             if (Find("m"))
  1636.             {
  1637.                 ModList = Parsed["m"].FirstOrDefault();
  1638.                 Console.WriteLine("VPKMOD -m Mod.lst:    {0}", ModList);
  1639.             }
  1640.             if (Find("d", true))
  1641.             {
  1642.                 OutputVPKDir = true;
  1643.                 Console.WriteLine("VPKMOD -d OutputVPKDir: {0}", OutputVPKDir);
  1644.             }
  1645.             if (Find("c", true))
  1646.             {
  1647.                 CachedManifest = true;
  1648.                 Console.WriteLine("VPKMOD -c CachedManifest: {0}", CachedManifest);
  1649.             }
  1650.             if (Find("v", true))
  1651.             {
  1652.                 VerifyVPKChecksums = true;
  1653.                 Console.WriteLine("VPKMOD -v VerifyVPKChecksums: {0}", VerifyVPKChecksums);
  1654.             }
  1655.             if (Find("s", true))
  1656.             {
  1657.                 Silent = true;
  1658.                 Console.WriteLine("VPKMOD -s Silent:     {0}", Silent);
  1659.             }
  1660.             if (Find("h", true) || args.Length == 0)
  1661.             {
  1662.                 Console.WriteLine("VPKMOD v1.0 \t AveYo / SteamDatabase");
  1663.                 Console.WriteLine(" -i  Input VPK file (unpak) or directory (pak)");
  1664.                 Console.WriteLine(" -r  Process all files in subdirectories (pak)");
  1665.                 Console.WriteLine(" -o  Output directory (unpak) or file.vpk (pak)");
  1666.                 Console.WriteLine(" -e  Extension(s) filter, example: \"vcss_c,vjs_c,vxml_c\"");
  1667.                 Console.WriteLine(" -p  Path(s) filter, example: \"particles/,models/\"");
  1668.                 Console.WriteLine(" -l  Import filters from external.lst,");
  1669.                 Console.WriteLine("     if -e or -p are also used, export filters");
  1670.                 Console.WriteLine(" -m  Import mod?src definitions from external.lst");
  1671.                 Console.WriteLine("     allowing in-memory unpak-rename-pak modding");
  1672.                 Console.WriteLine(" -d  Export VPK directory of files and their CRC");
  1673.                 Console.WriteLine(" -c  Use cached VPK manifest - only changed files get written to disk");
  1674.                 Console.WriteLine(" -v  Verify checksums and signatures");
  1675.                 Console.WriteLine(" -t  Parallel processing threads count");
  1676.                 Console.WriteLine(" -s  Silent processing");
  1677.                 Console.WriteLine(" -h  This help screen");
  1678.                 Console.WriteLine("Press ENTER to quit");
  1679.                 Console.ReadLine();
  1680.                 Environment.Exit(0);
  1681.             }
  1682.         }
  1683.     }
  1684.  
  1685.     public class Tree<TKey, TValue> : Dictionary<TKey, TValue> {}
  1686.     public class Tree<TKey1, TKey2, TValue> : Dictionary<TKey1, Tree<TKey2, TValue>> {}
  1687.     public class Tree<TKey1, TKey2, TKey3, TValue> : Dictionary<TKey1, Tree<TKey2, TKey3, TValue>> {}
  1688.     public static class TreeExtensions
  1689.     {
  1690.         public static Tree<TKey2, TValue> New<TKey1, TKey2, TValue>(this Tree<TKey1, TKey2, TValue> dictionary)
  1691.         {
  1692.             return new Tree<TKey2, TValue>();
  1693.         }
  1694.         public static Tree<TKey2, TKey3, TValue> New<TKey1,TKey2,TKey3,TValue>(this Tree<TKey1,TKey2,TKey3,TValue> dictionary)
  1695.         {
  1696.             return new Tree<TKey2, TKey3, TValue>();
  1697.         }
  1698.     }
  1699.  
  1700.     class Program
  1701.     {
  1702.         private static readonly object ConsoleWriterLock = new object();
  1703.         private static Options Options;
  1704.         private static int CurrentFile = 0;
  1705.         private static int TotalFiles = 0;
  1706.         private static Dictionary<string, uint> OldPakManifest = new Dictionary<string, uint>();
  1707.         private static Dictionary<string, Dictionary<string, bool>> ModSrc = new Dictionary<string, Dictionary<string, bool>>();
  1708.         private static Dictionary<string, string> SrcMod = new Dictionary<string, string>();
  1709.         private static List<string> FileFilter = new List<string>();
  1710.         private static List<string> ExtFilter = new List<string>();
  1711.         private static bool ExportFilter = false;
  1712.  
  1713.         public static void Main(string[] args)
  1714.         {
  1715.             Options = new Options();
  1716.             Options.Parse(args);
  1717.  
  1718.             if (String.IsNullOrEmpty(Options.Input))
  1719.             {
  1720.                 Echo("Missing -i input parameter!", ConsoleColor.Red);
  1721.                 return;
  1722.             }
  1723.             Options.Input = FixPathSlashes(Path.GetFullPath(Options.Input));
  1724.  
  1725.             if (!String.IsNullOrEmpty(Options.Output))
  1726.             {
  1727.                 Options.Output = FixPathSlashes(Path.GetFullPath(Options.Output));
  1728.             }
  1729.  
  1730.             if (!String.IsNullOrEmpty(Options.ModList))
  1731.             {
  1732.                 Options.ModList = FixPathSlashes(Path.GetFullPath(Options.ModList));
  1733.                 if (File.Exists(Options.ModList))
  1734.                 {
  1735.                     var file = new StreamReader(Options.ModList);
  1736.                     string line, ext, mod, src;
  1737.                     Dictionary<string, bool> m = new Dictionary<string, bool>();
  1738.                     while ((line = file.ReadLine()) != null)
  1739.                     {
  1740.                         var split = line.Split(new[] { '?' }, 2);
  1741.                         if (split.Length == 2)
  1742.                         {
  1743.                             mod = FixPathSlashes(split[0]);
  1744.                             src = FixPathSlashes(split[1]);
  1745.                             FileFilter.Add(src);
  1746.                             ext = Path.GetExtension(src);
  1747.                             if (ext.Length > 1) ExtFilter.Add(ext.Substring(1));
  1748.  
  1749.                             SrcMod[src] =  mod;
  1750.  
  1751.                             if (!ModSrc.ContainsKey(src))
  1752.                                 ModSrc.Add(src, new Dictionary<string, bool> { {mod , false} });
  1753.                             else
  1754.                               ModSrc[src].Add(mod, false);
  1755.                         }
  1756.                     }
  1757.                     file.Close();
  1758.                 }
  1759.             }
  1760.             else if (!String.IsNullOrEmpty(Options.FilterList))
  1761.             {
  1762.                 Options.FilterList = FixPathSlashes(Path.GetFullPath(Options.FilterList));
  1763.                 if (File.Exists(Options.FilterList))
  1764.                 {
  1765.                     var file = new StreamReader(Options.FilterList);
  1766.                     string line, ext;
  1767.                     while ((line = file.ReadLine()) != null)
  1768.                     {
  1769.                         FileFilter.Add(FixPathSlashes(line));
  1770.                         ext = Path.GetExtension(line);
  1771.                         if (ext.Length > 1) ExtFilter.Add(ext.Substring(1));
  1772.                     }
  1773.                     file.Close();
  1774.                 }
  1775.                 if (Options.PathFilter.Count > 0 || Options.ExtFilter.Count > 0)
  1776.                 {
  1777.                      ExportFilter = true;
  1778.                 }
  1779.             }
  1780.  
  1781.             if (Options.PathFilter.Count > 0)
  1782.             {
  1783.                 foreach (string filter in Options.PathFilter.ToList())
  1784.                 {
  1785.                     int index = Options.PathFilter.IndexOf(filter);
  1786.                     if (index != -1) Options.PathFilter[index] = FixPathSlashes(filter);
  1787.                 }
  1788.             }
  1789.  
  1790.             var paths = new List<string>();
  1791.  
  1792.             if (Directory.Exists(Options.Input))
  1793.             {
  1794.                 if (Path.GetExtension(Options.Output).ToLower() != ".vpk")
  1795.                 {
  1796.                     Echo(String.Format("Input \"{0}\" is a directory while Output \"{1}\" is not a VPK.", Options.Input, Options.Output), ConsoleColor.Red);
  1797.                     return;
  1798.                 }
  1799.                 paths.AddRange(Directory.GetFiles(Options.Input, "*.*", Options.Recursive ? SearchOption.AllDirectories : !!))
  1800.                 if (paths.Count == 0)
  1801.                 {
  1802.                     Echo(String.Format("No such file \"{0}\" or directory is empty. Did you mean to include -r (recursive) parameter?", Options.Input), ConsoleColor.Red);
  1803.                     return;
  1804.                 }
  1805.                 WriteVPK(paths, false); // pak directory into output.vpk
  1806.             }
  1807.             else if (File.Exists(Options.Input))
  1808.             {
  1809.                 if (Path.GetExtension(Options.Input).ToLower() != ".vpk")
  1810.                 {
  1811.                     Echo(String.Format("Input \"{0}\" is not a VPK.", Options.Input), ConsoleColor.Red);
  1812.                     return;
  1813.                 }
  1814.                 paths.Add(Options.Input);
  1815.                 if (Path.GetExtension(Options.Output).ToLower() != ".vpk")
  1816.                 {
  1817.                     ReadVPK(Options.Input); // unpak input.vpk into output dir
  1818.                 }
  1819.                 else
  1820.                 {
  1821.                     WriteVPK(paths, true); // mod input.vpk into output.vpk
  1822.                 }
  1823.             }
  1824.  
  1825.             CurrentFile = 0;
  1826.             TotalFiles = paths.Count;
  1827.         }
  1828.  
  1829.         private static void ReadVPK(string path)
  1830.         {
  1831.             Echo(String.Format("--- Listing files in package \"{0}\"", path), ConsoleColor.Green);
  1832.             var sw = Stopwatch.StartNew();
  1833.             var package = new Package();
  1834.             try
  1835.             {
  1836.                 package.Read(path);
  1837.             }
  1838.             catch (Exception e)
  1839.             {
  1840.                 Echo(e.ToString(), ConsoleColor.Yellow);
  1841.             }
  1842.  
  1843.             if (Options.VerifyVPKChecksums)
  1844.             {
  1845.                 try
  1846.                 {
  1847.                     package.VerifyHashes();
  1848.  
  1849.                     Console.WriteLine("VPK verification succeeded");
  1850.                 }
  1851.                 catch (Exception)
  1852.                 {
  1853.                     Echo("Failed to verify checksums and signature of given VPK:", ConsoleColor.Red);
  1854.                 }
  1855.                 return;
  1856.             }
  1857.  
  1858.             if (!String.IsNullOrEmpty(Options.Output) && !Options.OutputVPKDir)
  1859.             {
  1860.               //Console.WriteLine("--- Reading VPK files...");
  1861.                 var manifestPath = string.Concat(path, ".manifest.txt");
  1862.                 if (Options.CachedManifest && File.Exists(manifestPath))
  1863.                 {
  1864.                     var file = new StreamReader(manifestPath);
  1865.                     string line;
  1866.                     while ((line = file.ReadLine()) != null)
  1867.                     {
  1868.                         var split = line.Split(new[] { ' ' }, 2);
  1869.                         if (split.Length == 2)
  1870.                         {
  1871.                             OldPakManifest.Add(split[1], uint.Parse(split[0]));
  1872.                         }
  1873.                     }
  1874.                     file.Close();
  1875.                 }
  1876.  
  1877.                 foreach (var type in package.Entries)
  1878.                 {
  1879.                     if (ExtFilter.Count > 0 && !ExtFilter.Contains(type.Key))
  1880.                     {
  1881.                         continue;
  1882.                     }
  1883.                     else if (Options.ExtFilter.Count > 0 && !Options.ExtFilter.Contains(type.Key))
  1884.                     {
  1885.                         continue;
  1886.                     }
  1887.                     DumpVPK(package, type.Key);
  1888.                 }
  1889.  
  1890.                 if (Options.CachedManifest)
  1891.                 {
  1892.                     using (var file = new StreamWriter(manifestPath))
  1893.                     {
  1894.                         foreach (var hash in OldPakManifest)
  1895.                         {
  1896.                             if (package.FindEntry(hash.Key) == null)
  1897.                             {
  1898.                                 Console.WriteLine("\t{0} no longer exists in VPK", hash.Key);
  1899.                             }
  1900.                             file.WriteLine("{0} {1}", hash.Value, hash.Key);
  1901.                         }
  1902.                     }
  1903.                 }
  1904.             }
  1905.  
  1906.             if (Options.OutputVPKDir)
  1907.             {
  1908.                 foreach (var type in package.Entries)
  1909.                 {
  1910.                     foreach (var file in type.Value)
  1911.                     {
  1912.                         Console.WriteLine(file);
  1913.                     }
  1914.                 }
  1915.             }
  1916.  
  1917.             if (ExportFilter)
  1918.             {
  1919.                 using (var filter = new StreamWriter(Options.FilterList))
  1920.                 {
  1921.                     foreach (var type in package.Entries)
  1922.                     {
  1923.                         if (Options.ExtFilter.Count > 0 && !Options.ExtFilter.Contains(type.Key))
  1924.                         {
  1925.                             continue;
  1926.                         }
  1927.                         foreach (var file in type.Value)
  1928.                         {
  1929.                             var ListPath = FixPathSlashes(file.GetFullPath());
  1930.                             if (Options.PathFilter.Count > 0)
  1931.                             {
  1932.                                 var found = false;
  1933.                                 foreach (string pathfilter in Options.PathFilter)
  1934.                                 {
  1935.                                     if (ListPath.StartsWith(pathfilter, StringComparison.Ordinal)) found = true;
  1936.                                 }
  1937.                                 if (!found) continue;
  1938.                             }
  1939.                             filter.WriteLine(ListPath);
  1940.                             if (!Options.Silent) Console.WriteLine(ListPath);
  1941.                         }
  1942.                     }
  1943.                 }
  1944.             }
  1945.  
  1946.             sw.Stop();
  1947.  
  1948.             Echo(String.Format("--- Processed in {0}s", sw.Elapsed.TotalSeconds), ConsoleColor.Cyan);
  1949.         }
  1950.  
  1951.         private static void WriteVPK(List<string> paths, bool modding)
  1952.         {
  1953.  
  1954.             if (modding) Echo("--- Exporting filtered input", ConsoleColor.Green);
  1955.             else Echo("--- Paking input folder", ConsoleColor.Green);
  1956.             TotalFiles = paths.Count;
  1957.             var sw = Stopwatch.StartNew();
  1958.             Encoding iso = Encoding.GetEncoding("ISO-8859-1");
  1959.             Encoding utf = Encoding.UTF8;
  1960.             byte[] data = new byte[0];
  1961.             int Signature = 0x55AA1234;
  1962.             uint Version = 1;
  1963.             uint TreeSize = 0;
  1964.             uint HeaderSize = 4 * 3;
  1965.             var excluded = new List<string> { "zip", "reg", "rar", "msi", "exe", "dll", "com", "cmd", "bat", "vbs" };
  1966.             var tree = new Tree<string, string, string, byte[]>();
  1967.             var package = new Package();
  1968.  
  1969.             if (modding)
  1970.             {
  1971.                 try
  1972.                 {
  1973.                     package.Read(Options.Input);
  1974.                 }
  1975.                 catch (Exception e)
  1976.                 {
  1977.                     Echo(e.ToString(), ConsoleColor.Yellow);
  1978.                 }
  1979.                 foreach (var type in package.Entries)
  1980.                 {
  1981.                     if (ExtFilter.Count > 0 && !ExtFilter.Contains(type.Key))
  1982.                     {
  1983.                         continue;
  1984.                     }
  1985.                     else if (Options.ExtFilter.Count > 0 && !Options.ExtFilter.Contains(type.Key))
  1986.                     {
  1987.                         continue;
  1988.                     }
  1989.  
  1990.                     var entries = package.Entries[type.Key];
  1991.  
  1992.                     foreach (var file in entries)
  1993.                     {
  1994.                         var filePath = string.Format("{0}.{1}", file.FileName, file.TypeName);
  1995.                         if (file.DirectoryName.Length > 0)
  1996.                         {
  1997.                             filePath = Path.Combine(file.DirectoryName, filePath);
  1998.                         }
  1999.                         filePath = FixPathSlashes(filePath);
  2000.  
  2001.                         bool found = false;
  2002.                         if (FileFilter.Count > 0)
  2003.                         {
  2004.                             foreach (string filter in FileFilter)
  2005.                             {
  2006.                               //if (filePath.StartsWith(filter)) found = true;
  2007.                                 if (filePath == filter) found = true;
  2008.                             }
  2009.                             if (!found) continue;
  2010.                         }
  2011.                         else if (Options.PathFilter.Count > 0)
  2012.                         {
  2013.                             foreach (string filter in Options.PathFilter)
  2014.                             {
  2015.                               if (filePath.StartsWith(filter, StringComparison.Ordinal)) found = true;
  2016.                             }
  2017.                             if (!found) continue;
  2018.                         }
  2019.                         string root = string.Empty;
  2020.                         var ext = file.TypeName; //Path.GetExtension(root).TrimStart('.');
  2021.                         if (ext == string.Empty)
  2022.                         {
  2023.                             ext = " ";
  2024.                             if (!Options.Silent) Echo("  missing extension!", ConsoleColor.Red);
  2025.                             //continue;
  2026.                         }
  2027.                         if (excluded.Contains(ext))
  2028.                         {
  2029.                             if (!Options.Silent) Echo("  illegal extension!", ConsoleColor.Red);
  2030.                             continue;
  2031.                         }
  2032.                         var filename = file.FileName; //Path.GetFileNameWithoutExtension(root);
  2033.                         if (filename == string.Empty)
  2034.                         {
  2035.                             filename = " ";
  2036.                             if (!Options.Silent) Echo("  missing name!", ConsoleColor.Red);
  2037.                             //continue;
  2038.                         }
  2039.                         var rel = file.DirectoryName; //Path.GetDirectoryName(root).Replace('\\', '/');
  2040.  
  2041.                         byte[] output;
  2042.                         package.ReadEntry(file, out output);
  2043.  
  2044.                         if (ModSrc.ContainsKey(filePath))
  2045.                         {
  2046.                             if (!Options.Silent) Console.WriteLine("MOD: {0}", filePath);
  2047.                             foreach (var m in ModSrc[filePath])
  2048.                             {
  2049.                                 if (!Options.Silent) Console.WriteLine("   >>{0}", m);
  2050.                                 filePath = m.Key;
  2051.                                 ext = Path.GetExtension(m.Key).TrimStart('.');
  2052.                                 filename = Path.GetFileNameWithoutExtension(m.Key);
  2053.                                 rel = Path.GetDirectoryName(m.Key).Replace('\\', '/');
  2054.                                 if (rel == string.Empty) {
  2055.                                     rel = " ";
  2056.                                 }
  2057.                                 if (!tree.ContainsKey(ext)) {
  2058.                                     tree[ext] = tree.New();
  2059.                                 }
  2060.                                 if (!tree[ext].ContainsKey(rel)) {
  2061.                                     tree[ext][rel] = tree[ext].New();
  2062.                                 }
  2063.                                 tree[ext][rel][filename] = output; //File.ReadAllBytes(path);
  2064.                             }
  2065.                         }
  2066.                         else
  2067.                         {
  2068.                             if (rel == string.Empty) {
  2069.                                 rel = " ";
  2070.                             }
  2071.                             if (!tree.ContainsKey(ext)) {
  2072.                                 tree[ext] = tree.New();
  2073.                             }
  2074.                             if (!tree[ext].ContainsKey(rel)) {
  2075.                                 tree[ext][rel] = tree[ext].New();
  2076.                             }
  2077.                           //package.ReadEntry(file, out output);
  2078.                             tree[ext][rel][filename] = output; //File.ReadAllBytes(path);
  2079.                         }
  2080.  
  2081.                     }
  2082.                 }
  2083.             }
  2084.  
  2085.             if (!modding)
  2086.             {
  2087.                 foreach (var path in paths)
  2088.                 {
  2089.                     if (!Options.Silent) Console.WriteLine("[{0}/{1}] {2}", ++CurrentFile, TotalFiles, path);
  2090.                     byte[] latin = Encoding.Convert(utf, iso, utf.GetBytes(path.Substring(Options.Input.Length)));
  2091.                     string root = iso.GetString(latin).ToLower();
  2092.                     var ext = Path.GetExtension(root).TrimStart('.');
  2093.                     if (ext == string.Empty)
  2094.                     {
  2095.                         ext = " ";
  2096.                         if (!Options.Silent) Echo("  missing extension!", ConsoleColor.Red);
  2097.                         //continue;
  2098.                     }
  2099.                     if (excluded.Contains(ext))
  2100.                     {
  2101.                         if (!Options.Silent) Echo("  illegal extension!", ConsoleColor.Red);
  2102.                         continue;
  2103.                     }
  2104.                     var filename = Path.GetFileNameWithoutExtension(root);
  2105.                     if (filename == string.Empty)
  2106.                     {
  2107.                         filename = " ";
  2108.                         if (!Options.Silent) Echo("  missing name!", ConsoleColor.Red);
  2109.                         //continue;
  2110.                     }
  2111.                     var rel = Path.GetDirectoryName(root).Replace('\\', '/');
  2112.                     if (rel == string.Empty) {
  2113.                         rel = " ";
  2114.                     }
  2115.                     if (!tree.ContainsKey(ext)) {
  2116.                         tree[ext] = tree.New();
  2117.                     }
  2118.                     if (!tree[ext].ContainsKey(rel)) {
  2119.                         tree[ext][rel] = tree[ext].New();
  2120.                     }
  2121.                     tree[ext][rel][filename] = File.ReadAllBytes(path);
  2122.                 }
  2123.             }
  2124.  
  2125.             foreach (var ext in tree)
  2126.             {
  2127.                 TreeSize += (uint)ext.Key.Length + 2;
  2128.               //Console.WriteLine("[ {0} ]",ext.Key);
  2129.                 foreach (var relpath in tree[ext.Key]) {
  2130.                     TreeSize += (uint)relpath.Key.Length + 2;
  2131.                   //Console.WriteLine("[ --- {0}", relpath.Key);
  2132.                     foreach (var filename in tree[ext.Key][relpath.Key]) {
  2133.                         TreeSize += (uint)filename.Key.Length + 1 + 18;
  2134.                       //Console.WriteLine("[ --- : ---> {0}.{1}", filename.Key, ext.Key);
  2135.                     }
  2136.                 }
  2137.             }
  2138.             TreeSize += 1;
  2139.             using (var input = new MemoryStream())
  2140.             {
  2141.                 input.Write(BitConverter.GetBytes(Signature), 0, 4);
  2142.                 input.Write(BitConverter.GetBytes(Version), 0, 4);
  2143.                 input.Write(BitConverter.GetBytes(TreeSize), 0, 4);
  2144.                 HeaderSize = (uint)input.Position;
  2145.                 var data_offset = HeaderSize + TreeSize;
  2146.                 foreach (var ext in tree) {
  2147.                   input.Write(Encoding.UTF8.GetBytes(ext.Key), 0, ext.Key.Length);
  2148.                   input.Write(new byte[] { 0x0 }, 0, 1);
  2149.                   foreach (var relpath in tree[ext.Key]) {
  2150.                     input.Write(Encoding.UTF8.GetBytes(relpath.Key), 0, relpath.Key.Length);
  2151.                     input.Write(new byte[] { 0x0 }, 0, 1);
  2152.                     foreach (var filename in tree[ext.Key][relpath.Key]) {
  2153.                       input.Write(Encoding.UTF8.GetBytes(filename.Key), 0, filename.Key.Length);
  2154.                       input.Write(new byte[] { 0x0 }, 0, 1);
  2155.                       var metadata_offset = (uint)input.Position;
  2156.                       var file_offset = data_offset;
  2157.                       uint checksum = 0;
  2158.                       input.Position = data_offset;
  2159.                       var file = tree[ext.Key][relpath.Key][filename.Key];
  2160.                     //if (modding) checksum = package.FindEntry(relpath.Key, filename.Key, ext.Key).CRC32;
  2161.                     //else checksum = Crc32.Compute(file);
  2162.                       checksum = Crc32.Compute(file);
  2163.                       input.Write(file, 0, file.Length);
  2164.                       data_offset = (uint)input.Position;
  2165.                       var file_length = data_offset - file_offset;
  2166.                       input.Position = metadata_offset;
  2167.                       input.Write(BitConverter.GetBytes(checksum & 0xFFffFFff), 0, 4);
  2168.                       input.Write(new byte[] { 0x0, 0x0 }, 0, 2);
  2169.                       input.Write(BitConverter.GetBytes(0x7fff), 0, 2);
  2170.                       input.Write(BitConverter.GetBytes(file_offset - TreeSize - HeaderSize), 0, 4);
  2171.                       input.Write(BitConverter.GetBytes(file_length), 0, 4);
  2172.                       input.Write(BitConverter.GetBytes(0xffff), 0, 2);
  2173.                     }
  2174.                     // next relpath
  2175.                     input.Write(new byte[] { 0x0 }, 0, 1);
  2176.                   }
  2177.                   // next ext
  2178.                   input.Write(new byte[] { 0x0 }, 0, 1);
  2179.                 }
  2180.                 // end of file tree
  2181.                 input.Write(new byte[] { 0x0 }, 0, 1);
  2182.                 data = input.ToArray();
  2183.             }
  2184.             DumpFile(Options.Output, data);
  2185.             sw.Stop();
  2186.             Echo(String.Format("--- Processed in {0}s", sw.Elapsed.TotalSeconds), ConsoleColor.Cyan);
  2187.         }
  2188.  
  2189.         private static void DumpVPK(Package package, string type)
  2190.         {
  2191.             var entries = package.Entries[type];
  2192.  
  2193.             foreach (var file in entries)
  2194.             {
  2195.                 var filePath = string.Format("{0}.{1}", file.FileName, file.TypeName);
  2196.  
  2197.                 if (!String.IsNullOrEmpty(file.DirectoryName))
  2198.                 {
  2199.                     filePath = Path.Combine(file.DirectoryName, filePath);
  2200.                 }
  2201.  
  2202.                 filePath = FixPathSlashes(filePath);
  2203.  
  2204.                 bool found = false;
  2205.                 if (FileFilter.Count > 0)
  2206.                 {
  2207.                     foreach (string filter in FileFilter)
  2208.                     {
  2209.                       if (filePath.StartsWith(filter, StringComparison.Ordinal)) found = true;
  2210.                     }
  2211.                     if (!found) continue;
  2212.                 }
  2213.                 else if (Options.PathFilter.Count > 0)
  2214.                 {
  2215.                     foreach (string filter in Options.PathFilter)
  2216.                     {
  2217.                       if (filePath.StartsWith(filter, StringComparison.Ordinal)) found = true;
  2218.                     }
  2219.                     if (!found) continue;
  2220.                 }
  2221.  
  2222.                 if (!String.IsNullOrEmpty(Options.Output))
  2223.                 {
  2224.                     uint oldCrc32;
  2225.                     if (Options.CachedManifest && OldPakManifest.TryGetValue(filePath, out oldCrc32) && oldCrc32 == file.CRC32)
  2226.                     {
  2227.                         continue;
  2228.                     }
  2229.                     OldPakManifest[filePath] = file.CRC32;
  2230.                 }
  2231.  
  2232.                 byte[] output;
  2233.                 package.ReadEntry(file, out output);
  2234.  
  2235.                 if (!String.IsNullOrEmpty(Options.Output))
  2236.                 {
  2237.                     DumpFile(filePath, output);
  2238.                 }
  2239.             }
  2240.         }
  2241.  
  2242.         private static void DumpFile(string path, byte[] data)
  2243.         {
  2244.             var outputFile = Path.Combine(Options.Output, path);
  2245.             Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
  2246.             File.WriteAllBytes(outputFile, data);
  2247.             if (!Options.Silent) Console.WriteLine("--- Dump written to \"{0}\"", outputFile);
  2248.         }
  2249.  
  2250.         private static string FixPathSlashes(string path)
  2251.         {
  2252.             path = path.Replace('\\', '/');
  2253. //          if (Path.DirectorySeparatorChar != '/')
  2254. //          {
  2255. //              path = path.Replace('/', Path.DirectorySeparatorChar);
  2256. //          }
  2257.             return path;
  2258.         }
  2259.  
  2260.         public static void Echo(string msg, ConsoleColor clr)
  2261.         {
  2262.             lock (ConsoleWriterLock)
  2263.             {
  2264.                 Console.ForegroundColor = clr;
  2265.                 Console.Error.WriteLine(msg);
  2266.                 Console.ResetColor();
  2267.             }
  2268.         }
  2269.  
  2270.     }
  2271. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement