diff --git a/build.sh b/build.sh index 5fce5b4..ddc0c11 100755 --- a/build.sh +++ b/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 \ No newline at end of file +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/* \ No newline at end of file diff --git a/pcd2wc4.go b/pcd2wc4.go index 2cd166f..d083d75 100644 --- a/pcd2wc4.go +++ b/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}, } \ No newline at end of file diff --git a/PRNG.go b/rng.go similarity index 100% rename from PRNG.go rename to rng.go diff --git a/util/files.go b/util/files.go new file mode 100644 index 0000000..f0226a2 --- /dev/null +++ b/util/files.go @@ -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 +} \ No newline at end of file diff --git a/wc.go b/wc.go new file mode 100644 index 0000000..200153e --- /dev/null +++ b/wc.go @@ -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) +} \ No newline at end of file