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

[](https://github.com/WolvenKit/CP77Tools/releases)
[](https://github.com/WolvenKit/CP77Tools/releases)
-------------
# ❗ This repository is archived.
# ❗ It will be maintained here: https://github.com/WolvenKit/Wolvenkit
-------------
Modding tools for the CDPR Cyberpunk 2077 video game.
## Latest Stable Release
[](https://github.com/WolvenKit/CP77Tools/releases) https://github.com/WolvenKit/CP77Tools/releases
## Latest Beta Release
[](https://github.com/WolvenKit/CP77Tools/releases) https://github.com/WolvenKit/CP77Tools/releases
-------------
❗ **requires NET5.0.** (if you don't have it: https://dotnet.microsoft.com/download/dotnet/5.0)
❗ **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.
If you are building from source, the dll needs to be in the same folder as the build .exe, e.g.
C:\cpmod\CP77Tools\CP77Tools\bin\Debug\net5.0\oo2ext_7_win64.dll
-------------
❓ **Check the wiki for guides and how to use this tool:** https://github.com/WolvenKit/CP77Tools/wiki
## Usage:
❗ **Surround paths with quotation marks** (if the path contains spaces: e.g. `archive -e -p "C:\Cyberpunk 2077\modding"`)
* displays the general help: list all commands
`--help`
* displays help for a specific command, e.g.
`archive -h`
### Main functions
* extract all files from archive
`unbundle -p "PATH TO ARCHIVE"`
* extract all textures from archive (supports conversion to tga, bmp, jpg, png, dds)
`uncook --uext png -p "PATH TO ARCHIVE"`
* rebuild textures and buffers (this step is needed when packing textures!, see the wiki for how to use this command)
`rebuild -p "PATH TO FOLDER" -b -t --keep --unsaferaw`
* pack a folder into an .archive (see the wiki for how to use this command)
`pack -p "PATH TO FOLDER"`
### Debug Options
* dumps property info from extracted cr2w file
`cr2w -c -p "<PATH TO FILE>"`
gitextract_ckm5538u/ ├── .gitattributes ├── .github/ │ └── workflows/ │ ├── dotnet-core.yml │ └── nightly.yml ├── .gitignore ├── BUILD.md ├── CHANGELOG.txt ├── CP77.MSTests/ │ ├── ArchiveTests.cs │ ├── CP77.MSTests.csproj │ ├── Cr2wUnitTest.cs │ ├── FNV1A64Tests.cs │ ├── GameUnitTest.cs │ └── appsettings.json ├── CP77Tools/ │ ├── CP77Tools.csproj │ ├── Commands/ │ │ ├── CR2WCommand.cs │ │ ├── DumpCommand.cs │ │ ├── ExportCommand.cs │ │ ├── HashCommand.cs │ │ ├── OodleCommand.cs │ │ ├── PackCommand.cs │ │ ├── RebuildCommand.cs │ │ ├── UnbundleCommand.cs │ │ └── UncookCommand.cs │ ├── Extensions/ │ │ └── CommandLineExtensions.cs │ ├── Program.cs │ └── Tasks/ │ ├── Cr2wTask.cs │ ├── DumpTask.cs │ ├── ExportTask.cs │ ├── HashTask.cs │ ├── OodleTask.cs │ ├── PackTask.cs │ ├── RebuildTask.cs │ ├── UnbundleTask.cs │ └── UncookTask.cs ├── CP77Tools.sln ├── LICENSE └── README.md
SYMBOL INDEX (195 symbols across 24 files)
FILE: CP77.MSTests/ArchiveTests.cs
class ArchiveTests (line 21) | [TestClass]
method Test_Unbundle (line 24) | [TestMethod]
method Test_Uncook (line 30) | [TestMethod]
method test_archive (line 37) | private void test_archive(string extension = null)
FILE: CP77.MSTests/Cr2wUnitTest.cs
class Cr2WUnitTest (line 21) | [TestClass]
method SetupClass (line 24) | [ClassInitialize]
method Test_All_Extensions (line 33) | [TestMethod]
method Test_Can_Read_acousticdata (line 39) | [TestMethod]
method Test_Can_Read_actionanimdb (line 45) | [TestMethod]
method Test_Can_Read_aiarch (line 51) | [TestMethod]
method Test_Can_Read_animgraph (line 57) | [TestMethod]
method Test_Can_Read_anims (line 63) | [TestMethod]
method Test_Can_Read_app (line 69) | [TestMethod]
method Test_Can_Read_archetypes (line 75) | [TestMethod]
method Test_Can_Read_areas (line 81) | [TestMethod]
method Test_Can_Read_audio_metadata (line 87) | [TestMethod]
method Test_Can_Read_audiovehcurveset (line 93) | [TestMethod]
method Test_Can_Read_behavior (line 99) | [TestMethod]
method Test_Can_Read_bikecurveset (line 105) | [TestMethod]
method Test_Can_Read_bin (line 111) | [TestMethod]
method Test_Can_Read_bk2 (line 117) | [TestMethod]
method Test_Can_Read_bnk (line 123) | [TestMethod]
method Test_Can_Read_camcurveset (line 129) | [TestMethod]
method Test_Can_Read_cfoliage (line 135) | [TestMethod]
method Test_Can_Read_charcustpreset (line 141) | [TestMethod]
method Test_Can_Read_cminimap (line 147) | [TestMethod]
method Test_Can_Read_community (line 153) | [TestMethod]
method Test_Can_Read_conversations (line 159) | [TestMethod]
method Test_Can_Read_cooked_mlsetup (line 165) | [TestMethod]
method Test_Can_Read_cookedanims (line 171) | [TestMethod]
method Test_Can_Read_cookedapp (line 177) | [TestMethod]
method Test_Can_Read_credits (line 183) | [TestMethod]
method Test_Can_Read_csv (line 189) | [TestMethod]
method Test_Can_Read_cubemap (line 195) | [TestMethod]
method Test_Can_Read_curveset (line 201) | [TestMethod]
method Test_Can_Read_dat (line 207) | [TestMethod]
method Test_Can_Read_devices (line 213) | [TestMethod]
method Test_Can_Read_dtex (line 219) | [TestMethod]
method Test_Can_Read_effect (line 225) | [TestMethod]
method Test_Can_Read_ent (line 231) | [TestMethod]
method Test_Can_Read_env (line 237) | [TestMethod]
method Test_Can_Read_envparam (line 243) | [TestMethod]
method Test_Can_Read_envprobe (line 249) | [TestMethod]
method Test_Can_Read_es (line 255) | [TestMethod]
method Test_Can_Read_facialcustom (line 261) | [TestMethod]
method Test_Can_Read_facialsetup (line 267) | [TestMethod]
method Test_Can_Read_fb2tl (line 273) | [TestMethod]
method Test_Can_Read_fnt (line 279) | [TestMethod]
method Test_Can_Read_folbrush (line 285) | [TestMethod]
method Test_Can_Read_foldest (line 291) | [TestMethod]
method Test_Can_Read_fp (line 297) | [TestMethod]
method Test_Can_Read_gamedef (line 303) | [TestMethod]
method Test_Can_Read_garmentlayerparams (line 309) | [TestMethod]
method Test_Can_Read_genericanimdb (line 315) | [TestMethod]
method Test_Can_Read_geometry_cache (line 321) | [TestMethod]
method Test_Can_Read_gidata (line 327) | [TestMethod]
method Test_Can_Read_gradient (line 333) | [TestMethod]
method Test_Can_Read_hitrepresentation (line 339) | [TestMethod]
method Test_Can_Read_hp (line 345) | [TestMethod]
method Test_Can_Read_ies (line 351) | [TestMethod]
method Test_Can_Read_inkanim (line 357) | [TestMethod]
method Test_Can_Read_inkatlas (line 363) | [TestMethod]
method Test_Can_Read_inkcharcustomization (line 369) | [TestMethod]
method Test_Can_Read_inkfontfamily (line 375) | [TestMethod]
method Test_Can_Read_inkfullscreencomposition (line 381) | [TestMethod]
method Test_Can_Read_inkgamesettings (line 387) | [TestMethod]
method Test_Can_Read_inkhud (line 393) | [TestMethod]
method Test_Can_Read_inklayers (line 399) | [TestMethod]
method Test_Can_Read_inkmenu (line 405) | [TestMethod]
method Test_Can_Read_inkshapecollection (line 411) | [TestMethod]
method Test_Can_Read_inkstyle (line 417) | [TestMethod]
method Test_Can_Read_inktypography (line 423) | [TestMethod]
method Test_Can_Read_inkwidget (line 429) | [TestMethod]
method Test_Can_Read_interaction (line 435) | [TestMethod]
method Test_Can_Read_journal (line 441) | [TestMethod]
method Test_Can_Read_journaldesc (line 447) | [TestMethod]
method Test_Can_Read_json (line 453) | [TestMethod]
method Test_Can_Read_lane_connections (line 459) | [TestMethod]
method Test_Can_Read_lane_polygons (line 465) | [TestMethod]
method Test_Can_Read_lane_spots (line 471) | [TestMethod]
method Test_Can_Read_lights (line 477) | [TestMethod]
method Test_Can_Read_lipmap (line 483) | [TestMethod]
method Test_Can_Read_location (line 489) | [TestMethod]
method Test_Can_Read_locopaths (line 495) | [TestMethod]
method Test_Can_Read_loot (line 501) | [TestMethod]
method Test_Can_Read_mappins (line 507) | [TestMethod]
method Test_Can_Read_mesh (line 513) | [TestMethod]
method Test_Can_Read_mi (line 519) | [TestMethod]
method Test_Can_Read_mlmask (line 525) | [TestMethod]
method Test_Can_Read_mlsetup (line 531) | [TestMethod]
method Test_Can_Read_mltemplate (line 537) | [TestMethod]
method Test_Can_Read_morphtarget (line 543) | [TestMethod]
method Test_Can_Read_mt (line 549) | [TestMethod]
method Test_Can_Read_navmesh (line 555) | [TestMethod]
method Test_Can_Read_null_areas (line 561) | [TestMethod]
method Test_Can_Read_opusinfo (line 567) | [TestMethod]
method Test_Can_Read_opuspak (line 573) | [TestMethod]
method Test_Can_Read_particle (line 579) | [TestMethod]
method Test_Can_Read_phys (line 585) | [TestMethod]
method Test_Can_Read_physicalscene (line 591) | [TestMethod]
method Test_Can_Read_physmatlib (line 597) | [TestMethod]
method Test_Can_Read_poimappins (line 603) | [TestMethod]
method Test_Can_Read_psrep (line 609) | [TestMethod]
method Test_Can_Read_quest (line 615) | [TestMethod]
method Test_Can_Read_questphase (line 621) | [TestMethod]
method Test_Can_Read_regionset (line 627) | [TestMethod]
method Test_Can_Read_remt (line 633) | [TestMethod]
method Test_Can_Read_reslist (line 639) | [TestMethod]
method Test_Can_Read_rig (line 645) | [TestMethod]
method Test_Can_Read_scene (line 651) | [TestMethod]
method Test_Can_Read_scenerid (line 657) | [TestMethod]
method Test_Can_Read_scenesversions (line 663) | [TestMethod]
method Test_Can_Read_smartobject (line 669) | [TestMethod]
method Test_Can_Read_smartobjects (line 675) | [TestMethod]
method Test_Can_Read_sp (line 681) | [TestMethod]
method Test_Can_Read_spatial_representation (line 687) | [TestMethod]
method Test_Can_Read_streamingquerydata (line 693) | [TestMethod]
method Test_Can_Read_streamingsector (line 699) | [TestMethod]
method Test_Can_Read_streamingsector_inplace (line 705) | [TestMethod]
method Test_Can_Read_streamingworld (line 711) | [TestMethod]
method Test_Can_Read_terrainsetup (line 717) | [TestMethod]
method Test_Can_Read_texarray (line 723) | [TestMethod]
method Test_Can_Read_traffic_collisions (line 729) | [TestMethod]
method Test_Can_Read_traffic_persistent (line 735) | [TestMethod]
method Test_Can_Read_voicetags (line 741) | [TestMethod]
method Test_Can_Read_w2mesh (line 747) | [TestMethod]
method Test_Can_Read_w2mi (line 753) | [TestMethod]
method Test_Can_Read_wem (line 759) | [TestMethod]
method Test_Can_Read_workspot (line 765) | [TestMethod]
method Test_Can_Read_xbm (line 771) | [TestMethod]
method Test_Can_Read_xcube (line 777) | [TestMethod]
method Test_Extension (line 785) | private void Test_Extension(string extension = null)
method Read_Archive_Items (line 830) | private IEnumerable<TestResult> Read_Archive_Items(List<ArchiveItem> f...
method TestResultAsCsv (line 892) | private string TestResultAsCsv(IEnumerable<TestResult> results)
class TestResult (line 908) | private class TestResult
type ResultType (line 910) | public enum ResultType
FILE: CP77.MSTests/FNV1A64Tests.cs
class FNV1A64Tests (line 8) | [TestClass]
method TestFNV1a64 (line 11) | [TestMethod]
method TestFNV1a64_NullEnded (line 37) | [TestMethod]
method TestFNV1a64_ByteSpan (line 49) | [TestMethod]
method TestFNV1a64_CharSpan (line 69) | [TestMethod]
FILE: CP77.MSTests/GameUnitTest.cs
class GameUnitTest (line 16) | [TestClass]
method Setup (line 31) | protected static void Setup(TestContext context)
FILE: CP77Tools/Commands/CR2WCommand.cs
class CR2WCommand (line 6) | public class CR2WCommand : Command
method CR2WCommand (line 11) | public CR2WCommand() : base(Name, Description)
FILE: CP77Tools/Commands/DumpCommand.cs
class DumpCommand (line 7) | public class DumpCommand : Command
method DumpCommand (line 12) | public DumpCommand() : base(Name, Description)
FILE: CP77Tools/Commands/ExportCommand.cs
class ExportCommand (line 7) | public class ExportCommand : Command
method ExportCommand (line 12) | public ExportCommand() : base(Name, Description)
FILE: CP77Tools/Commands/HashCommand.cs
class HashCommand (line 7) | public class HashCommand : Command
method HashCommand (line 12) | public HashCommand() : base(Name, Description)
FILE: CP77Tools/Commands/OodleCommand.cs
class OodleCommand (line 6) | public class OodleCommand : Command
method OodleCommand (line 11) | public OodleCommand() : base(Name, Description)
FILE: CP77Tools/Commands/PackCommand.cs
class PackCommand (line 6) | public class PackCommand : Command
method PackCommand (line 11) | public PackCommand() : base(Name, Description)
FILE: CP77Tools/Commands/RebuildCommand.cs
class RebuildCommand (line 6) | public class RebuildCommand : Command
method RebuildCommand (line 11) | public RebuildCommand() : base(Name, Description)
FILE: CP77Tools/Commands/UnbundleCommand.cs
class UnbundleCommand (line 7) | public class UnbundleCommand : Command
method UnbundleCommand (line 12) | public UnbundleCommand() : base(Name, Description)
FILE: CP77Tools/Commands/UncookCommand.cs
class UncookCommand (line 8) | public class UncookCommand : Command
method UncookCommand (line 13) | public UncookCommand() : base(Name, Description)
FILE: CP77Tools/Extensions/CommandLineExtensions.cs
class CommandLineExtensions (line 7) | public class CommandLineExtensions
method ParseText (line 9) | public static IEnumerable<String> ParseText(String line, Char delimite...
FILE: CP77Tools/Program.cs
class Program (line 19) | class Program
method Main (line 21) | [STAThread]
method TryGetGameInstallDir (line 170) | private static string TryGetGameInstallDir()
method TryCopyOodleLib (line 245) | private static bool TryCopyOodleLib()
FILE: CP77Tools/Tasks/Cr2wTask.cs
class ConsoleFunctions (line 9) | public static partial class ConsoleFunctions
method Cr2wTask (line 11) | public static void Cr2wTask(string[] path, string outpath, bool all, b...
method Cr2wTaskInner (line 26) | private static int Cr2wTaskInner(string path, string outpath, bool all...
FILE: CP77Tools/Tasks/DumpTask.cs
class ArchiveDumpObject (line 23) | public class ArchiveDumpObject
class Cr2wChunkInfo (line 30) | public class Cr2wChunkInfo
class Cr2wTextureInfo (line 44) | public class Cr2wTextureInfo
class ConsoleFunctions (line 59) | public static partial class ConsoleFunctions
method DumpTask (line 63) | public static void DumpTask(string[] path, bool imports, bool missingh...
method DumpTaskInner (line 79) | public static int DumpTaskInner(string path, bool imports, bool missin...
FILE: CP77Tools/Tasks/ExportTask.cs
class ConsoleFunctions (line 9) | public static partial class ConsoleFunctions
method ExportTask (line 11) | public static void ExportTask(string[] path, EUncookExtension uncookext)
method ExportTaskInner (line 26) | private static int ExportTaskInner(string path, EUncookExtension uncoo...
FILE: CP77Tools/Tasks/HashTask.cs
class ConsoleFunctions (line 12) | public static partial class ConsoleFunctions
method HashTask (line 15) | public static int HashTask(string[] input, bool missing)
FILE: CP77Tools/Tasks/OodleTask.cs
class ConsoleFunctions (line 9) | public static partial class ConsoleFunctions
method OodleTask (line 12) | public static int OodleTask(string path, string outpath, bool decompress)
FILE: CP77Tools/Tasks/PackTask.cs
class ConsoleFunctions (line 9) | public static partial class ConsoleFunctions
method PackTask (line 16) | public static void PackTask(string[] path, string outpath)
method PackTaskInner (line 31) | private static void PackTaskInner(string path, string outpath, int cp ...
FILE: CP77Tools/Tasks/RebuildTask.cs
class ConsoleFunctions (line 9) | public static partial class ConsoleFunctions
method RebuildTask (line 16) | public static void RebuildTask(string[] path,
method RebuildTaskInner (line 38) | private static void RebuildTaskInner(string path,
FILE: CP77Tools/Tasks/UnbundleTask.cs
class ConsoleFunctions (line 12) | public static partial class ConsoleFunctions
method UnbundleTask (line 16) | public static void UnbundleTask(string[] path, string outpath,
method UnbundleTaskInner (line 33) | private static void UnbundleTaskInner(string path, string outpath,
FILE: CP77Tools/Tasks/UncookTask.cs
class ConsoleFunctions (line 12) | public static partial class ConsoleFunctions
method UncookTask (line 15) | public static void UncookTask(string[] path, string outpath,
method UncookTaskInner (line 32) | private static void UncookTaskInner(string path, string outpath,
Condensed preview — 36 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (122K chars).
[
{
"path": ".gitattributes",
"chars": 795,
"preview": "# 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 "
},
{
"path": ".github/workflows/dotnet-core.yml",
"chars": 1721,
"preview": "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 "
},
{
"path": ".github/workflows/nightly.yml",
"chars": 1755,
"preview": "name: Nightly\r\non:\r\n push:\r\n branches: [ nightly ]\r\n pull_request:\r\n branches: [ nightly ]\r\n workflow_dispatch:"
},
{
"path": ".gitignore",
"chars": 730,
"preview": "# 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"
},
{
"path": "BUILD.md",
"chars": 511,
"preview": "# 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 --"
},
{
"path": "CHANGELOG.txt",
"chars": 461,
"preview": "# 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 "
},
{
"path": "CP77.MSTests/ArchiveTests.cs",
"chars": 1089,
"preview": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Configuration;\r\nusing System.IO;\r\nusing System.IO.MemoryM"
},
{
"path": "CP77.MSTests/CP77.MSTests.csproj",
"chars": 1111,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n <PropertyGroup>\r\n <TargetFramework>net5.0-windows</TargetFramework>\r\n\r\n <Is"
},
{
"path": "CP77.MSTests/Cr2wUnitTest.cs",
"chars": 23830,
"preview": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Configuration;\r\nusing System.IO;\r\nusing System.IO.MemoryM"
},
{
"path": "CP77.MSTests/FNV1A64Tests.cs",
"chars": 5371,
"preview": "using System;\r\nusing System.Text;\r\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing WolvenKit.Common.FNV1A;\r\n\r"
},
{
"path": "CP77.MSTests/GameUnitTest.cs",
"chars": 2877,
"preview": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Configuration;\r\nusing System.IO;\r\nusing System.Linq;\r\nus"
},
{
"path": "CP77.MSTests/appsettings.json",
"chars": 112,
"preview": "{\n \"GameDirectory\": \"D:\\\\Program Files\\\\Steam\\\\steamapps\\\\common\\\\Cyberpunk 2077\",\n \"WriteToFile\": true\n}"
},
{
"path": "CP77Tools/CP77Tools.csproj",
"chars": 1999,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\r\n\r\n <PropertyGroup>\r\n <OutputType>Exe</OutputType>\r\n <TargetFramework>net5.0-w"
},
{
"path": "CP77Tools/Commands/CR2WCommand.cs",
"chars": 893,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n public class CR"
},
{
"path": "CP77Tools/Commands/DumpCommand.cs",
"chars": 1355,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\n\r\nnamespace CP77Tools.Commands"
},
{
"path": "CP77Tools/Commands/ExportCommand.cs",
"chars": 790,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing WolvenKit.Common.DDS;\r\n\r\nnamespace CP77Tools.Com"
},
{
"path": "CP77Tools/Commands/HashCommand.cs",
"chars": 667,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\n\r\nnamespace CP77Tools.Commands"
},
{
"path": "CP77Tools/Commands/OodleCommand.cs",
"chars": 723,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n public class Oo"
},
{
"path": "CP77Tools/Commands/PackCommand.cs",
"chars": 779,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n public class Pa"
},
{
"path": "CP77Tools/Commands/RebuildCommand.cs",
"chars": 1504,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\n\r\nnamespace CP77Tools.Commands\r\n{\r\n public class Re"
},
{
"path": "CP77Tools/Commands/UnbundleCommand.cs",
"chars": 1190,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\n\r\nnamespace CP77Tools.Commands"
},
{
"path": "CP77Tools/Commands/UncookCommand.cs",
"chars": 1426,
"preview": "using System.CommandLine;\r\nusing System.CommandLine.Invocation;\r\nusing CP77Tools.Tasks;\r\nusing WolvenKit.Common.DDS;\r\n\r"
},
{
"path": "CP77Tools/Extensions/CommandLineExtensions.cs",
"chars": 1869,
"preview": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Text;\r\n\r\nnamespace CP77Tools.Extensions\r\n{\r\n public c"
},
{
"path": "CP77Tools/Program.cs",
"chars": 10331,
"preview": "using System;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC;\r\nusing System.CommandLine;\r\nusing Sy"
},
{
"path": "CP77Tools/Tasks/Cr2wTask.cs",
"chars": 3864,
"preview": "using System.IO;\r\nusing System.Threading.Tasks;\r\nusing Newtonsoft.Json;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.Servi"
},
{
"path": "CP77Tools/Tasks/DumpTask.cs",
"chars": 19935,
"preview": "using System;\r\nusing System.Collections.Concurrent;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\n//using Syste"
},
{
"path": "CP77Tools/Tasks/ExportTask.cs",
"chars": 1572,
"preview": "using System.IO;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.DDS;\r\nusing WolvenKit.Common."
},
{
"path": "CP77Tools/Tasks/HashTask.cs",
"chars": 1659,
"preview": "using System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing Catel.IoC"
},
{
"path": "CP77Tools/Tasks/OodleTask.cs",
"chars": 1362,
"preview": "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 "
},
{
"path": "CP77Tools/Tasks/PackTask.cs",
"chars": 2159,
"preview": "using System.IO;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77To"
},
{
"path": "CP77Tools/Tasks/RebuildTask.cs",
"chars": 1965,
"preview": "using System.IO;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W;\r\nusing WolvenKit.Common.Services;\r\n\r\nnamespace CP77To"
},
{
"path": "CP77Tools/Tasks/UnbundleTask.cs",
"chars": 5455,
"preview": "using System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W"
},
{
"path": "CP77Tools/Tasks/UncookTask.cs",
"chars": 4408,
"preview": "using System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\nusing CP77.CR2W"
},
{
"path": "CP77Tools.sln",
"chars": 1620,
"preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 16\r\nVisualStudioVersion = 16.0.3"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2020 rfuzzo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "README.md",
"chars": 2991,
"preview": "# CP77Tools\r\n\r\n[](https://lbesson.mit-license.org/)\r\n[!"
}
]
About this extraction
This page contains the full source code of the WolvenKit/CP77Tools GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 36 files (109.3 KB), approximately 24.6k tokens, and a symbol index with 195 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.