[
  {
    "path": ".gitattributes",
    "content": "# Declare files to normalize crlf, in case people don't have core.autocrlf set.\r\n*.conf\t\t\ttext eol=crlf\r\n*.config\t\ttext eol=crlf\r\n*.cs\t\t\ttext eol=crlf\r\n*.csproj\t\ttext eol=crlf\r\n*.csv\t\t\ttext eol=crlf\r\n*.editorconfig\t\ttext eol=crlf\r\n*.gitattributes\t\ttext eol=crlf\r\n*.gitignore\t\ttext eol=crlf\r\n*.gitmodules\t\ttext eol=crlf\r\n*.manifest\t\ttext eol=crlf\r\n*.md\t\t\ttext eol=crlf\r\n*.resx\t\t\ttext eol=crlf\r\n*.settings\t\ttext eol=crlf\r\n*.sln\t\t\ttext eol=crlf\r\n*.txt\t\t\ttext eol=crlf\r\n*.xml\t\t\ttext eol=crlf\r\n*.yml\t\t\ttext eol=crlf\r\n# Explicitly declare files you do not want to be treated as text\r\n*.bmp binary\r\n*.dll binary\r\n*.exe binary\r\n*.ico binary\r\n*.jpg binary\r\n*.md2 binary\r\n*.png binary\r\n*.rtf binary\r\n*.svg -text binary\r\n*.tga binary\r\n*.xbm binary\r\n*.zip binary\r\n*.lib filter=lfs diff=lfs merge=lfs -text\r\n"
  },
  {
    "path": ".github/workflows/dotnet-core.yml",
    "content": "name: .NET Core\r\n\r\non:\r\n  push:\r\n    branches: [ main ]\r\n  pull_request:\r\n    branches: [ main ]\r\n\r\njobs:\r\n  build:\r\n\r\n    runs-on: windows-latest\r\n\r\n    steps:\r\n    - uses: actions/checkout@v2\r\n    - name: Setup .NET Core\r\n      uses: actions/setup-dotnet@v1\r\n      with:\r\n        dotnet-version: 5.0.100\r\n    - name: Clean\r\n      run: dotnet clean ./CP77Tools.sln --configuration Release && dotnet nuget locals all --clear\r\n    - name: Install dependencies\r\n      run: dotnet restore\r\n    - name: Build\r\n      run: dotnet build --configuration Release --no-restore\r\n    - name: Upload a Build Artifact\r\n      uses: actions/upload-artifact@v2.2.1\r\n      with:\r\n        name: artifact\r\n        path: ./CP77Tools/bin/Release/net5.0-windows\r\n    - uses: papeloto/action-zip@v1\r\n      with:\r\n        files: ./CP77Tools/bin/Release/net5.0-windows\r\n        dest: release.zip\r\n    - name: Create Release\r\n      id: create_release\r\n      uses: actions/create-release@v1\r\n      env:\r\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\r\n      with:\r\n        tag_name: commit-${{ github.sha }}\r\n        draft: true\r\n    - name: Upload Release Asset\r\n      id: upload-release-asset \r\n      uses: actions/upload-release-asset@v1\r\n      env:\r\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\r\n      with:\r\n        upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps \r\n        asset_path: ./release.zip\r\n        asset_name: release.zip\r\n        asset_content_type: application/zip\r\n"
  },
  {
    "path": ".github/workflows/nightly.yml",
    "content": "name: Nightly\r\non:\r\n  push:\r\n    branches: [ nightly ]\r\n  pull_request:\r\n    branches: [ nightly ]\r\n  workflow_dispatch:\r\n\r\njobs:\r\n  build:\r\n\r\n    runs-on: windows-latest\r\n\r\n    steps:\r\n    - uses: actions/checkout@v2\r\n    - name: Setup .NET Core\r\n      uses: actions/setup-dotnet@v1\r\n      with:\r\n        dotnet-version: 5.0.100\r\n    - name: Clean\r\n      run: dotnet clean ./CP77Tools.sln --configuration Release && dotnet nuget locals all --clear\r\n    - name: Install dependencies\r\n      run: dotnet restore\r\n    - name: Build\r\n      run: dotnet build --configuration Release --no-restore\r\n    - name: Upload a Build Artifact\r\n      uses: actions/upload-artifact@v2.2.1\r\n      with:\r\n        name: artifact\r\n        path: ./CP77Tools/bin/Release/net5.0\r\n    - uses: papeloto/action-zip@v1\r\n      with:\r\n        files: ./CP77Tools/bin/Release/net5.0\r\n        dest: nightly.zip\r\n    - name: Create Release\r\n      id: create_release\r\n      uses: actions/create-release@v1\r\n      env:\r\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\r\n      with:\r\n        tag_name: commit-${{ github.sha }}\r\n        draft: true\r\n        prerelease: true\r\n    - name: Upload Nightly Asset\r\n      id: upload-release-asset \r\n      uses: actions/upload-release-asset@v1\r\n      env:\r\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\r\n      with:\r\n        upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps \r\n        asset_path: ./nightly.zip\r\n        asset_name: nightly.zip\r\n        asset_content_type: application/zip\r\n"
  },
  {
    "path": ".gitignore",
    "content": "# Prerequisites\r\n*.d\r\n\r\n# Object files\r\n*.o\r\n*.ko\r\n*.obj\r\n*.elf\r\n\r\n# Linker output\r\n*.ilk\r\n*.map\r\n*.exp\r\n\r\n# Precompiled Headers\r\n*.gch\r\n*.pch\r\n\r\n# Libraries\r\n*.lib\r\n*.a\r\n*.la\r\n*.lo\r\n\r\n# Shared objects (inc. Windows DLLs)\r\n*.dll\r\n*.so\r\n*.so.*\r\n*.dylib\r\n\r\n# Executables\r\n*.exe\r\n*.out\r\n*.app\r\n*.i*86\r\n*.x86_64\r\n*.hex\r\n\r\n# Debug files\r\n*.dSYM/\r\n*.su\r\n*.idb\r\n*.pdb\r\n\r\n# Kernel Module Compile Results\r\n*.mod*\r\n*.cmd\r\n.tmp_versions/\r\nmodules.order\r\nModule.symvers\r\nMkfile.old\r\ndkms.conf\r\n\r\n#rider\r\n.idea\r\nCP77Tools.sln.DotSettings.user\r\n\r\n#macOS\r\n.DS_Store\r\n\r\n#obj\r\n*obj/\r\n*bin/\r\n*.vs/\r\n*.suo\r\n*.user\r\n*.pubxml\r\nCP77Tools/Properties/launchSettings.json\r\nCP77Tools/Resources/archivehashes.csv\r\n\r\n#include\r\n!texconv.exe\r\n!liboodle.dylib\r\n"
  },
  {
    "path": "BUILD.md",
    "content": "# Building CP77Tools\r\n\r\n## Windows\r\n\r\n### Requirements\r\n\r\n* Ensure that the .NET 5.0 SDK is installed.\r\n\r\nUse `dotnet --list-sdks` to check if it is already installed, if not, you can get it from here: https://dotnet.microsoft.com/download/dotnet/5.0\r\n\r\n* Clone or Download the contents of the repo\r\n\r\n* Open PowerShell within the root folder (where `CP77Tools.sln` is) and run `dotnet build --configuration Release`\r\n\r\n* The compiled project can be found at `CP77Tools\\bin\\Release\\net5.0\\`\r\n\r\n## Linux\r\n\r\n???\r\n"
  },
  {
    "path": "CHANGELOG.txt",
    "content": "# Changelog\r\n\r\n\r\n\r\nv1.1.0.2\r\nImplemented\r\n- automatically copy oodle lib from game folder\r\n\r\nFixed\r\n- fixed a bug where uncooking would not extract properly\r\n\r\nv1.1\r\nImplemented\r\n- Uncook support for more extensions\r\n- extracting from missing hash list\r\n- deprecate archive command: split to unbundle, uncook\r\n- add export command\r\n\r\nFixed\r\n- update archivehashes.zip\r\n- fixed vertical flip\r\n- fixed uppercase uncook extensions\r\n- code refactoring\r\n- bugfixes\r\n"
  },
  {
    "path": "CP77.MSTests/ArchiveTests.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Configuration;\r\nusing System.IO;\r\nusing System.IO.MemoryMappedFiles;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC;\r\nusing WolvenKit.Common.Services;\r\nusing CP77.CR2W;\r\nusing CP77.CR2W.Archive;\r\nusing Microsoft.Extensions.Configuration;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Converters;\r\nusing WolvenKit.Common;\r\n\r\nnamespace CP77.MSTests\r\n{\r\n    [TestClass]\r\n    public class ArchiveTests : GameUnitTest\r\n    {\r\n        [TestMethod]\r\n        public void Test_Unbundle()\r\n        {\r\n            \r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Uncook()\r\n        {\r\n            \r\n        }\r\n\r\n\r\n        private void test_archive(string extension = null)\r\n        {\r\n            var resultDir = Path.Combine(Environment.CurrentDirectory, TestResultsDirectory);\r\n            Directory.CreateDirectory(resultDir);\r\n\r\n            var success = true;\r\n\r\n            List<Archive> archives;\r\n\r\n\r\n\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77.MSTests/CP77.MSTests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <TargetFramework>net5.0-windows</TargetFramework>\r\n\r\n    <IsPackable>false</IsPackable>\r\n  </PropertyGroup>\r\n\r\n  <ItemGroup>\r\n    <PackageReference Include=\"Microsoft.Extensions.Configuration.Json\" Version=\"5.0.0\" />\r\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"16.8.3\" />\r\n    <PackageReference Include=\"MSTest.TestAdapter\" Version=\"2.1.2\" />\r\n    <PackageReference Include=\"MSTest.TestFramework\" Version=\"2.1.2\" />\r\n    <PackageReference Include=\"coverlet.collector\" Version=\"3.0.1\">\r\n      <PrivateAssets>all</PrivateAssets>\r\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\r\n    </PackageReference>\r\n    <PackageReference Include=\"WolvenKit.Common\" Version=\"1.0.1\" />\r\n    <PackageReference Include=\"WolvenKit.Cyberformats\" Version=\"1.0.2\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <None Remove=\"appsettings.json\" />\r\n    <Content Include=\"appsettings.json\">\r\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\r\n    </Content>\r\n  </ItemGroup>\r\n\r\n</Project>\r\n"
  },
  {
    "path": "CP77.MSTests/Cr2wUnitTest.cs",
    "content": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Configuration;\r\nusing System.IO;\r\nusing System.IO.MemoryMappedFiles;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC;\r\nusing WolvenKit.Common.Services;\r\nusing CP77.CR2W;\r\nusing CP77.CR2W.Archive;\r\nusing Microsoft.Extensions.Configuration;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Converters;\r\nusing WolvenKit.Common;\r\n\r\nnamespace CP77.MSTests\r\n{\r\n    [TestClass]\r\n    public class Cr2WUnitTest : GameUnitTest\r\n    {\r\n        [ClassInitialize]\r\n        public static void SetupClass(TestContext context)\r\n        {\r\n            Setup(context);\r\n        }\r\n\r\n\r\n        #region test methods\r\n\r\n        [TestMethod]\r\n        public void Test_All_Extensions()\r\n        {\r\n            Test_Extension();\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_acousticdata()\r\n        {\r\n            Test_Extension(\".acousticdata\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_actionanimdb()\r\n        {\r\n            Test_Extension(\".actionanimdb\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_aiarch()\r\n        {\r\n            Test_Extension(\".aiarch\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_animgraph()\r\n        {\r\n            Test_Extension(\".animgraph\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_anims()\r\n        {\r\n            Test_Extension(\".anims\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_app()\r\n        {\r\n            Test_Extension(\".app\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_archetypes()\r\n        {\r\n            Test_Extension(\".archetypes\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_areas()\r\n        {\r\n            Test_Extension(\".areas\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_audio_metadata()\r\n        {\r\n            Test_Extension(\".audio_metadata\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_audiovehcurveset()\r\n        {\r\n            Test_Extension(\".audiovehcurveset\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_behavior()\r\n        {\r\n            Test_Extension(\".behavior\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_bikecurveset()\r\n        {\r\n            Test_Extension(\".bikecurveset\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_bin()\r\n        {\r\n            Test_Extension(\".bin\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_bk2()\r\n        {\r\n            Test_Extension(\".bk2\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_bnk()\r\n        {\r\n            Test_Extension(\".bnk\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_camcurveset()\r\n        {\r\n            Test_Extension(\".camcurveset\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_cfoliage()\r\n        {\r\n            Test_Extension(\".cfoliage\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_charcustpreset()\r\n        {\r\n            Test_Extension(\".charcustpreset\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_cminimap()\r\n        {\r\n            Test_Extension(\".cminimap\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_community()\r\n        {\r\n            Test_Extension(\".community\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_conversations()\r\n        {\r\n            Test_Extension(\".conversations\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_cooked_mlsetup()\r\n        {\r\n            Test_Extension(\".cooked_mlsetup\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_cookedanims()\r\n        {\r\n            Test_Extension(\".cookedanims\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_cookedapp()\r\n        {\r\n            Test_Extension(\".cookedapp\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_credits()\r\n        {\r\n            Test_Extension(\".credits\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_csv()\r\n        {\r\n            Test_Extension(\".csv\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_cubemap()\r\n        {\r\n            Test_Extension(\".cubemap\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_curveset()\r\n        {\r\n            Test_Extension(\".curveset\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_dat()\r\n        {\r\n            Test_Extension(\".dat\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_devices()\r\n        {\r\n            Test_Extension(\".devices\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_dtex()\r\n        {\r\n            Test_Extension(\".dtex\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_effect()\r\n        {\r\n            Test_Extension(\".effect\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_ent()\r\n        {\r\n            Test_Extension(\".ent\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_env()\r\n        {\r\n            Test_Extension(\".env\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_envparam()\r\n        {\r\n            Test_Extension(\".envparam\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_envprobe()\r\n        {\r\n            Test_Extension(\".envprobe\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_es()\r\n        {\r\n            Test_Extension(\".es\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_facialcustom()\r\n        {\r\n            Test_Extension(\".facialcustom\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_facialsetup()\r\n        {\r\n            Test_Extension(\".facialsetup\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_fb2tl()\r\n        {\r\n            Test_Extension(\".fb2tl\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_fnt()\r\n        {\r\n            Test_Extension(\".fnt\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_folbrush()\r\n        {\r\n            Test_Extension(\".folbrush\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_foldest()\r\n        {\r\n            Test_Extension(\".foldest\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_fp()\r\n        {\r\n            Test_Extension(\".fp\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_gamedef()\r\n        {\r\n            Test_Extension(\".gamedef\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_garmentlayerparams()\r\n        {\r\n            Test_Extension(\".garmentlayerparams\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_genericanimdb()\r\n        {\r\n            Test_Extension(\".genericanimdb\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_geometry_cache()\r\n        {\r\n            Test_Extension(\".geometry_cache\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_gidata()\r\n        {\r\n            Test_Extension(\".gidata\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_gradient()\r\n        {\r\n            Test_Extension(\".gradient\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_hitrepresentation()\r\n        {\r\n            Test_Extension(\".hitrepresentation\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_hp()\r\n        {\r\n            Test_Extension(\".hp\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_ies()\r\n        {\r\n            Test_Extension(\".ies\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkanim()\r\n        {\r\n            Test_Extension(\".inkanim\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkatlas()\r\n        {\r\n            Test_Extension(\".inkatlas\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkcharcustomization()\r\n        {\r\n            Test_Extension(\".inkcharcustomization\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkfontfamily()\r\n        {\r\n            Test_Extension(\".inkfontfamily\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkfullscreencomposition()\r\n        {\r\n            Test_Extension(\".inkfullscreencomposition\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkgamesettings()\r\n        {\r\n            Test_Extension(\".inkgamesettings\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkhud()\r\n        {\r\n            Test_Extension(\".inkhud\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inklayers()\r\n        {\r\n            Test_Extension(\".inklayers\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkmenu()\r\n        {\r\n            Test_Extension(\".inkmenu\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkshapecollection()\r\n        {\r\n            Test_Extension(\".inkshapecollection\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkstyle()\r\n        {\r\n            Test_Extension(\".inkstyle\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inktypography()\r\n        {\r\n            Test_Extension(\".inktypography\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_inkwidget()\r\n        {\r\n            Test_Extension(\".inkwidget\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_interaction()\r\n        {\r\n            Test_Extension(\".interaction\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_journal()\r\n        {\r\n            Test_Extension(\".journal\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_journaldesc()\r\n        {\r\n            Test_Extension(\".journaldesc\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_json()\r\n        {\r\n            Test_Extension(\".json\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_lane_connections()\r\n        {\r\n            Test_Extension(\".lane_connections\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_lane_polygons()\r\n        {\r\n            Test_Extension(\".lane_polygons\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_lane_spots()\r\n        {\r\n            Test_Extension(\".lane_spots\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_lights()\r\n        {\r\n            Test_Extension(\".lights\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_lipmap()\r\n        {\r\n            Test_Extension(\".lipmap\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_location()\r\n        {\r\n            Test_Extension(\".location\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_locopaths()\r\n        {\r\n            Test_Extension(\".locopaths\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_loot()\r\n        {\r\n            Test_Extension(\".loot\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_mappins()\r\n        {\r\n            Test_Extension(\".mappins\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_mesh()\r\n        {\r\n            Test_Extension(\".mesh\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_mi()\r\n        {\r\n            Test_Extension(\".mi\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_mlmask()\r\n        {\r\n            Test_Extension(\".mlmask\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_mlsetup()\r\n        {\r\n            Test_Extension(\".mlsetup\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_mltemplate()\r\n        {\r\n            Test_Extension(\".mltemplate\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_morphtarget()\r\n        {\r\n            Test_Extension(\".morphtarget\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_mt()\r\n        {\r\n            Test_Extension(\".mt\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_navmesh()\r\n        {\r\n            Test_Extension(\".navmesh\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_null_areas()\r\n        {\r\n            Test_Extension(\".null_areas\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_opusinfo()\r\n        {\r\n            Test_Extension(\".opusinfo\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_opuspak()\r\n        {\r\n            Test_Extension(\".opuspak\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_particle()\r\n        {\r\n            Test_Extension(\".particle\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_phys()\r\n        {\r\n            Test_Extension(\".phys\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_physicalscene()\r\n        {\r\n            Test_Extension(\".physicalscene\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_physmatlib()\r\n        {\r\n            Test_Extension(\".physmatlib\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_poimappins()\r\n        {\r\n            Test_Extension(\".poimappins\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_psrep()\r\n        {\r\n            Test_Extension(\".psrep\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_quest()\r\n        {\r\n            Test_Extension(\".quest\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_questphase()\r\n        {\r\n            Test_Extension(\".questphase\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_regionset()\r\n        {\r\n            Test_Extension(\".regionset\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_remt()\r\n        {\r\n            Test_Extension(\".remt\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_reslist()\r\n        {\r\n            Test_Extension(\".reslist\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_rig()\r\n        {\r\n            Test_Extension(\".rig\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_scene()\r\n        {\r\n            Test_Extension(\".scene\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_scenerid()\r\n        {\r\n            Test_Extension(\".scenerid\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_scenesversions()\r\n        {\r\n            Test_Extension(\".scenesversions\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_smartobject()\r\n        {\r\n            Test_Extension(\".smartobject\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_smartobjects()\r\n        {\r\n            Test_Extension(\".smartobjects\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_sp()\r\n        {\r\n            Test_Extension(\".sp\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_spatial_representation()\r\n        {\r\n            Test_Extension(\".spatial_representation\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_streamingquerydata()\r\n        {\r\n            Test_Extension(\".streamingquerydata\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_streamingsector()\r\n        {\r\n            Test_Extension(\".streamingsector\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_streamingsector_inplace()\r\n        {\r\n            Test_Extension(\".streamingsector_inplace\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_streamingworld()\r\n        {\r\n            Test_Extension(\".streamingworld\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_terrainsetup()\r\n        {\r\n            Test_Extension(\".terrainsetup\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_texarray()\r\n        {\r\n            Test_Extension(\".texarray\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_traffic_collisions()\r\n        {\r\n            Test_Extension(\".traffic_collisions\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_traffic_persistent()\r\n        {\r\n            Test_Extension(\".traffic_persistent\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_voicetags()\r\n        {\r\n            Test_Extension(\".voicetags\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_w2mesh()\r\n        {\r\n            Test_Extension(\".w2mesh\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_w2mi()\r\n        {\r\n            Test_Extension(\".w2mi\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_wem()\r\n        {\r\n            Test_Extension(\".wem\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_workspot()\r\n        {\r\n            Test_Extension(\".workspot\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_xbm()\r\n        {\r\n            Test_Extension(\".xbm\");\r\n        }\r\n\r\n        [TestMethod]\r\n        public void Test_Can_Read_xcube()\r\n        {\r\n            Test_Extension(\".xcube\");\r\n        }\r\n\r\n        #endregion\r\n\r\n        private void Test_Extension(string extension = null)\r\n        {\r\n            var resultDir = Path.Combine(Environment.CurrentDirectory, TestResultsDirectory);\r\n            Directory.CreateDirectory(resultDir);\r\n\r\n            var success = true;\r\n\r\n            List<string> files;\r\n\r\n            if (string.IsNullOrEmpty(extension))\r\n                files = GroupedFiles.Keys.ToList();\r\n            else\r\n                files = GroupedFiles.Keys.Where(k => k.Equals(extension)).ToList();\r\n\r\n            var sb = new StringBuilder();\r\n            \r\n            Parallel.ForEach(files, ext =>\r\n            {\r\n                var results = Read_Archive_Items(GroupedFiles[ext]);\r\n\r\n                var successCount = results.Count(r => r.Success);\r\n                var totalCount = GroupedFiles[ext].Count;\r\n                \r\n                sb.AppendLine($\"{ext} -> Successful Reads: {successCount} / {totalCount} ({(int)(((double)successCount / (double)totalCount) * 100)}%)\");\r\n\r\n                if (success && results.Any(r => !r.Success)) success = false;\r\n                \r\n                if (WriteToFile)\r\n                {\r\n                    if (results.Any(r => !r.Success))\r\n                    {\r\n                        var resultPath = Path.Combine(resultDir, $\"{ext[1..]}.csv\");\r\n                        var csv = TestResultAsCsv(results.Where(r => !r.Success));\r\n                        File.WriteAllText(resultPath, csv);\r\n                    }\r\n                }\r\n            });\r\n            \r\n            var logPath = Path.Combine(resultDir, $\"logfile_{(string.IsNullOrEmpty(extension) ? string.Empty : $\"{extension[1..]}_\")}{DateTime.Now:yyyyMMddHHmmss}.log\");\r\n            File.WriteAllText(logPath, sb.ToString());\r\n            Console.WriteLine(sb.ToString());\r\n\r\n            if (!success) Assert.Fail();\r\n        }\r\n\r\n        private IEnumerable<TestResult> Read_Archive_Items(List<ArchiveItem> files)\r\n        {\r\n            var results = new List<TestResult>();\r\n            \r\n            foreach (var file in files)\r\n                try\r\n                {\r\n                    var (fileBytes, bufferBytes) = (file.Archive as Archive).GetFileData(file.NameHash64, false);\r\n\r\n                    using var ms = new MemoryStream(fileBytes);\r\n                    using var br = new BinaryReader(ms);\r\n\r\n                    var c = new CR2WFile();\r\n                    var readResult = c.Read(br);\r\n\r\n                    switch (readResult)\r\n                    {\r\n                        case EFileReadErrorCodes.NoCr2w:\r\n                            results.Add(new TestResult\r\n                            {\r\n                                ArchiveItem = file,\r\n                                Success = true,\r\n                                Result = TestResult.ResultType.NoCr2W\r\n                            });\r\n                            break;\r\n                        case EFileReadErrorCodes.UnsupportedVersion:\r\n                            results.Add(new TestResult\r\n                            {\r\n                                ArchiveItem = file,\r\n                                Success = false,\r\n                                Result = TestResult.ResultType.UnsupportedVersion,\r\n                                Message = $\"Unsupported Version ({c.GetFileHeader().version})\"\r\n                            });\r\n                            break;\r\n                        default:\r\n                            var hasAdditionalBytes = c.additionalCr2WFileBytes != null && c.additionalCr2WFileBytes.Any();\r\n                            results.Add(new TestResult\r\n                            {\r\n                                ArchiveItem = file,\r\n                                Success = !hasAdditionalBytes,\r\n                                Result = hasAdditionalBytes ? TestResult.ResultType.HasAdditionalBytes : TestResult.ResultType.NoError,\r\n                                Message = hasAdditionalBytes ? $\"Additional Bytes: {c.additionalCr2WFileBytes.Length}\" : null,\r\n                                AdditionalBytes = hasAdditionalBytes ? c.additionalCr2WFileBytes.Length : 0\r\n                            });\r\n                            break;\r\n                    }\r\n                }\r\n                catch (Exception e)\r\n                {\r\n                    results.Add(new TestResult\r\n                    {\r\n                        ArchiveItem = file,\r\n                        Success = false,\r\n                        Result = TestResult.ResultType.RuntimeException,\r\n                        ExceptionType = e.GetType(),\r\n                        Message = e.Message\r\n                    });\r\n                }\r\n\r\n            return results;\r\n        }\r\n\r\n        private string TestResultAsCsv(IEnumerable<TestResult> results)\r\n        {\r\n            var sb = new StringBuilder();\r\n\r\n            sb.AppendLine(\r\n                $@\"{nameof(ArchiveItem.NameHash64)},{nameof(ArchiveItem.FileName)},{nameof(TestResult.Result)},{nameof(TestResult.Success)},{nameof(TestResult.AdditionalBytes)},{nameof(TestResult.ExceptionType)},{nameof(TestResult.Message)}\");\r\n\r\n            foreach (var r in results)\r\n            {\r\n                sb.AppendLine(\r\n                    $\"{r.ArchiveItem.NameHash64},{r.ArchiveItem.FileName},{r.Result},{r.Success},{r.AdditionalBytes},{r.ExceptionType?.FullName},{r.Message}\");\r\n            }\r\n\r\n            return sb.ToString();\r\n        }\r\n\r\n        private class TestResult\r\n        {\r\n            public enum ResultType\r\n            {\r\n                NoCr2W,\r\n                UnsupportedVersion,\r\n                RuntimeException,\r\n                HasAdditionalBytes,\r\n                NoError\r\n            }\r\n            \r\n            public ArchiveItem ArchiveItem { get; set; }\r\n            \r\n            [JsonConverter(typeof(StringEnumConverter))]\r\n            public ResultType Result { get; set; }\r\n            public int AdditionalBytes { get; set; }\r\n            public bool Success { get; set; }\r\n            public Type ExceptionType { get; set; }\r\n            public string Message { get; set; }\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77.MSTests/FNV1A64Tests.cs",
    "content": "using System;\r\nusing System.Text;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing WolvenKit.Common.FNV1A;\r\n\r\nnamespace CP77.MSTests\r\n{\r\n    [TestClass]\r\n    public class FNV1A64Tests\r\n    {\r\n        [TestMethod]\r\n        [DataRow(\"\", 0xcbf29ce484222325UL)]\r\n        [DataRow(\"a\", 0xaf63dc4c8601ec8cUL)]\r\n        [DataRow(\"foobar\", 0x85944171f73967e8UL)]\r\n        [DataRow(\"\\0\", 0xaf63bd4c8601b7dfUL)]\r\n        [DataRow(\"a\\0\", 0x089be207b544f1e4UL)]\r\n        [DataRow(\"foobar\\0\", 0x34531ca7168b8f38UL)]\r\n        [DataRow(\"hi\", 0x08ba5f07b55ec3daUL)]\r\n        [DataRow(\"hi\\0\", 0x337354193006cb6eUL)]\r\n        [DataRow(\"hello\", 0xa430d84680aabd0bUL)]\r\n        [DataRow(\"hello\\0\", 0xa9bc8acca21f39b1UL)]\r\n        [DataRow(\"127.0.0.1\", 0xaabafe7104d914beUL)]\r\n        [DataRow(\"127.0.0.1\\0\", 0xf4d3180b3cde3edaUL)]\r\n        [DataRow(\"127.0.0.2\", 0xaabafd7104d9130bUL)]\r\n        [DataRow(\"127.0.0.2\\0\", 0xf4cfb20b3cdb5bb1UL)]\r\n        [DataRow(\"127.0.0.3\", 0xaabafc7104d91158UL)]\r\n        [DataRow(\"127.0.0.3\\0\", 0xf4cc4c0b3cd87888UL)]\r\n        [DataRow(\"feedfacedeadbeef\", 0xcac54572bb1a6fc8UL)]\r\n        [DataRow(\"feedfacedeadbeef\\0\", 0xa7a4c9f3edebf0d8UL)]\r\n        [DataRow(\"line 1\\nline 2\\nline 3\", 0x7829851fac17b143UL)]\r\n        public void TestFNV1a64(string test, ulong result)\r\n        {\r\n            // Assert.AreEqual(FNV1A64HashAlgorithm.HashString(test), result);\r\n            Assert.AreEqual(FNV1A64HashAlgorithm.HashString(test), result);\r\n        }\r\n\r\n        [TestMethod]\r\n        [DataRow(\"\", 0xaf63bd4c8601b7dfUL)]\r\n        [DataRow(\"a\", 0x089be207b544f1e4UL)]\r\n        [DataRow(\"hi\", 0x337354193006cb6eUL)]\r\n        [DataRow(\"hello\", 0xa9bc8acca21f39b1UL)]\r\n        [DataRow(\"foobar\", 0x34531ca7168b8f38UL)]\r\n        [DataRow(\"feedfacedeadbeef\", 0xa7a4c9f3edebf0d8UL)]\r\n        public void TestFNV1a64_NullEnded(string test, ulong result)\r\n        {\r\n            Assert.AreEqual(FNV1A64HashAlgorithm.HashString(test, Encoding.ASCII, true), result);\r\n        }\r\n\r\n        [TestMethod]\r\n        [DataRow(new byte[] {0xff, 0x00, 0x00, 0x01}, 0x6961196491cc682dUL)]\r\n        [DataRow(new byte[] {0x01, 0x00, 0x00, 0xff}, 0xad2bb1774799dfe9UL)]\r\n        [DataRow(new byte[] {0xff, 0x00, 0x00, 0x02}, 0x6961166491cc6314UL)]\r\n        [DataRow(new byte[] {0x02, 0x00, 0x00, 0xff}, 0x8d1bb3904a3b1236UL)]\r\n        [DataRow(new byte[] {0xff, 0x00, 0x00, 0x03}, 0x6961176491cc64c7UL)]\r\n        [DataRow(new byte[] {0x03, 0x00, 0x00, 0xff}, 0xed205d87f40434c7UL)]\r\n        [DataRow(new byte[] {0xff, 0x00, 0x00, 0x04}, 0x6961146491cc5faeUL)]\r\n        [DataRow(new byte[] {0x04, 0x00, 0x00, 0xff}, 0xcd3baf5e44f8ad9cUL)]\r\n        [DataRow(new byte[] {0x40, 0x51, 0x4e, 0x44}, 0xe3b36596127cd6d8UL)]\r\n        [DataRow(new byte[] {0x44, 0x4e, 0x51, 0x40}, 0xf77f1072c8e8a646UL)]\r\n        [DataRow(new byte[] {0x40, 0x51, 0x4e, 0x4a}, 0xe3b36396127cd372UL)]\r\n        [DataRow(new byte[] {0x4a, 0x4e, 0x51, 0x40}, 0x6067dce9932ad458UL)]\r\n        [DataRow(new byte[] {0x40, 0x51, 0x4e, 0x54}, 0xe3b37596127cf208UL)]\r\n        [DataRow(new byte[] {0x54, 0x4e, 0x51, 0x40}, 0x4b7b10fa9fe83936UL)]\r\n        public void TestFNV1a64_ByteSpan(byte[] test, ulong result)\r\n        {\r\n            Assert.AreEqual(FNV1A64HashAlgorithm.HashReadOnlySpan(test), result);\r\n        }       \r\n        \r\n        [TestMethod]\r\n        [DataRow(\"\\xff\\x00\\x00\\x01\", 0x6961196491cc682dUL)]\r\n        [DataRow(\"\\x01\\x00\\x00\\xff\", 0xad2bb1774799dfe9UL)]\r\n        [DataRow(\"\\xff\\x00\\x00\\x02\", 0x6961166491cc6314UL)]\r\n        [DataRow(\"\\x02\\x00\\x00\\xff\", 0x8d1bb3904a3b1236UL)]\r\n        [DataRow(\"\\xff\\x00\\x00\\x03\", 0x6961176491cc64c7UL)]\r\n        [DataRow(\"\\x03\\x00\\x00\\xff\", 0xed205d87f40434c7UL)]\r\n        [DataRow(\"\\xff\\x00\\x00\\x04\", 0x6961146491cc5faeUL)]\r\n        [DataRow(\"\\x04\\x00\\x00\\xff\", 0xcd3baf5e44f8ad9cUL)]\r\n        [DataRow(\"\\x40\\x51\\x4e\\x44\", 0xe3b36596127cd6d8UL)]\r\n        [DataRow(\"\\x44\\x4e\\x51\\x40\", 0xf77f1072c8e8a646UL)]\r\n        [DataRow(\"\\x40\\x51\\x4e\\x4a\", 0xe3b36396127cd372UL)]\r\n        [DataRow(\"\\x4a\\x4e\\x51\\x40\", 0x6067dce9932ad458UL)]\r\n        [DataRow(\"\\x40\\x51\\x4e\\x54\", 0xe3b37596127cf208UL)]\r\n        [DataRow(\"\\x54\\x4e\\x51\\x40\", 0x4b7b10fa9fe83936UL)]\r\n        [DataRow(\"\", 0xcbf29ce484222325UL)]\r\n        [DataRow(\"a\", 0xaf63dc4c8601ec8cUL)]\r\n        [DataRow(\"foobar\", 0x85944171f73967e8UL)]\r\n        [DataRow(\"\\0\", 0xaf63bd4c8601b7dfUL)]\r\n        [DataRow(\"a\\0\", 0x089be207b544f1e4UL)]\r\n        [DataRow(\"foobar\\0\", 0x34531ca7168b8f38UL)]\r\n        [DataRow(\"hi\", 0x08ba5f07b55ec3daUL)]\r\n        [DataRow(\"hi\\0\", 0x337354193006cb6eUL)]\r\n        [DataRow(\"hello\", 0xa430d84680aabd0bUL)]\r\n        [DataRow(\"hello\\0\", 0xa9bc8acca21f39b1UL)]\r\n        [DataRow(\"127.0.0.1\", 0xaabafe7104d914beUL)]\r\n        [DataRow(\"127.0.0.1\\0\", 0xf4d3180b3cde3edaUL)]\r\n        [DataRow(\"127.0.0.2\", 0xaabafd7104d9130bUL)]\r\n        [DataRow(\"127.0.0.2\\0\", 0xf4cfb20b3cdb5bb1UL)]\r\n        [DataRow(\"127.0.0.3\", 0xaabafc7104d91158UL)]\r\n        [DataRow(\"127.0.0.3\\0\", 0xf4cc4c0b3cd87888UL)]\r\n        [DataRow(\"feedfacedeadbeef\", 0xcac54572bb1a6fc8UL)]\r\n        [DataRow(\"feedfacedeadbeef\\0\", 0xa7a4c9f3edebf0d8UL)]\r\n        [DataRow(\"line 1\\nline 2\\nline 3\", 0x7829851fac17b143UL)]\r\n        public void TestFNV1a64_CharSpan(string test, ulong result)\r\n        {\r\n            Assert.AreEqual(FNV1A64HashAlgorithm.HashReadOnlySpan(test.AsSpan()), result);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77.MSTests/GameUnitTest.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Configuration;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC;\r\nusing CP77.CR2W.Archive;\r\nusing Microsoft.Extensions.Configuration;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77.MSTests\r\n{\r\n    [TestClass]\r\n    public class GameUnitTest\r\n    {\r\n        private const string GameDirectorySetting = \"GameDirectory\";\r\n        private const string WriteToFileSetting = \"WriteToFile\";\r\n\r\n        private static ArchiveManager bm;\r\n        internal static Dictionary<string, List<ArchiveItem>> GroupedFiles;\r\n\r\n        private static IConfigurationRoot _config;\r\n\r\n        internal const string TestResultsDirectory = \"_CR2WTestResults\";\r\n        private static string _gameDirectoryPath;\r\n        internal static bool WriteToFile;\r\n\r\n        protected static void Setup(TestContext context)\r\n        {\r\n            Console.WriteLine(\"BaseTestClass.BaseTestInitialize()\");\r\n\r\n            _config = new ConfigurationBuilder()\r\n                .AddJsonFile(\"appsettings.json\")\r\n                .Build();\r\n\r\n            // check for CP77_DIR environment variable first\r\n            // overrides hardcoded appsettings.json\r\n            var CP77_DIR = System.Environment.GetEnvironmentVariable(\"CP77_DIR\", EnvironmentVariableTarget.User);\r\n            if (!string.IsNullOrEmpty(CP77_DIR) && new DirectoryInfo(CP77_DIR).Exists)\r\n                _gameDirectoryPath = CP77_DIR;\r\n            else\r\n                _gameDirectoryPath = _config.GetSection(GameDirectorySetting).Value;\r\n            if (string.IsNullOrEmpty(_gameDirectoryPath))\r\n                throw new ConfigurationErrorsException($\"'{GameDirectorySetting}' is not configured\");\r\n            var gameDirectory = new DirectoryInfo(_gameDirectoryPath);\r\n            if (!gameDirectory.Exists)\r\n                throw new ConfigurationErrorsException($\"'{GameDirectorySetting}' is not a valid directory\");\r\n\r\n            WriteToFile = Boolean.Parse(_config.GetSection(WriteToFileSetting).Value);\r\n\r\n            ServiceLocator.Default.RegisterType<ILoggerService, LoggerService>();\r\n            ServiceLocator.Default.RegisterType<IHashService, HashService>();\r\n\r\n            var hashService = ServiceLocator.Default.ResolveType<IHashService>();\r\n            hashService.ReloadLocally();\r\n\r\n            DirectoryInfo gameArchiveDir;\r\n            try\r\n            {\r\n                gameArchiveDir = new DirectoryInfo(Path.Combine(gameDirectory.FullName, \"archive\", \"pc\", \"content\"));\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                Console.WriteLine(e);\r\n                throw;\r\n            }\r\n\r\n            bm = new ArchiveManager(gameArchiveDir);\r\n            GroupedFiles = bm.GroupedFiles;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "CP77.MSTests/appsettings.json",
    "content": "﻿{\n    \"GameDirectory\": \"D:\\\\Program Files\\\\Steam\\\\steamapps\\\\common\\\\Cyberpunk 2077\",\n    \"WriteToFile\": true\n}"
  },
  {
    "path": "CP77Tools/CP77Tools.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n  <PropertyGroup>\r\n    <OutputType>Exe</OutputType>\r\n    <TargetFramework>net5.0-windows</TargetFramework>\r\n    <AssemblyVersion>1.1.0.2</AssemblyVersion>\r\n    <FileVersion>1.1.0.2</FileVersion>\r\n\t<PublishSingleFile>true</PublishSingleFile>\r\n\t  <SelfContained>false</SelfContained>\r\n\t  <RuntimeIdentifier>win-x64</RuntimeIdentifier>\r\n\t  <PublishTrimmed>true</PublishTrimmed>\r\n\t  <PublishReadyToRun>true</PublishReadyToRun>\r\n\t<DebugType>embedded</DebugType>\r\n  </PropertyGroup>\r\n\r\n\t<ItemGroup>\r\n    <None Remove=\"lib\\archivehashes.zip\" />\r\n    <None Remove=\"lib\\kraken.so\" />\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Content Include=\"lib\\kraken.so\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </Content>\r\n    <None Remove=\"lib\\liboodle.dylib\" />\r\n    <Content Include=\"lib\\liboodle.dylib\">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </Content>\r\n  </ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <PackageReference Include=\"Luna.ConsoleProgressBar\" Version=\"1.0.29\" />\r\n    <PackageReference Include=\"CsvHelper\" Version=\"21.2.1\" />\r\n    <PackageReference Include=\"System.CommandLine\" Version=\"2.0.0-beta1.20574.7\" />\r\n\t<PackageReference Include=\"WolvenKit.Common\" Version=\"1.0.3\" GeneratePathProperty=\"true\" />\r\n    <PackageReference Include=\"WolvenKit.Cyberformats\" Version=\"1.0.5\" />\r\n  </ItemGroup>\r\n\r\n\t<ItemGroup>\r\n\t\t<None Include=\"$(PkgWolvenKit_Common)\\contentFiles\\any\\net5.0-windows7.0\\texconv.exe\" CopyToOutputDirectory=\"PreserveNewest\" />\r\n\t</ItemGroup>\r\n\r\n  <ItemGroup>\r\n    <Content Include=\"lib\\\\kraken.dll\" Condition=\" '$(OS)' == 'Windows_NT' \">\r\n      <CopyToOutputDirectory>Always</CopyToOutputDirectory>\r\n    </Content>\r\n    <Content Include=\"lib\\\\kraken.so\" Condition=\" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' \">\r\n      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\r\n    </Content>\r\n  </ItemGroup>\r\n\r\n</Project>\r\n"
  },
  {
    "path": "CP77Tools/Commands/CR2WCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class CR2WCommand : Command\r\n    {\r\n        private static string Name = \"cr2w\";\r\n        private static string Description = \"Target a specific cr2w (extracted) file and dumps file information.\";\r\n\r\n        public CR2WCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--path\", \"-p\"}, \"Input path to a cr2w file.\"));\r\n            AddOption(new Option<string>(new []{\"--outpath\", \"-o\"}, \"Output path.\"));\r\n            AddOption(new Option<bool>(new[] {\"--all\", \"-a\"}, \"Dump all information.\"));\r\n            AddOption(new Option<bool>(new[] {\"--chunks\", \"-c\"}, \"Dump all class information of file.\"));\r\n\r\n            Handler = CommandHandler.Create<string[], string, bool, bool>(Tasks.ConsoleFunctions.Cr2wTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/DumpCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class DumpCommand : Command\r\n    {\r\n        private static string Name = \"dump\";\r\n        private static string Description = \"Target an archive or a directory to dump archive information.\";\r\n        \r\n        public DumpCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--path\", \"-p\"}, \"Input path to .archive or to a directory (runs over all archives in directory).\"));\r\n            AddOption(new Option<bool>(new[] {\"--imports\", \"-i\"}, \"Dump all imports (all filenames that are referenced by all files in the archive).\"));\r\n            AddOption(new Option<bool>(new[] {\"--missinghashes\", \"-m\"}, \"List all missing hashes of all input archives.\"));\r\n            AddOption(new Option<bool>(new[] {\"--texinfo\"}, \"Dump all xbm info.\"));\r\n            AddOption(new Option<bool>(new[] {\"--classinfo\"}, \"Dump all class info.\"));\r\n            AddOption(new Option<bool>(new[] {\"--dump\", \"-d\"}, \"Dump archive information.\"));\r\n            AddOption(new Option<bool>(new[] {\"--list\", \"-l\"}, \"List contents of archive.\"));\r\n\r\n            \r\n            Handler = CommandHandler.Create<string[], bool, bool, bool, bool, bool, bool>(ConsoleFunctions.DumpTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/ExportCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing WolvenKit.Common.DDS;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class ExportCommand : Command\r\n    {\r\n        private static string Name = \"export\";\r\n        private static string Description = \"Export a file or a list of files into raw files.\";\r\n        \r\n        public ExportCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--path\", \"-p\"}, \"Input path. Must be a file or a list of files.\"));\r\n            AddOption(new Option<EUncookExtension>(new[] { \"--uext\" }, \"Uncook extension (tga, bmp, jpg, png, dds). Default is tga.\"));\r\n\r\n            Handler = CommandHandler.Create<string[], EUncookExtension>(Tasks.ConsoleFunctions.ExportTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/HashCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class HashCommand : Command\r\n    {\r\n        private static string Name = \"hash\";\r\n        private static string Description = \"Some helper functions related to hashes.\";\r\n\r\n        public HashCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--input\", \"-i\"}, \"Create FNV1A hash of given string\"));\r\n            AddOption(new Option<bool>(new[] {\"--missing\", \"-m\"}, \"\"));\r\n\r\n            Handler = CommandHandler.Create<string[], bool>(ConsoleFunctions.HashTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/OodleCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class OodleCommand : Command\r\n    {\r\n        private static string Name = \"oodle\";\r\n        private static string Description = \"Some helper functions related to oodle compression.\";\r\n\r\n        public OodleCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string>(new[] {\"--path\", \"-p\"}, \"\"));\r\n            AddOption(new Option<string>(new []{\"--outpath\", \"-o\"}, \"\"));\r\n            AddOption(new Option<bool>(new[] {\"--decompress\", \"-d\"}, \"\"));\r\n            \r\n            Handler = CommandHandler.Create<string,string, bool>(Tasks.ConsoleFunctions.OodleTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/PackCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class PackCommand : Command\r\n    {\r\n        private static string Name = \"pack\";\r\n        private static string Description = \"Pack a folder of files into an .archive file.\";\r\n        \r\n        public PackCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--path\", \"-p\"}, \"Input path. Must be a folder or a list of folders.\"));\r\n            AddOption(new Option<string>(new[] {\"--outpath\", \"-o\"}, \"Output directory to create archive.\\nIf not specified, will output twill be in place.\"));\r\n            \r\n            Handler = CommandHandler.Create<string[], string>(Tasks.ConsoleFunctions.PackTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/RebuildCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class RebuildCommand : Command\r\n    {\r\n        private static string Name = \"rebuild\";\r\n        private static string Description = \"Recombine split buffers and textures in a folder.\";\r\n        \r\n        public RebuildCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--path\", \"-p\"}, \r\n                \"Input path. Must be a folder or a list of folders.\"));\r\n            AddOption(new Option<bool>(new[] {\"--buffers\", \"-b\"}, \r\n                \"Recombine buffers\"));\r\n            AddOption(new Option<bool>(new[] {\"--textures\", \"-t\"}, \r\n                \"Recombine textures\"));\r\n            AddOption(new Option<bool>(new[] {\"--import\", \"-i\"}, \r\n                \"Optionally import missing raw files into their redengine formats.\"));\r\n            AddOption(new Option<bool>(new[] {\"--keep\"}, \r\n                \"Optionally keep existing cr2w files intact and only append the buffer\"));\r\n            AddOption(new Option<bool>(new[] {\"--clean\"}, \r\n                \"Optionally remove buffers and textures after successful recombination.\"));\r\n            AddOption(new Option<bool>(new[] {\"--unsaferaw\"}, \r\n                \"Optionally add raw assets (dds textures, fbx) as buffers without check.\"));\r\n\r\n            Handler = CommandHandler.Create<string[], bool, bool, bool, bool, bool, bool>(Tasks.ConsoleFunctions.RebuildTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/UnbundleCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class UnbundleCommand : Command\r\n    {\r\n        private static string Name = \"unbundle\";\r\n        private static string Description = \"Target an archive to extract files.\";\r\n        \r\n        public UnbundleCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--path\", \"-p\"}, \"Input path to .archive.\"));\r\n            AddOption(new Option<string>(new[] {\"--outpath\", \"-o\"}, \"Output directory to extract files to.\"));\r\n            AddOption(new Option<string>(new[] {\"--pattern\", \"-w\"}, \"Use optional search pattern, e.g. *.ink. If both regex and pattern is defined, pattern will be used first\"));\r\n            AddOption(new Option<string>(new[] {\"--regex\", \"-r\"}, \"Use optional regex pattern.\"));\r\n            AddOption(new Option<string>(new[] {\"--hash\"}, \"Extract single file with given hash. If a path is supplied it extracts all hashes from that.\"));\r\n            \r\n            Handler = CommandHandler.Create<string[], string, string, string, string>(ConsoleFunctions.UnbundleTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Commands/UncookCommand.cs",
    "content": "﻿using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\nusing WolvenKit.Common.DDS;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n    public class UncookCommand : Command\r\n    {\r\n        private static string Name = \"uncook\";\r\n        private static string Description = \"Target an archive to uncook files.\";\r\n        \r\n        public UncookCommand() : base(Name, Description)\r\n        {\r\n            AddOption(new Option<string[]>(new[] {\"--path\", \"-p\"}, \"Input path to .archive.\"));\r\n            AddOption(new Option<string>(new[] {\"--outpath\", \"-o\"}, \"Output directory to extract files to.\"));\r\n            AddOption(new Option<string>(new[] {\"--pattern\", \"-w\"}, \"Use optional search pattern, e.g. *.ink. If both regex and pattern is defined, pattern will be used first\"));\r\n            AddOption(new Option<string>(new[] {\"--regex\", \"-r\"}, \"Use optional regex pattern.\"));\r\n            AddOption(new Option<EUncookExtension>(new[] {\"--uext\"}, \"Uncook extension (tga, bmp, jpg, png, dds). Default is tga.\"));\r\n            AddOption(new Option<bool>(new[] { \"--flip\", \"-f\" }, \"Flips textures vertically\"));\r\n            AddOption(new Option<ulong>(new[] {\"--hash\"}, \"Extract single file with given hash.\"));\r\n            \r\n            Handler = CommandHandler.Create<string[], string, \r\n                EUncookExtension, bool, ulong, string, string>(ConsoleFunctions.UncookTask);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Extensions/CommandLineExtensions.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\n\r\nnamespace CP77Tools.Extensions\r\n{\r\n    public class CommandLineExtensions\r\n    {\r\n        public static IEnumerable<String> ParseText(String line, Char delimiter, Char textQualifier)\r\n        {\r\n\r\n            if (line == null)\r\n                yield break;\r\n\r\n            else\r\n            {\r\n                Char prevChar = '\\0';\r\n                Char nextChar = '\\0';\r\n                Char currentChar = '\\0';\r\n\r\n                Boolean inString = false;\r\n\r\n                StringBuilder token = new StringBuilder();\r\n\r\n                for (int i = 0; i < line.Length; i++)\r\n                {\r\n                    currentChar = line[i];\r\n\r\n                    if (i > 0)\r\n                        prevChar = line[i - 1];\r\n                    else\r\n                        prevChar = '\\0';\r\n\r\n                    if (i + 1 < line.Length)\r\n                        nextChar = line[i + 1];\r\n                    else\r\n                        nextChar = '\\0';\r\n\r\n                    if (currentChar == textQualifier && prevChar != 0x5c && !inString)\r\n                    {\r\n                        inString = true;\r\n                        continue;\r\n                    }\r\n\r\n                    if (currentChar == textQualifier && inString)\r\n                    {\r\n                        inString = false;\r\n                        continue;\r\n                    }\r\n\r\n                    if (currentChar == delimiter && !inString)\r\n                    {\r\n                        yield return token.ToString();\r\n                        token = token.Remove(0, token.Length);\r\n                        continue;\r\n                    }\r\n\r\n                    token = token.Append(currentChar);\r\n\r\n                }\r\n\r\n                yield return token.ToString();\r\n\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "CP77Tools/Program.cs",
    "content": "﻿using System;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC;\r\nusing System.CommandLine;\r\nusing System.ComponentModel;\r\nusing System.Configuration;\r\nusing System.IO;\r\nusing System.Reflection;\r\nusing CP77Tools.Commands;\r\nusing CP77Tools.Extensions;\r\nusing Luna.ConsoleProgressBar;\r\nusing Microsoft.Win32;\r\nusing WolvenKit.Common.Oodle;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools\r\n{\r\n    class Program\r\n    {\r\n        [STAThread]\r\n        public static async Task Main(string[] args)\r\n        {\r\n            ServiceLocator.Default.RegisterType<ILoggerService, LoggerService>();\r\n            ServiceLocator.Default.RegisterType<IHashService, HashService>();\r\n\r\n            var logger = ServiceLocator.Default.ResolveType<ILoggerService>();\r\n            var hashService = ServiceLocator.Default.ResolveType<IHashService>();\r\n            logger.OnStringLogged += delegate (object? sender, LogStringEventArgs args)\r\n            {\r\n                switch (args.Logtype)\r\n                {\r\n                    \r\n                    case Logtype.Error:\r\n                        Console.ForegroundColor = ConsoleColor.Red;\r\n                        break;\r\n                    case Logtype.Important:\r\n                        Console.ForegroundColor = ConsoleColor.Yellow;\r\n                        break;\r\n                    case Logtype.Success:\r\n                        Console.ForegroundColor = ConsoleColor.Green;\r\n                        break;\r\n                    case Logtype.Normal:\r\n                    case Logtype.Wcc:\r\n                        break;\r\n                    default:\r\n                        throw new ArgumentOutOfRangeException();\r\n                }\r\n                \r\n                Console.WriteLine(\"[\" + args.Logtype + \"]\" + args.Message);\r\n                Console.ResetColor();\r\n            };\r\n\r\n            var rootCommand = new RootCommand\r\n            {\r\n                new UnbundleCommand(),\r\n                new UncookCommand(),\r\n                new RebuildCommand(),\r\n                new PackCommand(),\r\n                new ExportCommand(),\r\n                \r\n                new DumpCommand(),\r\n                new CR2WCommand(),\r\n                \r\n                new HashCommand(),\r\n                new OodleCommand(),\r\n            };\r\n\r\n            //await ConsoleFunctions.UpdateHashesAsync();\r\n            hashService.ReloadLocally();\r\n\r\n            // try get oodle dll from game\r\n            if (!TryCopyOodleLib())\r\n            {\r\n                logger.LogString(\"Could not automatically find oo2ext_7_win64.dll. \" +\r\n                                 \"Please manually copy and paste the dll found here Cyberpunk 2077\\\\bin\\\\x64\\\\oo2ext_7_win64.dll into this folder: \" +\r\n                                 $\"{AppDomain.CurrentDomain.BaseDirectory}.\");\r\n            }\r\n\r\n            // Run\r\n            if (args == null || args.Length == 0)\r\n            {\r\n                // write welcome message\r\n                rootCommand.InvokeAsync(\"-h\").Wait();\r\n\r\n                while (true)\r\n                {\r\n                    string line = System.Console.ReadLine();\r\n                    \r\n\r\n                    if (line == \"q()\")\r\n                        return;\r\n\r\n                    var pb = new ConsoleProgressBar()\r\n                    {\r\n                        DisplayBars = false,\r\n                        DisplayPercentComplete = false,\r\n                        DisplayAnimation = false\r\n                    };\r\n                    var parsed = CommandLineExtensions.ParseText(line, ' ', '\"');\r\n\r\n                    logger.PropertyChanged += OnLoggerOnPropertyChanged;\r\n                    void OnLoggerOnPropertyChanged(object? sender, PropertyChangedEventArgs args)\r\n                    {\r\n                        switch (args.PropertyName)\r\n                        {\r\n                            case \"Progress\":\r\n                            {\r\n                                if (logger.Progress.Item1 == 0)\r\n                                {\r\n                                    pb = new ConsoleProgressBar() { DisplayBars = true, DisplayAnimation = false };\r\n                                }\r\n\r\n                                pb?.Report(logger.Progress.Item1);\r\n                                if (logger.Progress.Item1 == 1)\r\n                                {\r\n                                    System.Threading.Thread.Sleep(1000);\r\n\r\n                                    Console.WriteLine();\r\n                                    pb?.Dispose();\r\n                                    pb = null;\r\n                                }\r\n\r\n                                break;\r\n                            }\r\n                            default:\r\n                                break;\r\n                        }\r\n                    }\r\n\r\n                    rootCommand.InvokeAsync(parsed.ToArray()).Wait();\r\n\r\n                    await WriteLog();\r\n                    logger.PropertyChanged -= OnLoggerOnPropertyChanged;\r\n                }\r\n\r\n            }\r\n            else\r\n            {\r\n                rootCommand.InvokeAsync(args).Wait();\r\n\r\n                await WriteLog();\r\n            }\r\n\r\n\r\n            async Task WriteLog()\r\n            {\r\n                if (string.IsNullOrEmpty(logger.ErrorLogStr))\r\n                    return;\r\n\r\n                var t = DateTime.Now.ToString(\"yyyyMMddHHmmss\");\r\n                var fi = new FileInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),\r\n                    $\"errorlogs/log_{t}.txt\"));\r\n                if (fi.Directory != null)\r\n                {\r\n                    Directory.CreateDirectory(fi.Directory.FullName);\r\n                    var log = logger.ErrorLogStr;\r\n                    await File.WriteAllTextAsync(fi.FullName, log);\r\n                }\r\n                else\r\n                {\r\n                    \r\n                }\r\n            }\r\n\r\n        }\r\n\r\n        private delegate void StrDelegate(string value);\r\n\r\n        private static string TryGetGameInstallDir()\r\n        {\r\n            var cp77BinDir = \"\";\r\n            var cp77exe = \"\";\r\n            // check for CP77_DIR environment variable first\r\n            var CP77_DIR = System.Environment.GetEnvironmentVariable(\"CP77_DIR\", EnvironmentVariableTarget.User);\r\n            if (!string.IsNullOrEmpty(CP77_DIR) && new DirectoryInfo(CP77_DIR).Exists)\r\n                cp77BinDir = Path.Combine(CP77_DIR, \"bin\", \"x64\");\r\n            if (File.Exists(Path.Combine(cp77BinDir, \"Cyberpunk2077.exe\")))\r\n                return cp77BinDir;\r\n\r\n            // else: look for install location\r\n            const string uninstallkey = \"SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\\";\r\n            const string uninstallkey2 = \"SOFTWARE\\\\Wow6432Node\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Uninstall\\\\\";\r\n            const string gameName = \"Cyberpunk 2077\";\r\n            const string exeName = \"Cyberpunk2077.exe\";\r\n            var exePath = \"\";\r\n            StrDelegate strDelegate = msg => cp77exe = msg;\r\n\r\n            try\r\n            {\r\n                Parallel.ForEach(Registry.LocalMachine.OpenSubKey(uninstallkey)?.GetSubKeyNames(), item =>\r\n                {\r\n                    var programName = Registry.LocalMachine.OpenSubKey(uninstallkey + item)\r\n                        ?.GetValue(\"DisplayName\");\r\n                    var installLocation = Registry.LocalMachine.OpenSubKey(uninstallkey + item)\r\n                        ?.GetValue(\"InstallLocation\");\r\n                    if (programName != null && installLocation != null)\r\n                    {\r\n                        if (programName.ToString().Contains(gameName) ||\r\n                            programName.ToString().Contains(gameName))\r\n                        {\r\n                            exePath = Directory.GetFiles(installLocation.ToString(), exeName,\r\n                                SearchOption.AllDirectories).First();\r\n                        }\r\n                    }\r\n\r\n                    strDelegate.Invoke(exePath);\r\n                });\r\n                Parallel.ForEach(Registry.LocalMachine.OpenSubKey(uninstallkey2)?.GetSubKeyNames(), item =>\r\n                {\r\n                    var programName = Registry.LocalMachine.OpenSubKey(uninstallkey2 + item)\r\n                        ?.GetValue(\"DisplayName\");\r\n                    var installLocation = Registry.LocalMachine.OpenSubKey(uninstallkey2 + item)\r\n                        ?.GetValue(\"InstallLocation\");\r\n                    if (programName != null && installLocation != null)\r\n                    {\r\n                        if (programName.ToString().Contains(gameName) ||\r\n                            programName.ToString().Contains(gameName))\r\n                        {\r\n                            if (Directory.Exists(installLocation.ToString()))\r\n                                exePath = Directory.GetFiles(installLocation.ToString(), exeName,\r\n                                    SearchOption.AllDirectories).First();\r\n                        }\r\n                    }\r\n\r\n                    strDelegate.Invoke(exePath);\r\n                });\r\n\r\n                if (File.Exists(cp77exe))\r\n                    cp77BinDir = new FileInfo(cp77exe).Directory.FullName;\r\n            }\r\n            catch (Exception e)\r\n            {\r\n\r\n            }\r\n\r\n            if (string.IsNullOrEmpty(cp77BinDir))\r\n                return null;\r\n            if (!File.Exists(Path.Combine(cp77BinDir, \"Cyberpunk2077.exe\")))\r\n                return null;\r\n\r\n            return cp77BinDir;\r\n        }\r\n\r\n        private static bool TryCopyOodleLib()\r\n        {\r\n            var ass = AppDomain.CurrentDomain.BaseDirectory;\r\n            var destFileName = Path.Combine(ass, \"oo2ext_7_win64.dll\");\r\n            if (File.Exists(destFileName))\r\n                return true;\r\n\r\n            var cp77BinDir = TryGetGameInstallDir();\r\n            if (string.IsNullOrEmpty(cp77BinDir))\r\n                return false;\r\n\r\n            // copy oodle dll\r\n            var oodleInfo = new FileInfo(Path.Combine(cp77BinDir, \"oo2ext_7_win64.dll\"));\r\n            if (!oodleInfo.Exists)\r\n                return false;\r\n\r\n            if (!File.Exists(destFileName))\r\n                oodleInfo.CopyTo(destFileName);\r\n\r\n            return true;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "CP77Tools/Tasks/Cr2wTask.cs",
    "content": "﻿using System.IO;\r\nusing System.Threading.Tasks;\r\nusing Newtonsoft.Json;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n    public static partial class ConsoleFunctions\r\n    {\r\n        public static void Cr2wTask(string[] path, string outpath, bool all, bool chunks)\r\n        {\r\n            if (path == null || path.Length < 1)\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            Parallel.ForEach(path, file =>\r\n            {\r\n                Cr2wTaskInner(file, outpath, all, chunks);\r\n            });\r\n\r\n        }\r\n\r\n        private static int Cr2wTaskInner(string path, string outpath, bool all, bool chunks)\r\n        {\r\n            // initial checks\r\n            if (string.IsNullOrEmpty(path))\r\n            {\r\n                logger.LogString(\"Please fill in an input path.\", Logtype.Error);\r\n                return 0;\r\n            }\r\n            var inputFileInfo = new FileInfo(path);\r\n            if (!inputFileInfo.Exists)\r\n            {\r\n                logger.LogString(\"Input file does not exist.\", Logtype.Error);\r\n                return 0;\r\n            }\r\n\r\n            var outputDirInfo = string.IsNullOrEmpty(outpath) \r\n                ? inputFileInfo.Directory \r\n                : new DirectoryInfo(outpath);\r\n            if (outputDirInfo == null || !outputDirInfo.Exists)\r\n            {\r\n                logger.LogString(\"Output directory is not valid.\", Logtype.Error);\r\n                return 0;\r\n            }\r\n\r\n            var f = File.ReadAllBytes(inputFileInfo.FullName);\r\n            using var ms = new MemoryStream(f);\r\n            using var br = new BinaryReader(ms);\r\n\r\n            var cr2w = new CR2WFile();\r\n\r\n            if (all)\r\n            {\r\n                cr2w.ReadImportsAndBuffers(br);\r\n\r\n                var obj = new Cr2wChunkInfo { Filename = inputFileInfo.FullName };\r\n\r\n                obj.Stringdict = cr2w.StringDictionary;\r\n                obj.Imports = cr2w.Imports;\r\n                obj.Buffers = cr2w.Buffers;\r\n                obj.Chunks = cr2w.Chunks;\r\n                foreach (var chunk in cr2w.Chunks)\r\n                {\r\n                    obj.ChunkData.Add(chunk.GetDumpObject(br));\r\n                }\r\n\r\n                //write\r\n                File.WriteAllText(Path.Combine(outputDirInfo.FullName, $\"{inputFileInfo.Name}.info.json\"),\r\n                    JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings()\r\n                    {\r\n                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\r\n                        PreserveReferencesHandling = PreserveReferencesHandling.None,\r\n                        TypeNameHandling = TypeNameHandling.Auto\r\n                    }));\r\n                logger.LogString($\"Finished. Dump file written to {Path.Combine(outputDirInfo.FullName, $\"{inputFileInfo.Name}.info.json\")}\", Logtype.Success);\r\n            }\r\n\r\n            if (chunks)\r\n            {\r\n                br.BaseStream.Seek(0, SeekOrigin.Begin);\r\n                cr2w.Read(br);\r\n\r\n                //write\r\n                File.WriteAllText(Path.Combine(outputDirInfo.FullName, $\"{inputFileInfo.Name}.json\"),\r\n                    JsonConvert.SerializeObject(cr2w, Formatting.Indented, new JsonSerializerSettings()\r\n                    {\r\n                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\r\n                        PreserveReferencesHandling = PreserveReferencesHandling.None,\r\n                        TypeNameHandling = TypeNameHandling.None\r\n                    }));\r\n                logger.LogString($\"Finished. Dump file written to {Path.Combine(outputDirInfo.FullName, $\"{inputFileInfo.Name}.json\")}\", Logtype.Success);\r\n            }\r\n\r\n\r\n\r\n\r\n            return 1;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Tasks/DumpTask.cs",
    "content": "﻿using System;\r\nusing System.Collections.Concurrent;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\n//using System.IO.MemoryMappedFiles;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Threading;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC;\r\nusing CP77.CR2W.Archive;\r\nusing Newtonsoft.Json;\r\nusing WolvenKit.Common;\r\nusing WolvenKit.Common.Extensions;\r\nusing CP77.CR2W;\r\nusing CP77.CR2W.Reflection;\r\nusing CP77.CR2W.Types;\r\nusing WolvenKit.Common.FNV1A;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n    public class ArchiveDumpObject\r\n    {\r\n        public ConcurrentDictionary<ulong, Cr2wChunkInfo> FileDictionary { get; set; }\r\n        public ConcurrentDictionary<ulong, Cr2wTextureInfo> TextureDictionary { get; set; }\r\n        public string Filename { get; set; }\r\n    }\r\n\r\n    public class Cr2wChunkInfo\r\n    {\r\n        public Dictionary<uint, string> Stringdict { get; set; }\r\n        public List<CR2WImportWrapper> Imports { get; set; }\r\n        public List<CR2WBufferWrapper> Buffers { get; set; }\r\n\r\n        public List<CR2WExportWrapper> Chunks { get; set; }\r\n\r\n        public List<CR2WExportWrapper.Cr2wVariableDumpObject> ChunkData { get; } =\r\n            new List<CR2WExportWrapper.Cr2wVariableDumpObject>();\r\n\r\n        public string Filename { get; set; }\r\n    }\r\n\r\n    public class Cr2wTextureInfo\r\n    {\r\n        public string Filename { get; set; }\r\n\r\n        public ushort width { get; set; }\r\n        public ushort height { get; set; }\r\n        public byte mips { get; set; }\r\n        public ushort slicecount { get; set; }\r\n        public uint alignment { get; set; }\r\n        public CEnum<Enums.ETextureCompression> compression { get; set; }\r\n        public CEnum<Enums.GpuWrapApieTextureGroup> Group { get; set; }\r\n        public CEnum<Enums.ETextureRawFormat> rawFormat { get; set; }\r\n    }\r\n\r\n\r\n    public static partial class ConsoleFunctions\r\n    {\r\n        private static byte[] MAGIC = {0x43, 0x52, 0x32, 0x57};\r\n\r\n        public static void DumpTask(string[] path, bool imports, bool missinghashes, \r\n            bool texinfo, bool classinfo, bool dump, bool list)\r\n        {\r\n            if (path == null || path.Length < 1)\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            Parallel.ForEach(path, file =>\r\n            {\r\n                DumpTaskInner(file, imports, missinghashes, texinfo, classinfo, dump, list);\r\n            });\r\n\r\n        }\r\n\r\n        public static int DumpTaskInner(string path, bool imports, bool missinghashes, \r\n            bool texinfo, bool classinfo, bool dump, bool list)\r\n        {\r\n            #region checks\r\n\r\n            if (string.IsNullOrEmpty(path))\r\n            {\r\n                ConsoleFunctions.logger.LogString(\"Please fill in an input path\",Logtype.Error);\r\n                return 0;\r\n            }\r\n\r\n            var isDirectory = false;\r\n            var inputFileInfo = new FileInfo(path);\r\n            var inputDirInfo = new DirectoryInfo(path);\r\n            if (!inputFileInfo.Exists)\r\n            {\r\n                if (!inputDirInfo.Exists)\r\n                    return 0;\r\n                else\r\n                    isDirectory = true;\r\n            }\r\n            #endregion\r\n            \r\n            var archives = new List<Archive>();\r\n\r\n            if (isDirectory)\r\n            {\r\n                archives.AddRange(inputDirInfo\r\n                    .GetFiles(\"*.archive\", SearchOption.AllDirectories)\r\n                    .Select(_ => new Archive(_.FullName)));\r\n            }\r\n            else\r\n            {\r\n                archives.Add(new Archive(inputFileInfo.FullName));\r\n            }\r\n\r\n            \r\n\r\n            var mainController = ServiceLocator.Default.ResolveType<IHashService>();\r\n            var logger = ServiceLocator.Default.ResolveType<ILoggerService>();\r\n            var typedict = new ConcurrentDictionary<string, IEnumerable<string>>();\r\n\r\n            // Parallel\r\n            foreach (var ar in archives)\r\n            {\r\n                if (classinfo)\r\n                {\r\n                    // using var mmf = MemoryMappedFile.CreateFromFile(ar.Filepath, FileMode.Open,\r\n                    //     ar.Filepath.GetHashMD5(), 0,\r\n                    //     MemoryMappedFileAccess.Read);\r\n\r\n                    var fileinfo = ar.Files.Values;\r\n                    var query = fileinfo.GroupBy(\r\n                        ext => Path.GetExtension(ext.FileName),\r\n                        file => file,\r\n                        (ext, finfo) => new\r\n                        {\r\n                            Key = ext,\r\n                            File = fileinfo.Where(_ => Path.GetExtension(_.FileName) == ext)\r\n                        }).ToList();\r\n\r\n\r\n\r\n                    var total = query.Count;\r\n                    logger.LogString($\"Exporting {total} bundle entries \");\r\n\r\n                    Thread.Sleep(1000);\r\n                    int progress = 0;\r\n                    logger.LogProgress(0);\r\n\r\n                    // foreach extension\r\n                    Parallel.ForEach(query, result =>\r\n                    {\r\n                        if (!string.IsNullOrEmpty(result.Key))\r\n                        {\r\n                            Parallel.ForEach(result.File, fi =>\r\n                            {\r\n                                var (f, b) = ar.GetFileData(fi.NameHash64, false);\r\n                                using var ms = new MemoryStream(f);\r\n                                using var br = new BinaryReader(ms);\r\n\r\n                                var cr2w = new CR2WFile();\r\n                                try\r\n                                {\r\n                                    cr2w.ReadImportsAndBuffers(br);\r\n                                }\r\n                                catch (Exception e)\r\n                                {\r\n                                    return;\r\n                                }\r\n                                \r\n                                foreach (var chunk in cr2w.Chunks)\r\n                                {\r\n                                    var o = chunk.GetDumpObject(br);\r\n                                    if (o != null) Register(o);\r\n                                }\r\n                            });\r\n                        }\r\n                        \r\n                        Interlocked.Increment(ref progress);\r\n                        logger.LogProgress(progress / (float)total);\r\n\r\n                        logger.LogString($\"Dumped extension {result.Key}\", Logtype.Normal);\r\n                    });\r\n\r\n\r\n                }\r\n                if (imports || texinfo)\r\n                {\r\n                    // using var mmf = MemoryMappedFile.CreateFromFile(ar.Filepath, FileMode.Open,\r\n                    //     ar.Filepath.GetHashMD5(), 0,\r\n                    //     MemoryMappedFileAccess.Read);\r\n\r\n                    var fileDictionary = new ConcurrentDictionary<ulong, Cr2wChunkInfo>();\r\n                    var texDictionary = new ConcurrentDictionary<ulong, Cr2wTextureInfo>();\r\n\r\n                    // get info\r\n                    var count = ar.FileCount;\r\n                    logger.LogString($\"Exporting {count} bundle entries \");\r\n\r\n                    Thread.Sleep(1000);\r\n                    int progress = 0;\r\n                    logger.LogProgress(0);\r\n\r\n                    Parallel.For(0, count, i =>\r\n                    {\r\n                        var entry = ar.Files.ToList()[i];\r\n                        var hash = entry.Key;\r\n                        var filename = string.IsNullOrEmpty(entry.Value.FileName) ? hash.ToString() : entry.Value.FileName;\r\n\r\n                        if (imports)\r\n                        {\r\n                            var (f, buffers) = ar.GetFileData(hash, false);\r\n\r\n                            // check if cr2w file\r\n                            if (f.Length < 4)\r\n                                return;\r\n                            var id = f.Take(4);\r\n                            if (!id.SequenceEqual(MAGIC))\r\n                                return;\r\n\r\n                            var cr2w = new CR2WFile();\r\n\r\n                            using var ms = new MemoryStream(f);\r\n                            using var br = new BinaryReader(ms);\r\n                            cr2w.ReadImportsAndBuffers(br);\r\n\r\n                            var obj = new Cr2wChunkInfo\r\n                            {\r\n                                Filename = filename,\r\n                                Imports = cr2w.Imports\r\n                            };\r\n\r\n                            \r\n\r\n                            fileDictionary.AddOrUpdate(hash, obj, (arg1, o) => obj);\r\n                        }\r\n\r\n                        if (texinfo)\r\n                        {\r\n                            if (!string.IsNullOrEmpty(entry.Value.FileName) && entry.Value.FileName.Contains(\".xbm\"))\r\n                            {\r\n                                var (f, buffers) = ar.GetFileData(hash, false);\r\n\r\n                                // check if cr2w file\r\n                                if (f.Length < 4)\r\n                                    return;\r\n                                var id = f.Take(4);\r\n                                if (!id.SequenceEqual(MAGIC))\r\n                                    return;\r\n\r\n                                var cr2w = new CR2WFile();\r\n\r\n                                using var ms = new MemoryStream(f);\r\n                                using var br = new BinaryReader(ms);\r\n                                var result = cr2w.Read(br);\r\n\r\n                                if (result != EFileReadErrorCodes.NoError)\r\n                                    return;\r\n                                if (!(cr2w.Chunks.FirstOrDefault()?.data is CBitmapTexture xbm) ||\r\n                                    !(cr2w.Chunks[1]?.data is rendRenderTextureBlobPC blob))\r\n                                    return;\r\n\r\n                                // create dds header\r\n                                var texinfo = new Cr2wTextureInfo()\r\n                                {\r\n                                    Filename = filename,\r\n                                    width = blob.Header.SizeInfo.Width.val,\r\n                                    height = blob.Header.SizeInfo.Height.val,\r\n                                    mips = blob.Header.TextureInfo.MipCount.val,\r\n                                    slicecount = blob.Header.TextureInfo.SliceCount.val,\r\n                                    alignment = blob.Header.TextureInfo.DataAlignment.val,\r\n                                    compression = xbm.Setup.Compression,\r\n                                    Group = xbm.Setup.Group,\r\n                                    rawFormat = xbm.Setup.RawFormat,\r\n                                };\r\n\r\n                                texDictionary.AddOrUpdate(hash, texinfo, (arg1, o) => texinfo);\r\n                            }\r\n                        }\r\n\r\n                        Interlocked.Increment(ref progress);\r\n                        logger.LogProgress(progress / (float)count);\r\n                    });\r\n\r\n                    // write\r\n                    var arobj = new ArchiveDumpObject()\r\n                    {\r\n                        Filename = ar.ArchiveAbsolutePath,\r\n                        FileDictionary = fileDictionary,\r\n                        TextureDictionary = texDictionary\r\n                    };\r\n\r\n                    if (imports)\r\n                    {\r\n                        using var hwriter = File.CreateText($\"{ar.ArchiveAbsolutePath}.hashes.csv\");\r\n                        hwriter.WriteLine(\"String,Hash\");\r\n                        List<string> allimports = new List<string>();\r\n\r\n                        foreach (var (key, value) in arobj.FileDictionary)\r\n                        {\r\n                            if (value.Imports == null)\r\n                                continue;\r\n                            allimports.AddRange(value.Imports.Select(import => import.DepotPathStr));\r\n                        }\r\n                        foreach (var str in allimports.Distinct())\r\n                        {\r\n                            var hash = FNV1A64HashAlgorithm.HashString(str);\r\n                            if (!mainController.Hashdict.ContainsKey(hash))\r\n                                hwriter.WriteLine($\"{str},{hash}\");\r\n                        }\r\n\r\n                        logger.LogString($\"Finished. Dump file written to {ar.ArchiveAbsolutePath}.\", Logtype.Success);\r\n\r\n                        //write\r\n                        File.WriteAllText($\"{ar.ArchiveAbsolutePath}.json\",\r\n                            JsonConvert.SerializeObject(arobj, Formatting.Indented, new JsonSerializerSettings()\r\n                            {\r\n                                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\r\n                                PreserveReferencesHandling = PreserveReferencesHandling.None,\r\n                                TypeNameHandling = TypeNameHandling.None\r\n                            }));\r\n                        logger.LogString($\"Finished. Dump file written to {inputFileInfo.FullName}.json\", Logtype.Success);\r\n\r\n                    }\r\n\r\n                    if (texinfo)\r\n                    {\r\n                        //write\r\n                        File.WriteAllText($\"{ar.ArchiveAbsolutePath}.textures.json\",\r\n                            JsonConvert.SerializeObject(arobj, Formatting.Indented, new JsonSerializerSettings()\r\n                            {\r\n                                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\r\n                                PreserveReferencesHandling = PreserveReferencesHandling.None,\r\n                                TypeNameHandling = TypeNameHandling.None\r\n                            }));\r\n                        logger.LogString($\"Finished. Dump file written to {inputFileInfo.FullName}.json\", Logtype.Success);\r\n                    }\r\n                }\r\n                \r\n                // TODO: add this here\r\n                if (dump)\r\n                {\r\n                    File.WriteAllText($\"{ar.ArchiveAbsolutePath}.json\",\r\n                        JsonConvert.SerializeObject(ar, Formatting.Indented, new JsonSerializerSettings()\r\n                        {\r\n                            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\r\n                            PreserveReferencesHandling = PreserveReferencesHandling.None,\r\n                            TypeNameHandling = TypeNameHandling.None\r\n                        }));\r\n\r\n                    logger.LogString($\"Finished dumping {ar.ArchiveAbsolutePath}.\", Logtype.Success);\r\n                }\r\n\r\n                if (list)\r\n                {\r\n                    foreach (var entry in ar.Files)\r\n                    {\r\n                        logger.LogString(entry.Value.FileName, Logtype.Normal);\r\n                    }\r\n                }\r\n            }\r\n\r\n            if (classinfo)\r\n            {\r\n                //write class definitions\r\n                var outdir = isDirectory\r\n                ? Path.Combine(inputDirInfo.FullName, \"ClassDefinitions\")\r\n                : Path.Combine(inputFileInfo.Directory.FullName, \"ClassDefinitions\");\r\n                Directory.CreateDirectory(outdir);\r\n                var outfile = Path.Combine(outdir, \"classdefinitions.txt\");\r\n                var outfileS = Path.Combine(outdir, \"classdefinitions_simple.json\");\r\n                var text = \"\";\r\n                foreach (var (typename, variables) in typedict)\r\n                {\r\n                    //write\r\n                    var sb = new StringBuilder($\"[REDMeta] public class {typename} : CVariable {{\\r\\n\");\r\n\r\n                    var variableslist = variables.ToList();\r\n                    for (int i = 0; i < variableslist.Count; i++)\r\n                    {\r\n                        var typ = variableslist[i].Split(' ').First();\r\n                        var nam = variableslist[i].Split(' ').Last();\r\n                        var wktype = REDReflection.GetWKitBaseTypeFromREDBaseType(typ);\r\n\r\n                        if (string.IsNullOrEmpty(nam)) nam = \"Missing\";\r\n                        if (string.IsNullOrEmpty(typ)) typ = \"Missing\";\r\n\r\n\r\n                        sb.Append($\"\\t[Ordinal({i})]  [RED(\\\"{nam}\\\")] public {wktype} {nam.FirstCharToUpper()} {{ get; set; }}\\r\\n\");\r\n                    }\r\n\r\n                    sb.Append(\r\n                        $\"public {typename}(CR2WFile cr2w, CVariable parent, string name) : base(cr2w, parent, name) {{ }}\\r\\n\");\r\n\r\n                    sb.Append(\"}\\r\\n\");\r\n                    text += sb.ToString();\r\n                }\r\n                File.WriteAllText(outfile, text);\r\n\r\n                //write\r\n                File.WriteAllText(outfileS,\r\n                    JsonConvert.SerializeObject(typedict, Formatting.Indented, new JsonSerializerSettings()\r\n                    {\r\n                        ReferenceLoopHandling = ReferenceLoopHandling.Ignore,\r\n                        PreserveReferencesHandling = PreserveReferencesHandling.None,\r\n                        TypeNameHandling = TypeNameHandling.None\r\n                    }));\r\n\r\n                logger.LogString(\"Done.\", Logtype.Success);\r\n            }\r\n            if (missinghashes)\r\n            {\r\n                var missinghashtxt = isDirectory\r\n                    ? Path.Combine(inputDirInfo.FullName, \"missinghashes.txt\")\r\n                    : $\"{inputFileInfo.FullName}.missinghashes.txt\";\r\n                using var mwriter = File.CreateText(missinghashtxt);\r\n                foreach (var ar in archives)\r\n                {\r\n                    var ctr = 0;\r\n                    foreach (var (hash, fileInfoEntry) in ar.Files)\r\n                    {\r\n                        if (fileInfoEntry.NameOrHash == hash.ToString())\r\n                        {\r\n                            mwriter.WriteLine(hash);\r\n                            ctr++;\r\n                        }\r\n                    }\r\n                    logger.LogString($\"{ar.ArchiveAbsolutePath} - missing: {ctr}\", Logtype.Normal);\r\n                }\r\n            }\r\n\r\n\r\n            return 1;\r\n\r\n            void Register(CR2WExportWrapper.Cr2wVariableDumpObject o)\r\n            {\r\n                if (o?.Type == null) return;\r\n\r\n                o.Variables ??= new List<CR2WExportWrapper.Cr2wVariableDumpObject>();\r\n\r\n                IEnumerable<string> vars = o.Variables.Select(_ => _.ToSimpleString());\r\n                if (typedict.ContainsKey(o.Type))\r\n                {\r\n                    var existing = typedict[o.Type];\r\n                    var newlist = o.Variables.Select(_ => _.ToSimpleString());\r\n                    if (existing != null) vars = existing.Union(newlist);\r\n                }\r\n                typedict.AddOrUpdate(o.Type, vars, (arg1, ol) => ol);\r\n\r\n                foreach (var oVariable in o.Variables)\r\n                {\r\n                    // generic types (arrays, handles, refs)\r\n                    if (oVariable.Type != null && oVariable.Type.Contains(\":\"))\r\n                    {\r\n                        var gentyp = oVariable.Type.Split(\":\").First();\r\n                        var innertype = oVariable.Type.Substring(gentyp.Length + 1);\r\n                        var innertype2 = oVariable.Type[(gentyp.Length + 1)];\r\n                        if (gentyp == \"array\")\r\n                        {\r\n                            oVariable.Type = innertype;\r\n                            Register(oVariable);\r\n                        }\r\n                        else\r\n                        {\r\n                            continue;\r\n                        }\r\n\r\n                        \r\n                    }\r\n\r\n                    Register(oVariable);\r\n                }\r\n            }\r\n\r\n        }\r\n    }\r\n}\r\n\r\n"
  },
  {
    "path": "CP77Tools/Tasks/ExportTask.cs",
    "content": "﻿using System.IO;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.DDS;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n    public static partial class ConsoleFunctions\r\n    {\r\n        public static void ExportTask(string[] path, EUncookExtension uncookext)\r\n        {\r\n            if (path == null || path.Length < 1)\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            Parallel.ForEach(path, file =>\r\n            {\r\n                ExportTaskInner(file, uncookext);\r\n            });\r\n\r\n        }\r\n\r\n        private static int ExportTaskInner(string path, EUncookExtension uncookext)\r\n        {\r\n\r\n            #region checks\r\n\r\n            if (string.IsNullOrEmpty(path))\r\n            {\r\n                logger.LogString(\"Please fill in an input path.\", Logtype.Error);\r\n                return 0;\r\n            }\r\n            var inputFileInfo = new FileInfo(path);\r\n            if (!inputFileInfo.Exists)\r\n            {\r\n                logger.LogString(\"Input file does not exist.\", Logtype.Error);\r\n                return 0;\r\n            }\r\n\r\n            #endregion\r\n\r\n            if (ModTools.Export(new FileInfo(path), uncookext) == 1)\r\n            {\r\n                logger.LogString($\"Successfully exported {path}.\", Logtype.Success);\r\n            }\r\n            else\r\n            {\r\n                logger.LogString($\"Failed to export {path}.\", Logtype.Error);\r\n            }\r\n\r\n            return 1;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Tasks/HashTask.cs",
    "content": "﻿using System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC;\r\nusing WolvenKit.Common.FNV1A;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n\r\n    public static partial class ConsoleFunctions\r\n    {\r\n\r\n        public static int HashTask(string[] input, bool missing)\r\n        {\r\n            #region checks\r\n\r\n            foreach (var s in input)\r\n            {\r\n                if (!string.IsNullOrEmpty(s))\r\n                    logger.LogString(FNV1A64HashAlgorithm.HashString(s).ToString(), Logtype.Normal);\r\n            }\r\n\r\n            #endregion\r\n\r\n            if (missing)\r\n            {\r\n                var missingh = File.ReadAllLines(@\"C:\\Gog\\Cyberpunk 2077\\archive\\pc\\content\\missinghashes.txt\");\r\n                var lines = File.ReadAllLines(@\"X:\\cp77\\langs-work.txt\");\r\n                var Hashdict = new Dictionary<ulong, string>();\r\n                var bad = new Dictionary<ulong, string>();\r\n               \r\n\r\n                foreach (var line in lines)\r\n                {\r\n                    var hash = FNV1A64HashAlgorithm.HashString(line);\r\n\r\n                    if (missingh.Contains(hash.ToString()))\r\n                    {\r\n                        if (!Hashdict.ContainsKey(hash))\r\n                            Hashdict.Add(hash, line);\r\n                    }\r\n                    else\r\n                    {\r\n                        if (!bad.ContainsKey(hash))\r\n                            bad.Add(hash, line);\r\n                    }\r\n                    \r\n                }\r\n\r\n\r\n\r\n            }\r\n            \r\n\r\n            return 1;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Tasks/OodleTask.cs",
    "content": "﻿using System;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing WolvenKit.Common.Oodle;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n\r\n    public static partial class ConsoleFunctions\r\n    {\r\n\r\n        public static int OodleTask(string path, string outpath, bool decompress)\r\n        {\r\n            if (string.IsNullOrEmpty(path))\r\n                return 0;\r\n\r\n            if (string.IsNullOrEmpty(outpath)) { outpath = path; }\r\n\r\n            if (decompress)\r\n            {\r\n                var file = File.ReadAllBytes(path);\r\n                using var ms = new MemoryStream(file);\r\n                using var br = new BinaryReader(ms);\r\n\r\n                var oodleCompression = br.ReadBytes(4);\r\n                if (!(oodleCompression.SequenceEqual(new byte[] { 0x4b, 0x41, 0x52, 0x4b })))\r\n                    throw new NotImplementedException();\r\n                var size = br.ReadUInt32();\r\n\r\n                var buffer = br.ReadBytes(file.Length - 8);\r\n\r\n                byte[] unpacked = new byte[size];\r\n                long unpackedSize = OodleHelper.Decompress(buffer, unpacked);\r\n\r\n                using var msout = new MemoryStream();\r\n                using var bw = new BinaryWriter(msout);\r\n                bw.Write(unpacked);\r\n\r\n                File.WriteAllBytes($\"{outpath}.kark\", msout.ToArray());\r\n            }\r\n\r\n\r\n            return 1;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Tasks/PackTask.cs",
    "content": "﻿using System.IO;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n\r\n    public static partial class ConsoleFunctions\r\n    {\r\n        /// <summary>\r\n        /// Packs a folder or list of folders to .archive files.\r\n        /// </summary>\r\n        /// <param name=\"path\"></param>\r\n        /// <param name=\"outpath\"></param>\r\n        public static void PackTask(string[] path, string outpath)\r\n        {\r\n            if (path == null || path.Length < 1)\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            Parallel.ForEach(path, file =>\r\n            {\r\n                PackTaskInner(file, outpath);\r\n            });\r\n\r\n        }\r\n\r\n        private static void PackTaskInner(string path, string outpath, int cp = 0)\r\n        {\r\n            #region checks\r\n\r\n            if (string.IsNullOrEmpty(path))\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var inputDirInfo = new DirectoryInfo(path);\r\n            if (!Directory.Exists(path) || !inputDirInfo.Exists)\r\n            {\r\n                logger.LogString(\"Input path does not exist\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var basedir = inputDirInfo;\r\n            if (basedir?.Parent == null) return;\r\n\r\n            DirectoryInfo outDir;\r\n            if (string.IsNullOrEmpty(outpath))\r\n            {\r\n                outDir = basedir.Parent;\r\n            }\r\n            else\r\n            {\r\n                outDir = new DirectoryInfo(outpath);\r\n                if (!outDir.Exists) \r\n                    outDir = Directory.CreateDirectory(outpath);\r\n            }\r\n\r\n            #endregion\r\n\r\n            var ar = ModTools.Pack(basedir, outDir);\r\n            if (ar != null)\r\n                logger.LogString($\"Finished packing {ar.ArchiveAbsolutePath}.\", Logtype.Success);\r\n            else\r\n                logger.LogString($\"Packing failed.\", Logtype.Error);\r\n\r\n            return;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Tasks/RebuildTask.cs",
    "content": "﻿using System.IO;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n\r\n    public static partial class ConsoleFunctions\r\n    {\r\n        /// <summary>\r\n        /// Recombine split buffers and textures in a folder.\r\n        /// </summary>\r\n        /// <param name=\"path\"></param>\r\n        /// <param name=\"outpath\"></param>\r\n        public static void RebuildTask(string[] path, \r\n            bool buffers, \r\n            bool textures,\r\n            bool import,\r\n            bool keep,\r\n            bool clean,\r\n            bool unsaferaw\r\n            )\r\n        {\r\n            if (path == null || path.Length < 1)\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            Parallel.ForEach(path, p =>\r\n            {\r\n                RebuildTaskInner(p, buffers, textures, import, keep, clean, unsaferaw);\r\n            });\r\n\r\n        }\r\n\r\n        private static void RebuildTaskInner(string path, \r\n            bool buffers, \r\n            bool textures,\r\n            bool import,\r\n            bool keep,\r\n            bool clean,\r\n            bool unsaferaw\r\n        )\r\n        {\r\n            #region checks\r\n\r\n            if (string.IsNullOrEmpty(path))\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var inputDirInfo = new DirectoryInfo(path);\r\n            if (!Directory.Exists(path) || !inputDirInfo.Exists)\r\n            {\r\n                logger.LogString(\"Input path does not exist\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var basedir = inputDirInfo;\r\n            if (basedir?.Parent == null) return;\r\n\r\n            #endregion\r\n\r\n\r\n            ModTools.Recombine(basedir, buffers, textures, import, keep, clean, unsaferaw);\r\n\r\n            return;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Tasks/UnbundleTask.cs",
    "content": "﻿using System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W.Archive;\r\nusing Catel.IoC;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n    public static partial class ConsoleFunctions\r\n    {\r\n        private static readonly ILoggerService logger = ServiceLocator.Default.ResolveType<ILoggerService>();\r\n\r\n        public static void UnbundleTask(string[] path, string outpath,\r\n            string hash, string pattern, string regex)\r\n        {\r\n            if (path == null || path.Length < 1)\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            Parallel.ForEach(path, file =>\r\n            {\r\n                UnbundleTaskInner(file, outpath, hash, pattern, regex);\r\n            });\r\n\r\n        }\r\n\r\n\r\n        private static void UnbundleTaskInner(string path, string outpath, \r\n            string hash, string pattern, string regex)\r\n        {\r\n            #region checks\r\n\r\n            if (string.IsNullOrEmpty(path))\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var inputFileInfo = new FileInfo(path);\r\n            var inputDirInfo = new DirectoryInfo(path);\r\n            \r\n\r\n            if (!inputFileInfo.Exists && !inputDirInfo.Exists)\r\n            {\r\n                logger.LogString(\"Input path does not exist\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            if (inputFileInfo.Exists && inputFileInfo.Extension != \".archive\")\r\n            {\r\n                logger.LogString(\"Input file is not an .archive\", Logtype.Error);\r\n                return;\r\n            }\r\n            else if (inputDirInfo.Exists && inputDirInfo.GetFiles().All(_ => _.Extension != \".archive\"))\r\n            {\r\n                logger.LogString(\"No .archive file to process in the input directory\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var isDirectory = !inputFileInfo.Exists;\r\n            var basedir = inputFileInfo.Exists ? new FileInfo(path).Directory : inputDirInfo;\r\n\r\n            #endregion\r\n\r\n            List<FileInfo> archiveFileInfos;\r\n            if (isDirectory)\r\n            {\r\n                var archiveManager = new ArchiveManager(basedir);\r\n                // TODO: use the manager here?\r\n                archiveFileInfos = archiveManager.Archives.Select(_ => new FileInfo(_.Value.ArchiveAbsolutePath)).ToList();\r\n            }\r\n            else\r\n            {\r\n                archiveFileInfos = new List<FileInfo> {inputFileInfo};\r\n            }\r\n\r\n\r\n            foreach (var processedarchive in archiveFileInfos)\r\n            {\r\n                // get outdirectory\r\n                DirectoryInfo outDir;\r\n                if (string.IsNullOrEmpty(outpath))\r\n                {\r\n                    outDir = Directory.CreateDirectory(Path.Combine(\r\n                        basedir.FullName,\r\n                        processedarchive.Name.Replace(\".archive\", \"\")));\r\n                }\r\n                else\r\n                {\r\n                    outDir = new DirectoryInfo(outpath);\r\n                    if (!outDir.Exists)\r\n                    {\r\n                        outDir = Directory.CreateDirectory(outpath);\r\n                    }\r\n\r\n                    if (inputDirInfo.Exists)\r\n                    {\r\n                        outDir = Directory.CreateDirectory(Path.Combine(\r\n                            outDir.FullName,\r\n                            processedarchive.Name.Replace(\".archive\", \"\")));\r\n                    }\r\n                }\r\n\r\n                // read archive\r\n                var ar = new Archive(processedarchive.FullName);\r\n\r\n                var isHash = ulong.TryParse(hash, out ulong hashNumber);\r\n\r\n                // run\r\n                {\r\n                    if (!isHash && File.Exists(hash))\r\n                    {\r\n                        var hashlist = File.ReadAllLines(hash)\r\n                            .ToList().Select(_ => ulong.TryParse(_, out ulong res) ? res : 0);\r\n                        logger.LogString($\"Extracing all files from the hashlist ({hashlist.Count()}hashes) ...\",\r\n                            Logtype.Normal);\r\n                        foreach (var hash_num in hashlist)\r\n                        {\r\n                            ar.ExtractSingle(hash_num, outDir);\r\n                            logger.LogString($\" {ar.ArchiveAbsolutePath}: Extracted one file: {hash_num}\", Logtype.Success);\r\n                        }\r\n\r\n                        logger.LogString($\"Bulk extraction from hashlist file completed!\", Logtype.Success);\r\n                    }\r\n                    else if (isHash && hashNumber != 0)\r\n                    {\r\n                        ar.ExtractSingle(hashNumber, outDir);\r\n                        logger.LogString($\" {ar.ArchiveAbsolutePath}: Extracted one file: {hashNumber}\", Logtype.Success);\r\n                    }\r\n                    else\r\n                    {\r\n                        var r = ar.ExtractAll(outDir, pattern, regex);\r\n                        logger.LogString($\"{ar.ArchiveAbsolutePath}: Extracted {r.Item1.Count}/{r.Item2} files.\",\r\n                            Logtype.Success);\r\n                    }\r\n\r\n                }\r\n\r\n\r\n\r\n\r\n            }\r\n\r\n            return;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools/Tasks/UncookTask.cs",
    "content": "﻿using System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W.Archive;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.DDS;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77Tools.Tasks\r\n{\r\n    public static partial class ConsoleFunctions\r\n    {\r\n\r\n        public static void UncookTask(string[] path, string outpath,\r\n            EUncookExtension uext, bool flip, ulong hash, string pattern, string regex)\r\n        {\r\n            if (path == null || path.Length < 1)\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            Parallel.ForEach(path, file =>\r\n            {\r\n                UncookTaskInner(file, outpath, uext, flip, hash, pattern, regex);\r\n            });\r\n\r\n        }\r\n\r\n\r\n        private static void UncookTaskInner(string path, string outpath, \r\n            EUncookExtension uext, bool flip, ulong hash, string pattern, string regex)\r\n        {\r\n            #region checks\r\n\r\n            if (string.IsNullOrEmpty(path))\r\n            {\r\n                logger.LogString(\"Please fill in an input path\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var inputFileInfo = new FileInfo(path);\r\n            var inputDirInfo = new DirectoryInfo(path);\r\n            \r\n\r\n            if (!inputFileInfo.Exists && !inputDirInfo.Exists)\r\n            {\r\n                logger.LogString(\"Input path does not exist\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            if (inputFileInfo.Exists && inputFileInfo.Extension != \".archive\")\r\n            {\r\n                logger.LogString(\"Input file is not an .archive\", Logtype.Error);\r\n                return;\r\n            }\r\n            else if (inputDirInfo.Exists && inputDirInfo.GetFiles().All(_ => _.Extension != \".archive\"))\r\n            {\r\n                logger.LogString(\"No .archive file to process in the input directory\", Logtype.Error);\r\n                return;\r\n            }\r\n\r\n            var isDirectory = !inputFileInfo.Exists;\r\n            var basedir = inputFileInfo.Exists ? new FileInfo(path).Directory : inputDirInfo;\r\n\r\n            #endregion\r\n\r\n            List<FileInfo> archiveFileInfos;\r\n            if (isDirectory)\r\n            {\r\n                var archiveManager = new ArchiveManager(basedir);\r\n                // TODO: use the manager here?\r\n                archiveFileInfos = archiveManager.Archives.Select(_ => new FileInfo(_.Value.ArchiveAbsolutePath)).ToList();\r\n            }\r\n            else\r\n            {\r\n                archiveFileInfos = new List<FileInfo> {inputFileInfo};\r\n            }\r\n\r\n\r\n            foreach (var processedarchive in archiveFileInfos)\r\n            {\r\n                // get outdirectory\r\n                DirectoryInfo outDir;\r\n                if (string.IsNullOrEmpty(outpath))\r\n                {\r\n                    outDir = Directory.CreateDirectory(Path.Combine(\r\n                        basedir.FullName,\r\n                        processedarchive.Name.Replace(\".archive\", \"\")));\r\n                }\r\n                else\r\n                {\r\n                    outDir = new DirectoryInfo(outpath);\r\n                    if (!outDir.Exists)\r\n                    {\r\n                        outDir = Directory.CreateDirectory(outpath);\r\n                    }\r\n\r\n                    if (inputDirInfo.Exists)\r\n                    {\r\n                        outDir = Directory.CreateDirectory(Path.Combine(\r\n                            outDir.FullName,\r\n                            processedarchive.Name.Replace(\".archive\", \"\")));\r\n                    }\r\n                }\r\n\r\n                // read archive\r\n                var ar = new Archive(processedarchive.FullName);\r\n\r\n                // run\r\n                if (hash != 0)\r\n                {\r\n                    ar.UncookSingle(hash, outDir, uext, flip);\r\n                    logger.LogString($\" {ar.ArchiveAbsolutePath}: Uncooked one file: {hash}\", Logtype.Success);\r\n                }\r\n                else\r\n                {\r\n                    var r = ar.UncookAll(outDir, pattern, regex, uext, flip);\r\n                    logger.LogString($\" {ar.ArchiveAbsolutePath}: Uncooked {r.Item1.Count}/{r.Item2} files.\",\r\n                        Logtype.Success);\r\n                }\r\n            }\r\n\r\n            return;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "CP77Tools.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.30709.64\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"CP77Tools\", \"CP77Tools\\CP77Tools.csproj\", \"{37C3288C-A6D7-4C71-8174-0F2F6D47938E}\"\r\nEndProject\r\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"CP77.MSTests\", \"CP77.MSTests\\CP77.MSTests.csproj\", \"{78302151-8D4B-407B-A2FE-5774C471F8AA}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Any CPU = Debug|Any CPU\r\n\t\tRelease|Any CPU = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{37C3288C-A6D7-4C71-8174-0F2F6D47938E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{37C3288C-A6D7-4C71-8174-0F2F6D47938E}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{37C3288C-A6D7-4C71-8174-0F2F6D47938E}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{37C3288C-A6D7-4C71-8174-0F2F6D47938E}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\t\t{78302151-8D4B-407B-A2FE-5774C471F8AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r\n\t\t{78302151-8D4B-407B-A2FE-5774C471F8AA}.Debug|Any CPU.Build.0 = Debug|Any CPU\r\n\t\t{78302151-8D4B-407B-A2FE-5774C471F8AA}.Release|Any CPU.ActiveCfg = Release|Any CPU\r\n\t\t{78302151-8D4B-407B-A2FE-5774C471F8AA}.Release|Any CPU.Build.0 = Release|Any CPU\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {D17FF89E-56D7-4FBF-9C1D-90FA8B7813DD}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 rfuzzo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# CP77Tools\r\n\r\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)\r\n[![Discord](https://img.shields.io/discord/717692382849663036.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/Epkq79kd96)\r\n[![CodeFactor](https://www.codefactor.io/repository/github/wolvenkit/cp77tools/badge)](https://www.codefactor.io/repository/github/wolvenkit/cp77tools)\r\n![GitHub all releases](https://img.shields.io/github/downloads/rfuzzo/cp77tools/total)\r\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/rfuzzo/cp77tools)](https://github.com/WolvenKit/CP77Tools/releases)\r\n[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/rfuzzo/cp77tools?include_prereleases)](https://github.com/WolvenKit/CP77Tools/releases)\r\n\r\n\r\n-------------\r\n\r\n# ❗  This repository is archived. \r\n\r\n# ❗  It will be maintained here: https://github.com/WolvenKit/Wolvenkit\r\n\r\n\r\n-------------\r\n\r\nModding tools for the CDPR Cyberpunk 2077 video game.\r\n\r\n## Latest Stable Release\r\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/rfuzzo/cp77tools)](https://github.com/WolvenKit/CP77Tools/releases) https://github.com/WolvenKit/CP77Tools/releases\r\n\r\n\r\n## Latest Beta Release\r\n[![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/rfuzzo/cp77tools?include_prereleases)](https://github.com/WolvenKit/CP77Tools/releases) https://github.com/WolvenKit/CP77Tools/releases\r\n\r\n-------------\r\n\r\n❗  **requires NET5.0.** (if you don't have it: https://dotnet.microsoft.com/download/dotnet/5.0)\r\n\r\n❗  **The cp77 tools require oo2ext_7_win64.dll to work.** Copy and paste the dll found here `Cyberpunk 2077\\bin\\x64\\oo2ext_7_win64.dll` into the cp77Tools folder.\r\n\r\nIf you are building from source, the dll needs to be in the same folder as the build .exe, e.g.\r\nC:\\cpmod\\CP77Tools\\CP77Tools\\bin\\Debug\\net5.0\\oo2ext_7_win64.dll\r\n\r\n-------------\r\n\r\n❓  **Check the wiki for guides and how to use this tool:** https://github.com/WolvenKit/CP77Tools/wiki\r\n\r\n\r\n## Usage:\r\n\r\n❗  **Surround paths with quotation marks** (if the path contains spaces: e.g. `archive -e -p \"C:\\Cyberpunk 2077\\modding\"`)\r\n\r\n* displays the general help: list all commands\r\n`--help`\r\n\r\n* displays help for a specific command, e.g. \r\n`archive -h`\r\n\r\n### Main functions\r\n* extract all files from archive\r\n`unbundle -p \"PATH TO ARCHIVE\"`\r\n\r\n* extract all textures from archive (supports conversion to tga, bmp, jpg, png, dds)\r\n`uncook --uext png -p \"PATH TO ARCHIVE\"`\r\n\r\n* rebuild textures and buffers (this step is needed when packing textures!, see the wiki for how to use this command)\r\n`rebuild -p \"PATH TO FOLDER\" -b -t --keep --unsaferaw`\r\n\r\n* pack a folder into an .archive (see the wiki for how to use this command)\r\n`pack -p \"PATH TO FOLDER\"`\r\n\r\n### Debug Options\r\n* dumps property info from extracted cr2w file\r\n`cr2w -c -p \"<PATH TO FILE>\"`\r\n"
  }
]