Initial commit

This commit is contained in:
Kamron Batman
2022-05-01 12:21:13 -07:00
parent 091f084307
commit 9dae151b87
7 changed files with 294 additions and 2 deletions

65
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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);
}
}

View File

@@ -1,2 +1,13 @@
# PCDWC4Converter
Converter for PCD&lt;->WC4
# PCD/WC4 Converter for Pokemon Gen 4 Wondercards
## Requirements
* .NET 6
### Usage
```bash
./pcdwc4converter <path-to-folder-or-file>
```
## Building Solution
* .NET 6 SDK
* Visual Studio 2022 or Rider 2021.3+