diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..32becce
--- /dev/null
+++ b/.gitignore
@@ -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/
+
diff --git a/LCRng.cs b/LCRng.cs
new file mode 100644
index 0000000..a6b76cd
--- /dev/null
+++ b/LCRng.cs
@@ -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;
+}
diff --git a/PCDWC4Converter.csproj b/PCDWC4Converter.csproj
new file mode 100644
index 0000000..b9de063
--- /dev/null
+++ b/PCDWC4Converter.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/PCDWC4Converter.sln b/PCDWC4Converter.sln
new file mode 100644
index 0000000..ef38814
--- /dev/null
+++ b/PCDWC4Converter.sln
@@ -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
diff --git a/PokemonData.cs b/PokemonData.cs
new file mode 100644
index 0000000..2e49269
--- /dev/null
+++ b/PokemonData.cs
@@ -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 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 data)
+ {
+ var pid = BinaryPrimitives.ReadUInt32LittleEndian(data[..4]);
+ var shiftValue = ((pid & 0x3E000) >> 0xD) % 24;
+
+ // Offset by first block
+ data = data.Slice(8, 32 * 4);
+
+ Span 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 data)
+ {
+ var pid = BinaryPrimitives.ReadUInt32LittleEndian(data[..4]);
+ var shiftValue = ((pid & 0x3E000) >> 0xD) % 24;
+
+ // Offset by first block
+ data = data.Slice(8, 32 * 4);
+
+ Span 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 pkmData)
+ {
+ XorCrypt(pkmData);
+ Unshuffle(pkmData);
+ }
+
+ public static void EncryptData(Span pkmData)
+ {
+ XorCrypt(pkmData);
+ Shuffle(pkmData);
+ }
+}
diff --git a/Program.cs b/Program.cs
new file mode 100644
index 0000000..c164d9b
--- /dev/null
+++ b/Program.cs
@@ -0,0 +1,86 @@
+using PCDWC4Converter;
+
+Console.WriteLine("PCDWC4Converter v1.0 by Sabresite");
+
+if (args.Length == 0)
+{
+ Console.WriteLine("Usage: ./pcdwc4converter ");
+ return;
+}
+
+var path = args[0];
+var fileAttr = File.GetAttributes(path);
+
+var extensions = new [] { ".pcd", ".wc4" };
+var files = Array.Empty();
+
+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 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);
+ }
+}
diff --git a/README.md b/README.md
index a7a3cc1..bc546fc 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,13 @@
-# PCDWC4Converter
-Converter for PCD<->WC4
+# PCD/WC4 Converter for Pokemon Gen 4 Wondercards
+
+## Requirements
+* .NET 6
+
+### Usage
+```bash
+./pcdwc4converter
+```
+
+## Building Solution
+* .NET 6 SDK
+* Visual Studio 2022 or Rider 2021.3+