mirror of
https://github.com/projectpokemon/PCD2WC4.git
synced 2026-09-08 02:25:25 -05:00
Updates to v1.1. Fixes a few bugs and refactors
This commit is contained in:
18
build.sh
18
build.sh
@@ -1,9 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GOOS=windows GOARCH=amd64 go build -o ./build/windows/pcd2wc4.exe
|
||||
GOOS=darwin GOARCH=amd64 go build -o ./build/darwin/pcd2wc4
|
||||
GOOS=linux GOARCH=amd64 go build -o ./build/linux/pcd2wc4
|
||||
rm -rf ./build
|
||||
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o ./build/windows/pcd2wc4.exe
|
||||
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o ./build/darwin/pcd2wc4
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ./build/linux/pcd2wc4
|
||||
|
||||
upx --brute ./build/darwin/pcd2wc4
|
||||
upx --brute ./build/linux/pcd2wc4
|
||||
upx --brute ./build/windows/pcd2wc4.exe
|
||||
|
||||
cp README.md ./build/darwin
|
||||
cp README.md ./build/linux
|
||||
cp README.md ./build/windows
|
||||
cp README.md ./build/windows
|
||||
|
||||
zip -j -9 ./build/pcd2wc4_v1.1_darwin.zip ./build/darwin/*
|
||||
zip -j -9 ./build/pcd2wc4_v1.1_linux.zip ./build/linux/*
|
||||
zip -j -9 ./build/pcd2wc4_v1.1_windows.zip ./build/windows/*
|
||||
175
pcd2wc4.go
175
pcd2wc4.go
@@ -2,17 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
. "github.com/projectpokemon/PCD2WC4/util"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const version = "1.0.0"
|
||||
const version = "1.1.0"
|
||||
|
||||
func main() {
|
||||
log.Printf("PCD to WC4 by Sabresite v%s%s", version, LineBreak)
|
||||
@@ -30,9 +27,33 @@ func main() {
|
||||
|
||||
wait.Add(1)
|
||||
go func(ch chan string) {
|
||||
for f := range ch {
|
||||
log.Printf("Processing file %s%s", f, LineBreak)
|
||||
processFile(f, ch)
|
||||
for p := range ch {
|
||||
log.Printf("Processing %s%s", p, LineBreak)
|
||||
|
||||
fi, isDir := GetFileStat(p, regexp.MustCompile("\\.pcd$"), 856)
|
||||
|
||||
if isDir {
|
||||
go func(dir string) {
|
||||
for file := range GetFiles(dir) {
|
||||
ch <- file
|
||||
}
|
||||
}(p)
|
||||
continue
|
||||
}
|
||||
|
||||
if fi == nil { continue }
|
||||
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
log.Printf("Error: Unable to open file %s%s", p, LineBreak)
|
||||
continue
|
||||
}
|
||||
|
||||
saveData := ConvertWondercard(f)
|
||||
_ = f.Close()
|
||||
|
||||
if saveData == nil { return }
|
||||
SaveWondercard(saveData, p[0:len(p)-4] + ".wc4")
|
||||
}
|
||||
|
||||
// Signal
|
||||
@@ -50,147 +71,9 @@ func main() {
|
||||
end()
|
||||
}
|
||||
|
||||
func processFile(p string, ch chan<- string) {
|
||||
info, err := os.Stat(p)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("File: %s does not exist%s", p, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if os.IsPermission(err) {
|
||||
log.Printf("File: %s cannot be accessed%s", p, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(p, ".pcd") {
|
||||
log.Printf("File: %s is not a PCD file%s", p, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if info.Size() != 856 {
|
||||
log.Printf("File: %s is not the correct size%s", p, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
getFiles(info.Name(), ch)
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Open(info.Name())
|
||||
if err != nil {
|
||||
log.Printf("%v%s", err, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
saveData := convertWondercard(f)
|
||||
_ = f.Close()
|
||||
|
||||
if saveData != nil {
|
||||
// Save the data
|
||||
newPath := p[0:len(p)-4] + ".wc4"
|
||||
newFile, err := os.Create(newPath)
|
||||
if err != nil {
|
||||
log.Printf("File: Error opening new file file %s%s", newPath, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
writer := bufio.NewWriter(newFile)
|
||||
if _, err := writer.Write(saveData); err != nil {
|
||||
log.Printf("File: Error writing new file %v%s", err, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if err := writer.Flush(); err != nil {
|
||||
log.Printf("File: Error flushing new file %v%s", err, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
_ = newFile.Close()
|
||||
log.Printf("Created %s%s", newPath, LineBreak)
|
||||
}
|
||||
}
|
||||
|
||||
func convertWondercard(f *os.File) []byte {
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
buf := make([]byte, 856)
|
||||
_, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
log.Printf("Error reading data for %s%s", f.Name(), LineBreak)
|
||||
return nil
|
||||
}
|
||||
|
||||
pkmBuf := buf[8:244]
|
||||
|
||||
pid := binary.LittleEndian.Uint32(pkmBuf[0:4])
|
||||
seed := uint32(binary.LittleEndian.Uint16(pkmBuf[6:8]))
|
||||
|
||||
shiftValue := ((pid & 0x3E000) >> 0xD) % 24
|
||||
|
||||
decryptBuf := make([]byte, 228)
|
||||
|
||||
prng := NewPokemonRng(seed)
|
||||
|
||||
// Decrypt
|
||||
for i := 0; i < 228; i += 2 {
|
||||
if i == 128 { prng = NewPokemonRng(pid) }
|
||||
v := binary.LittleEndian.Uint16(pkmBuf[i+8:i+10])
|
||||
binary.LittleEndian.PutUint16(decryptBuf[i:i+2], v ^ prng.Next().H())
|
||||
}
|
||||
|
||||
// Unshuffle the blocks
|
||||
newBlocks := make([]byte, 228)
|
||||
|
||||
for block := 0; block < 4; block++ {
|
||||
pos := 32 * blockPositions[block][shiftValue]
|
||||
blockPos := block * 32
|
||||
nextBlockPos := (block+1) * 32
|
||||
copy(newBlocks[blockPos:nextBlockPos], decryptBuf[pos:32+pos])
|
||||
}
|
||||
|
||||
// Copy party stuff
|
||||
copy(newBlocks[128:228], decryptBuf[128:228])
|
||||
|
||||
wcBuf := make([]byte, 856)
|
||||
copy(wcBuf[0:16], buf[0:16])
|
||||
copy(wcBuf[16:244], newBlocks)
|
||||
copy(wcBuf[244:], buf[244:])
|
||||
|
||||
return wcBuf
|
||||
}
|
||||
|
||||
func getFiles(dir string, ch chan<- string) {
|
||||
walk := func (path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f := info.Name()
|
||||
|
||||
if strings.HasSuffix(f, ".pcd") {
|
||||
ch <- f
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := filepath.Walk(dir, walk)
|
||||
if err != nil {
|
||||
log.Printf("%v%s", err, LineBreak)
|
||||
}
|
||||
}
|
||||
|
||||
func end() {
|
||||
log.Print("Press any key to exit...")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
_, _, _ = reader.ReadRune()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
var blockPositions = [][]int{
|
||||
{0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3},
|
||||
{1, 1, 2, 3, 2, 3, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2},
|
||||
{2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2, 0, 0, 0, 0, 0, 0, 3, 2, 3, 2, 1, 1},
|
||||
{3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0},
|
||||
}
|
||||
53
util/files.go
Normal file
53
util/files.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func GetFiles(dir string) <-chan string {
|
||||
ch := make(chan string, 1)
|
||||
|
||||
go func(d string, fch chan<- string) {
|
||||
walk := func (path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fch <- path
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = filepath.Walk(d, walk)
|
||||
close(fch)
|
||||
}(dir, ch)
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func GetFileStat(p string, pattern *regexp.Regexp, size int64) (info os.FileInfo, isDir bool) {
|
||||
info, err := os.Stat(p)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("File: %s does not exist%s", p, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if os.IsPermission(err) {
|
||||
log.Printf("File: %s cannot be accessed%s", p, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if !pattern.MatchString(p) {
|
||||
return
|
||||
}
|
||||
|
||||
if size > -1 && info.Size() != size {
|
||||
return
|
||||
}
|
||||
|
||||
isDir = info.IsDir()
|
||||
return
|
||||
}
|
||||
89
wc.go
Normal file
89
wc.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
. "github.com/projectpokemon/PCD2WC4/util"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var blockPositions = [][]int{
|
||||
{0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3, 1, 1, 2, 3, 2, 3},
|
||||
{1, 1, 2, 3, 2, 3, 0, 0, 0, 0, 0, 0, 2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2},
|
||||
{2, 3, 1, 1, 3, 2, 2, 3, 1, 1, 3, 2, 0, 0, 0, 0, 0, 0, 3, 2, 3, 2, 1, 1},
|
||||
{3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 3, 2, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0},
|
||||
}
|
||||
|
||||
func ConvertWondercard(f *os.File) []byte {
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
buf := make([]byte, 856)
|
||||
_, err := io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
log.Printf("Error reading data for %s%s", f.Name(), LineBreak)
|
||||
return nil
|
||||
}
|
||||
|
||||
pkmBuf := buf[8:244]
|
||||
|
||||
pid := binary.LittleEndian.Uint32(pkmBuf[0:4])
|
||||
seed := uint32(binary.LittleEndian.Uint16(pkmBuf[6:8]))
|
||||
|
||||
shiftValue := ((pid & 0x3E000) >> 0xD) % 24
|
||||
|
||||
decryptBuf := make([]byte, 228)
|
||||
|
||||
prng := NewPokemonRng(seed)
|
||||
|
||||
// Decrypt
|
||||
for i := 0; i < 228; i += 2 {
|
||||
if i == 128 { prng = NewPokemonRng(pid) }
|
||||
v := binary.LittleEndian.Uint16(pkmBuf[i+8:i+10])
|
||||
binary.LittleEndian.PutUint16(decryptBuf[i:i+2], v ^ prng.Next().H())
|
||||
}
|
||||
|
||||
// Unshuffle the blocks
|
||||
newBlocks := make([]byte, 228)
|
||||
|
||||
for block := 0; block < 4; block++ {
|
||||
pos := 32 * blockPositions[block][shiftValue]
|
||||
blockPos := block * 32
|
||||
nextBlockPos := (block+1) * 32
|
||||
copy(newBlocks[blockPos:nextBlockPos], decryptBuf[pos:32+pos])
|
||||
}
|
||||
|
||||
// Copy party stuff
|
||||
copy(newBlocks[128:228], decryptBuf[128:228])
|
||||
|
||||
wcBuf := make([]byte, 856)
|
||||
copy(wcBuf[0:16], buf[0:16])
|
||||
copy(wcBuf[16:244], newBlocks)
|
||||
copy(wcBuf[244:], buf[244:])
|
||||
|
||||
return wcBuf
|
||||
}
|
||||
|
||||
func SaveWondercard(saveData []byte, newPath string) {
|
||||
// Save the data
|
||||
newFile, err := os.Create(newPath)
|
||||
if err != nil {
|
||||
log.Printf("File: Error opening new file file %s%s", newPath, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
writer := bufio.NewWriter(newFile)
|
||||
if _, err := writer.Write(saveData); err != nil {
|
||||
log.Printf("File: Error writing new file %v%s", err, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
if err := writer.Flush(); err != nil {
|
||||
log.Printf("File: Error flushing new file %v%s", err, LineBreak)
|
||||
return
|
||||
}
|
||||
|
||||
_ = newFile.Close()
|
||||
log.Printf("Created %s%s", newPath, LineBreak)
|
||||
}
|
||||
Reference in New Issue
Block a user