mirror of
https://github.com/projectpokemon/PCDWC4Converter.git
synced 2026-09-08 03:15:26 -05:00
Initial commit
This commit is contained in:
65
.gitignore
vendored
Normal file
65
.gitignore
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
### Windows template
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
### macOS template
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# JetBrains
|
||||
.idea/
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
bin/
|
||||
obj/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
16
LCRng.cs
Normal file
16
LCRng.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace PCDWC4Converter;
|
||||
|
||||
public class LCRng
|
||||
{
|
||||
private uint _seed;
|
||||
|
||||
public LCRng(uint seed = 0) => _seed = seed;
|
||||
|
||||
public uint Next() => _seed = _seed * 0x41C64E6Du + 0x00006073u;
|
||||
|
||||
public uint NextH() => Next() >> 0x10;
|
||||
|
||||
public uint Prev() => _seed = _seed * 0xEEB9EB65u + 0xA3561A1u;
|
||||
|
||||
public uint PrevH() => Prev() >> 0x10;
|
||||
}
|
||||
10
PCDWC4Converter.csproj
Normal file
10
PCDWC4Converter.csproj
Normal file
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
16
PCDWC4Converter.sln
Normal file
16
PCDWC4Converter.sln
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PCDWC4Converter", "PCDWC4Converter.csproj", "{CAED887B-C68C-4EF4-9897-BA1D25ECBC89}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CAED887B-C68C-4EF4-9897-BA1D25ECBC89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CAED887B-C68C-4EF4-9897-BA1D25ECBC89}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CAED887B-C68C-4EF4-9897-BA1D25ECBC89}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CAED887B-C68C-4EF4-9897-BA1D25ECBC89}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
88
PokemonData.cs
Normal file
88
PokemonData.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace PCDWC4Converter;
|
||||
|
||||
public static class PokemonData
|
||||
{
|
||||
private static readonly int[][] _blockPositions =
|
||||
{
|
||||
new[] { 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3 },
|
||||
new[] { 1, 1, 2, 3, 2, 3, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2 },
|
||||
new[] { 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2, 0, 0, 0, 0, 0, 0, 3, 2, 3, 2, 1, 1 },
|
||||
new[] { 3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
private static void XorCrypt(Span<byte> data)
|
||||
{
|
||||
var pid = BinaryPrimitives.ReadUInt32LittleEndian(data[..4]);
|
||||
var initialSeed = BinaryPrimitives.ReadUInt16LittleEndian(data[6..8]);
|
||||
|
||||
var rng = new LCRng(initialSeed);
|
||||
|
||||
for (var i = 8; i < 236; i += 2)
|
||||
{
|
||||
if (i == 136)
|
||||
{
|
||||
rng = new LCRng(pid);
|
||||
}
|
||||
|
||||
var dataBlock = data[i..];
|
||||
var value = BinaryPrimitives.ReadUInt16LittleEndian(dataBlock);
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(dataBlock, (ushort)(value ^ rng.NextH()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void Unshuffle(Span<byte> data)
|
||||
{
|
||||
var pid = BinaryPrimitives.ReadUInt32LittleEndian(data[..4]);
|
||||
var shiftValue = ((pid & 0x3E000) >> 0xD) % 24;
|
||||
|
||||
// Offset by first block
|
||||
data = data.Slice(8, 32 * 4);
|
||||
|
||||
Span<byte> originalData = stackalloc byte[32 * 4];
|
||||
data.CopyTo(originalData);
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
// Unshuffle the data
|
||||
originalData.Slice(32 * _blockPositions[i][shiftValue], 32).CopyTo(data);
|
||||
|
||||
// Increment to next block
|
||||
data = data[32..];
|
||||
}
|
||||
}
|
||||
|
||||
private static void Shuffle(Span<byte> data)
|
||||
{
|
||||
var pid = BinaryPrimitives.ReadUInt32LittleEndian(data[..4]);
|
||||
var shiftValue = ((pid & 0x3E000) >> 0xD) % 24;
|
||||
|
||||
// Offset by first block
|
||||
data = data.Slice(8, 32 * 4);
|
||||
|
||||
Span<byte> originalData = stackalloc byte[32 * 4];
|
||||
data.CopyTo(originalData);
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
// Shuffle the data
|
||||
originalData[..32].CopyTo(data.Slice(_blockPositions[i][shiftValue], 32));
|
||||
|
||||
// Increment to next block
|
||||
originalData = originalData[32..];
|
||||
}
|
||||
}
|
||||
|
||||
public static void DecryptData(Span<byte> pkmData)
|
||||
{
|
||||
XorCrypt(pkmData);
|
||||
Unshuffle(pkmData);
|
||||
}
|
||||
|
||||
public static void EncryptData(Span<byte> pkmData)
|
||||
{
|
||||
XorCrypt(pkmData);
|
||||
Shuffle(pkmData);
|
||||
}
|
||||
}
|
||||
86
Program.cs
Normal file
86
Program.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using PCDWC4Converter;
|
||||
|
||||
Console.WriteLine("PCDWC4Converter v1.0 by Sabresite");
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Console.WriteLine("Usage: ./pcdwc4converter <path-to-folder-or-file>");
|
||||
return;
|
||||
}
|
||||
|
||||
var path = args[0];
|
||||
var fileAttr = File.GetAttributes(path);
|
||||
|
||||
var extensions = new [] { ".pcd", ".wc4" };
|
||||
var files = Array.Empty<FileInfo>();
|
||||
|
||||
if ((fileAttr & FileAttributes.Directory) != 0)
|
||||
{
|
||||
files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
|
||||
.Select(file => new FileInfo(file))
|
||||
.Where(file => extensions.Contains(file.Extension) && file.Length == 856)
|
||||
.ToArray();
|
||||
}
|
||||
else if (File.Exists(path))
|
||||
{
|
||||
var file = new FileInfo(path);
|
||||
if (extensions.Contains(file.Extension) && file.Length == 856)
|
||||
{
|
||||
files = new[] { file };
|
||||
}
|
||||
}
|
||||
|
||||
if (files.Length == 0)
|
||||
{
|
||||
Console.WriteLine("No files found to convert.");
|
||||
return;
|
||||
}
|
||||
|
||||
// All PCD data
|
||||
Span<byte> fileData = stackalloc byte[856];
|
||||
|
||||
for (var i = 0; i < files.Length; i++)
|
||||
{
|
||||
var file = files[i];
|
||||
using (var fs = file.OpenRead())
|
||||
{
|
||||
var bytesRead = fs.Read(fileData);
|
||||
if (bytesRead != 856)
|
||||
{
|
||||
Console.WriteLine("Failed to read file {0}", file.FullName);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var newExtension = "";
|
||||
|
||||
switch (file.Extension)
|
||||
{
|
||||
case ".pcd":
|
||||
{
|
||||
PokemonData.DecryptData(fileData[8..]);
|
||||
newExtension = ".wc4";
|
||||
break;
|
||||
}
|
||||
case ".wc4":
|
||||
{
|
||||
PokemonData.EncryptData(fileData[8..]);
|
||||
newExtension = ".pcd";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var newFilePath = Path.Join(file.DirectoryName, $"{file.Name[..^file.Extension.Length]}{newExtension}");
|
||||
Console.WriteLine("New File {0}", newFilePath);
|
||||
try
|
||||
{
|
||||
File.Delete(newFilePath);
|
||||
using var writer = new FileStream(newFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
writer.Write(fileData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Failed to write file {0}", newFilePath);
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user