├── .gitignore ├── COPYING ├── Makefile ├── README.md ├── afp ├── archive.go └── model.go ├── cmd ├── pso2-afp │ └── afp.go ├── pso2-download │ └── download.go ├── pso2-ice │ └── ice.go ├── pso2-net │ └── net.go ├── pso2-text │ └── text.go ├── pso2-trans-apply │ └── trans.go └── pso2-trans │ └── trans.go ├── download ├── cmd │ ├── config.go │ ├── ddraw.go │ └── download.go ├── download.go └── patchlist.go ├── ice ├── prsreader.go └── prswriter.go ├── net ├── cipher.go ├── connection.go ├── handlers.go ├── log.go ├── packets │ ├── block.go │ ├── blockresponse.go │ ├── blocks.go │ ├── cipher.go │ ├── packet.go │ ├── room.go │ ├── ship.go │ └── string.go ├── proxy.go └── routing.go ├── pso2-download.md ├── text ├── tagfile.go └── textfile.go ├── trans ├── cmd │ └── trans.go ├── db.go └── trans.go └── util ├── bufreader.go ├── closeguard.go ├── copyfile.go ├── memreader.go ├── pipe.go ├── readerat.go ├── readn.go ├── readwriter.go └── seeker.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | /pso2-ice 3 | /pso2-afp 4 | /pso2-text 5 | /pso2-net 6 | /pso2-trans 7 | /pso2-trans-apply 8 | /pso2-download 9 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CMDS := pso2-ice pso2-afp pso2-text pso2-trans pso2-trans-apply pso2-net pso2-download 2 | GO := go 3 | UTIL_GO := $(wildcard util/*.go) 4 | NET_PACKETS_GO := $(wildcard net/packets/*.go) 5 | NET_GO := $(wildcard net/*.go) $(NET_PACKETS_GO) 6 | ICE_GO := $(wildcard ice/*.go) $(UTIL_GO) 7 | AFP_GO := $(wildcard afp/*.go) $(UTIL_GO) 8 | TEXT_GO := $(wildcard text/*.go) $(UTIL_GO) 9 | TRANS_GO := $(wildcard trans/*.go) $(UTIL_GO) 10 | TRANS_CMD_GO := $(wildcard trans/cmd/*.go) $(TRANS_GO) $(TEXT_GO) $(ICE_GO) 11 | DOWNLOAD_GO := $(wildcard download/*.go) 12 | DOWNLOAD_CMD_GO := $(wildcard download/cmd/*.go) $(DOWNLOAD_GO) 13 | 14 | all: $(CMDS) 15 | 16 | $(CMDS): 17 | $(GO) build -o $@ ./cmd/$@ 18 | 19 | clean: 20 | rm -f $(CMDS) 21 | 22 | pso2-ice: $(ICE_GO) $(wildcard cmd/pso2-ice/*.go) 23 | pso2-text: $(TEXT_GO) $(wildcard cmd/pso2-text/*.go) 24 | pso2-trans: $(TRANS_CMD_GO) $(wildcard cmd/pso2-trans/*.go) 25 | pso2-trans-apply: $(TRANS_CMD_GO) $(wildcard cmd/pso2-trans-apply/*.go) 26 | pso2-afp: $(AFP_GO) $(wildcard cmd/pso2-afp/*.go) 27 | pso2-net: $(NET_GO) $(wildcard cmd/pso2-net/*.go) 28 | pso2-download: $(DOWNLOAD_CMD_GO) $(TRANS_CMD_GO) $(wildcard cmd/pso2-download/*.go) 29 | 30 | .PHONY: all clean 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pso2-go 2 | 3 | A [go](http://golang.org) library and tools for 4 | [Phantasy Star Online 2](http://pso2.jp) 5 | 6 | This project's import prefix is `aaronlindsay.com/go/pkg/pso2` 7 | 8 | ## cmd 9 | 10 | ### cmd/pso2-download 11 | 12 | [pso2-download](http://aaronlindsay.com/pso2) is an alternate launcher, patcher, and 13 | downloader for PSO2. 14 | 15 | ### cmd/pso2-net 16 | 17 | The `pso2-net` command can be used to set up servers. Example usage to set up 18 | and run a PSO2 proxy: 19 | 20 | go get aaronlindsay.com/go/pkg/pso2/cmd/pso2-net 21 | $GOPATH/bin/pso2-net -priv serverkey.pem -pub segakey.pem 22 | 23 | See [PSO2Proxy](https://github.com/cyberkitsune/PSO2Proxy) for instructions on 24 | creating and retrieving these key files. Try `-help` for a list of additional 25 | options the tool accepts. 26 | 27 | ## net 28 | 29 | A PSO2 protocol library with server, client, and proxy implementations. See 30 | `cmd/pso2-net` for usage examples. 31 | 32 | ### net/packets 33 | 34 | Contains data structures and parsing tools for handling game packets. 35 | 36 | ## License 37 | 38 | Copyright (C) 2014 Aaron Lindsay 39 | 40 | This work is free. You can redistribute it and/or modify it under the 41 | terms of the Do What The Fuck You Want To Public License, Version 2, 42 | as published by Sam Hocevar. See the COPYING file for more details. 43 | -------------------------------------------------------------------------------- /afp/archive.go: -------------------------------------------------------------------------------- 1 | package afp 2 | 3 | import ( 4 | "io" 5 | "errors" 6 | "aaronlindsay.com/go/pkg/pso2/util" 7 | "github.com/quarnster/util/encoding/binary" 8 | ) 9 | 10 | const ( 11 | HeaderMagic uint32 = 0x00706661 // little endian "afp\0" 12 | ) 13 | 14 | type Archive struct { 15 | reader io.ReadSeeker 16 | header archiveHeader 17 | entries []archiveEntry `if:"0"` 18 | } 19 | 20 | func NewArchive(reader io.ReadSeeker) (*Archive, error) { 21 | a := &Archive{ reader: reader } 22 | return a, a.parse() 23 | } 24 | 25 | type archiveHeader struct { 26 | Magic, EntryCount, Zero, Count2 uint32 27 | } 28 | 29 | type archiveEntry struct { 30 | Name string `length:"0x20"` 31 | DataSize, DataOffset, DataEnd uint32 32 | Type string `length:"4"` 33 | 34 | Alignment []uint8 `skip:"DataEnd-0x30" length:"0"` 35 | 36 | Data io.ReadSeeker `if:"0"` 37 | } 38 | 39 | func (h *archiveHeader) Validate() error { 40 | if h.Magic != HeaderMagic { 41 | return errors.New("not an AFP archive") 42 | } 43 | 44 | if h.Zero != 0 { 45 | return errors.New("header format error (zero != 0)") 46 | } 47 | 48 | if h.Count2 != 1 { 49 | return errors.New("header format error (unk != 1)") 50 | } 51 | 52 | return nil 53 | } 54 | 55 | func (a *Archive) parse() (err error) { 56 | reader := binary.BinaryReader{ Reader: a.reader, Endianess: binary.LittleEndian } 57 | 58 | if err = reader.ReadInterface(&a.header); err != nil { 59 | return 60 | } 61 | 62 | entryOffset := int64(0x10) 63 | a.entries = make([]archiveEntry, a.header.EntryCount) 64 | for i := uint32(0); i < a.header.EntryCount; i++ { 65 | entry := &a.entries[i] 66 | 67 | if err = reader.ReadInterface(entry); err != nil { 68 | return 69 | } 70 | 71 | entry.Data = io.NewSectionReader(util.ReaderAt(a.reader), entryOffset + int64(entry.DataOffset), int64(entry.DataSize)) 72 | entryOffset += int64(entry.DataEnd) 73 | } 74 | 75 | return 76 | } 77 | 78 | func (a *Archive) Write(writer io.Writer) error { 79 | return nil 80 | } 81 | 82 | type Entry struct { 83 | Type, Name string 84 | Size uint32 85 | Data io.ReadSeeker 86 | 87 | file *archiveEntry 88 | } 89 | 90 | func (a *Archive) EntryCount() int { 91 | return len(a.entries) 92 | } 93 | 94 | func (a *Archive) Entry(i int) Entry { 95 | entry := &a.entries[i] 96 | 97 | return Entry{entry.Type, entry.Name, entry.DataSize, entry.Data, entry} 98 | } 99 | -------------------------------------------------------------------------------- /afp/model.go: -------------------------------------------------------------------------------- 1 | package afp 2 | 3 | import ( 4 | "io" 5 | "fmt" 6 | "errors" 7 | "aaronlindsay.com/go/pkg/pso2/util" 8 | "github.com/quarnster/util/encoding/binary" 9 | ) 10 | 11 | const ( 12 | ModelHeaderMagic uint32 = 0x46425456 // little endian "VTBF" 13 | ) 14 | 15 | type Model struct { 16 | reader io.ReadSeeker 17 | 18 | Header ModelHeader 19 | Entries []ModelEntry 20 | } 21 | 22 | func NewModel(reader io.ReadSeeker) (*Model, error) { 23 | m := &Model{ reader: reader } 24 | return m, m.parse() 25 | } 26 | 27 | type ModelHeader struct { 28 | Magic, HeaderSize uint32 29 | Type string `length:"4"` 30 | Unk uint32 31 | } 32 | 33 | type ModelEntry struct { 34 | Type string `length:"4"` 35 | Size uint32 36 | 37 | SubType string `length:"4"` 38 | 39 | Alignment []uint8 `skip:"Size-4" length:"0"` 40 | 41 | Data io.ReadSeeker `if:"0"` 42 | } 43 | 44 | func (h *ModelHeader) Validate() error { 45 | if h.Magic != ModelHeaderMagic { 46 | return errors.New("not a VTBF file") 47 | } 48 | 49 | if h.HeaderSize != 0x10 { 50 | return errors.New("header format error (size != 0x10)") 51 | } 52 | 53 | if h.Unk != 0x4c000001 { 54 | return errors.New("header format error (unk != 0x4c000001)") 55 | } 56 | 57 | return nil 58 | } 59 | 60 | func (m *Model) parse() (err error) { 61 | reader := binary.BinaryReader{ Reader: m.reader, Endianess: binary.LittleEndian } 62 | 63 | if err = reader.ReadInterface(&m.Header); err != nil { 64 | return 65 | } 66 | 67 | reader.Seek(int64(m.Header.HeaderSize) - 0x10, 1) 68 | 69 | offset := int64(m.Header.HeaderSize) 70 | for err == nil { 71 | entry := ModelEntry{} 72 | 73 | if err = reader.ReadInterface(&entry); err != nil { 74 | if err == io.EOF { 75 | return nil 76 | } 77 | return 78 | } 79 | 80 | entry.Data = io.NewSectionReader(util.ReaderAt(m.reader), offset + 0x0c, int64(entry.Size) - 0x04) 81 | offset += 0x08 + int64(entry.Size) 82 | 83 | m.Entries = append(m.Entries, entry) 84 | 85 | switch entry.SubType { 86 | case "NODE": // Bone data 87 | 88 | case "NODO": // More bone things 89 | 90 | case "VSET": // Vertex data shit 91 | err = parseModelEntryVSET(&entry) 92 | } 93 | } 94 | 95 | return 96 | } 97 | 98 | func (m *Model) Write(writer io.Writer) error { 99 | return nil 100 | } 101 | 102 | func parseModelEntryVSET(entry *ModelEntry) error { 103 | reader := binary.BinaryReader{ Reader: entry.Data, Endianess: binary.LittleEndian } 104 | 105 | var err error 106 | 107 | unk, err := reader.Uint16() 108 | count, err := reader.Uint16() 109 | 110 | fmt.Printf("Count: %04x\n", count) 111 | fmt.Printf("Unk: %04x\n", unk) 112 | 113 | for i := uint16(0); i < count && err == nil; i++ { 114 | var identifier uint16 115 | identifier, err = reader.Uint16() 116 | var data []uint8 117 | 118 | if err != nil { 119 | break 120 | } 121 | 122 | if identifier & 0x8000 != 0 { // Size is fucked up 123 | var sz, unk uint8 124 | unk, err = reader.Uint8() 125 | 126 | var size uint16 127 | if unk == 0x10 { 128 | size, err = reader.Uint16() 129 | } else if unk == 0x08 { 130 | sz, err = reader.Uint8(); 131 | size = uint16(sz) 132 | } else { 133 | return errors.New("Unknown size flag") 134 | } 135 | 136 | fmt.Printf("Oh %02x, %04x\n", unk, size) 137 | x := 2 138 | data = make([]uint8, x * int(size + 1)) 139 | } else if identifier & 0x0900 != 0 { // and 0x0900 140 | data = make([]uint8, 4) 141 | } else if identifier & 0x0600 == 0x0600 { // and 0x0600 142 | data = make([]uint8, 2) 143 | } else if identifier & 0xff00 != 0 { 144 | fmt.Printf("what the fuck is %04x\n", identifier & 0xff00) 145 | //break 146 | } 147 | 148 | _, err = util.ReadN(entry.Data, data) 149 | 150 | fmt.Printf("\t\t%04x (%08x): %x\n", identifier, len(data), data) 151 | } 152 | 153 | if _, err = entry.Data.Read(make([]uint8, 1)); err != io.EOF { 154 | return errors.New("Expected EOF") 155 | } 156 | 157 | if err == io.EOF { 158 | return nil 159 | } 160 | 161 | return err 162 | } 163 | -------------------------------------------------------------------------------- /cmd/pso2-afp/afp.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "io" 7 | "path" 8 | "flag" 9 | "aaronlindsay.com/go/pkg/pso2/afp" 10 | ) 11 | 12 | func usage() { 13 | fmt.Fprintln(os.Stderr, "usage: afp [flags] archive.afp") 14 | flag.PrintDefaults() 15 | os.Exit(2) 16 | } 17 | 18 | func ragequit(apath string, err error) { 19 | if err != nil { 20 | if apath != "" { 21 | fmt.Fprintf(os.Stderr, "error with file `%s`\n", apath) 22 | } 23 | fmt.Fprintln(os.Stderr, err); 24 | os.Exit(1) 25 | } 26 | } 27 | 28 | func main() { 29 | var flagPrint bool 30 | var flagExtract string 31 | var flagWrite string 32 | 33 | flag.Usage = usage 34 | flag.BoolVar(&flagPrint, "p", false, "print details about the archive") 35 | flag.StringVar(&flagExtract, "x", "", "extract the archive to a folder") 36 | flag.StringVar(&flagWrite, "w", "", "write a repacked archive") 37 | flag.Parse() 38 | 39 | if flag.NArg() != 1 { 40 | fmt.Fprintln(os.Stderr, "no archive provided") 41 | flag.Usage() 42 | flag.PrintDefaults() 43 | } 44 | 45 | apath := flag.Arg(0) 46 | fmt.Fprintf(os.Stderr, "Opening archive `%s`...\n", apath) 47 | f, err := os.OpenFile(apath, os.O_RDONLY, 0); 48 | ragequit(apath, err) 49 | 50 | a, err := afp.NewArchive(f) 51 | ragequit(apath, err) 52 | 53 | if flagPrint { 54 | for i := 0; i < a.EntryCount(); i++ { 55 | file := a.Entry(i) 56 | 57 | fmt.Printf("\t%s (%s):\t0x%08x\n", file.Name, file.Type, file.Size); 58 | 59 | if file.Type == "aqo" { 60 | m, err := afp.NewModel(file.Data) 61 | ragequit(file.Name, err) 62 | 63 | for _, entry := range m.Entries { 64 | fmt.Printf("\t\t%s (%s):\t0x%08x\n", entry.Type, entry.SubType, entry.Size); 65 | } 66 | } 67 | } 68 | } 69 | 70 | if flagExtract != "" { 71 | os.MkdirAll(flagExtract, 0777); 72 | 73 | for i := 0; i < a.EntryCount(); i++ { 74 | file := a.Entry(i) 75 | fmt.Println("Extracting", file.Name, "...") 76 | 77 | f, err := os.Create(path.Join(flagExtract, file.Name)); 78 | ragequit(file.Name, err) 79 | 80 | io.Copy(f, file.Data) 81 | f.Close() 82 | } 83 | } 84 | 85 | if flagWrite != "" { 86 | ofile, err := os.Create(flagWrite) 87 | ragequit(flagWrite, err) 88 | 89 | fmt.Fprintf(os.Stderr, "Writing to archive `%s`...\n", flagWrite) 90 | a.Write(ofile) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /cmd/pso2-download/download.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "flag" 7 | "path" 8 | "time" 9 | "strings" 10 | "runtime" 11 | "io/ioutil" 12 | "aaronlindsay.com/go/pkg/pso2/trans" 13 | transcmd "aaronlindsay.com/go/pkg/pso2/trans/cmd" 14 | "aaronlindsay.com/go/pkg/pso2/download" 15 | "aaronlindsay.com/go/pkg/pso2/download/cmd" 16 | ) 17 | 18 | func wait() { 19 | if runtime.GOOS == "windows" { 20 | fmt.Println("Press enter to exit...") 21 | os.Stdin.Read([]byte{0}) 22 | } 23 | } 24 | 25 | func usage() { 26 | fmt.Fprintln(os.Stderr, "usage: pso2-download [flags] pso2/root/path") 27 | flag.PrintDefaults() 28 | os.Exit(2) 29 | } 30 | 31 | func complain(apath string, err error) bool { 32 | if err != nil { 33 | if apath != "" { 34 | fmt.Fprintf(os.Stderr, "error with file `%s`\n", apath) 35 | } 36 | fmt.Fprintln(os.Stderr, err) 37 | return true 38 | } 39 | 40 | return false 41 | } 42 | 43 | func ragequit(apath string, err error) { 44 | if complain(apath, err) { 45 | wait() 46 | os.Exit(1) 47 | } 48 | } 49 | 50 | func main() { 51 | var flagPrint, flagAll, flagCheck, flagHash, flagDownload, flagUpdate, flagGarbage, flagLaunch, flagItemTranslation, flagBackup bool 52 | var flagTranslate, flagPublicKey, flagDumpPublicKey string 53 | var flagParallel int 54 | 55 | flag.Usage = usage 56 | flag.BoolVar(&flagAll, "a", false, "ignore patchlist, check all files") 57 | flag.BoolVar(&flagCheck, "c", false, "check files") 58 | flag.BoolVar(&flagHash, "h", false, "check file hashes") 59 | flag.BoolVar(&flagDownload, "d", false, "download files") 60 | flag.IntVar(&flagParallel, "p", 3, "max parallel downloads") 61 | flag.BoolVar(&flagPrint, "v", false, "print verbose information") 62 | flag.BoolVar(&flagUpdate, "u", false, "refresh patchlist") 63 | flag.BoolVar(&flagGarbage, "g", false, "clean up old/unused files") 64 | flag.BoolVar(&flagBackup, "b", false, "back up any modified files") 65 | flag.BoolVar(&flagItemTranslation, "i", false, "enable the item translation") 66 | flag.BoolVar(&flagLaunch, "l", false, "launch the game") 67 | flag.StringVar(&flagTranslate, "t", "", "use the translation with the specified comma-separated names (example: eng,story-eng)") 68 | flag.StringVar(&flagPublicKey, "pubkey", "", "inject a public key (path relative to pso2_bin)") 69 | flag.StringVar(&flagDumpPublicKey, "dumppubkey", "", "dump the PSO2 public key (path relative to pso2_bin)") 70 | flag.Parse() 71 | 72 | maxprocs := runtime.GOMAXPROCS(0) 73 | if maxprocs < 0x10 { 74 | runtime.GOMAXPROCS(0x10) 75 | } 76 | 77 | if flagHash { 78 | flagCheck = true 79 | } 80 | 81 | if flag.NArg() != 1 { 82 | fmt.Fprintln(os.Stderr, "no pso2 path provided") 83 | flag.Usage() 84 | flag.PrintDefaults() 85 | } 86 | 87 | pso2path := flag.Arg(0) 88 | 89 | scratch := cmd.PathScratch(pso2path) 90 | err := os.Mkdir(scratch, 0777) 91 | if !os.IsExist(err) { 92 | ragequit(scratch, err) 93 | } 94 | 95 | flagTranslations := strings.Split(flagTranslate, ",") 96 | if flagTranslate == "" { 97 | flagTranslations = nil 98 | } 99 | 100 | fmt.Fprintln(os.Stderr, "Checking for updates...") 101 | netVersion, err := cmd.DownloadProductionVersion() 102 | complain(download.ProductionVersion, err) 103 | 104 | version, _ := cmd.LoadVersionFile(path.Join(scratch, cmd.PathVersion)) 105 | 106 | needsTranslation, err := cmd.DownloadEnglishFiles(pso2path) 107 | complain("", err) 108 | 109 | installedVersion, _ := cmd.LoadVersionFile(path.Join(scratch, cmd.PathVersionInstalled)) 110 | if installedVersion != "" { 111 | fmt.Fprintln(os.Stderr, "Current version:", installedVersion) 112 | } 113 | 114 | if netVersion != "" && version != netVersion { 115 | fmt.Fprintln(os.Stderr, "Update", netVersion, "found") 116 | flagUpdate = true 117 | } 118 | 119 | fmt.Fprintln(os.Stderr, "Loading patchlist...") 120 | patchlist, err := cmd.LoadPatchlist(pso2path) 121 | 122 | var installedPatchlist *download.PatchList 123 | if !flagAll { 124 | installedPatchlist, _ = cmd.LoadPatchlistFile(path.Join(scratch, cmd.PathPatchlistInstalled), "") 125 | } else { 126 | fmt.Fprintln(os.Stderr, "Ignoring patchlist, checking all files...") 127 | needsTranslation = true 128 | } 129 | 130 | if flagUpdate || patchlist == nil { 131 | fmt.Fprintln(os.Stderr, "Downloading patchlist.txt...") 132 | patchlist, err = cmd.DownloadPatchlist(pso2path, netVersion) 133 | ragequit(download.ProductionPatchlist, err) 134 | } 135 | 136 | patchdiff := patchlist.Diff(installedPatchlist) 137 | 138 | if installedPatchlist == nil { 139 | flagCheck = true 140 | } 141 | 142 | fmt.Fprintln(os.Stderr, len(patchdiff.Entries), "file(s) changed since last update") 143 | 144 | if flagPrint { 145 | for _, e := range patchdiff.Entries { 146 | fmt.Fprintf(os.Stderr, "\t%s (0x%08x): %x\n", download.RemoveExtension(e.Path), e.Size, e.MD5) 147 | } 148 | } 149 | 150 | var changes []*download.PatchEntry 151 | if flagCheck && len(patchdiff.Entries) > 0 { 152 | fmt.Fprintln(os.Stderr, "Checking files...") 153 | changes, err = cmd.CheckFiles(pso2path, flagHash, patchdiff) 154 | ragequit("", err) 155 | 156 | if len(changes) == 0 { 157 | cmd.CommitInstalled(pso2path, patchlist) 158 | } 159 | } else { 160 | for i := range patchdiff.Entries { 161 | changes = append(changes, &patchdiff.Entries[i]) 162 | } 163 | } 164 | 165 | changesSize := int64(0) 166 | for _, e := range changes { 167 | changesSize += e.Size 168 | } 169 | 170 | fmt.Fprintf(os.Stderr, "%d file(s) (%0.2f MB) need updating\n", len(changes), float32(changesSize) / 1024 / 1024) 171 | 172 | if flagPrint { 173 | for _, e := range changes { 174 | fmt.Fprintf(os.Stderr, "\t%s (0x%08x): %x\n", download.RemoveExtension(e.Path), e.Size, e.MD5) 175 | } 176 | } 177 | 178 | if flagDownload && len(changes) > 0 { 179 | errs := cmd.DownloadChanges(pso2path, changes, flagParallel) 180 | 181 | if len(errs) > 0 { 182 | for _, err := range errs { 183 | complain("", err) 184 | } 185 | fmt.Fprintln(os.Stderr, "Update unsuccessful, errors encountered") 186 | } else { 187 | fmt.Fprintln(os.Stderr, "Update complete!") 188 | cmd.CommitInstalled(pso2path, patchlist) 189 | needsTranslation = true 190 | } 191 | } 192 | 193 | if flagGarbage { 194 | fmt.Fprintln(os.Stderr, "Deleting old, unused files...") 195 | 196 | garbageSize, err := cmd.PruneFiles(pso2path, patchlist) 197 | complain("", err) 198 | 199 | fmt.Fprintf(os.Stderr, "Done! Saved %0.2f MB of space.\n", float32(garbageSize) / 1024 / 1024) 200 | } 201 | 202 | if needsTranslation && len(flagTranslations) > 0 { 203 | fmt.Fprintln(os.Stderr, "Applying english patches...") 204 | db, err := trans.NewDatabase(path.Join(scratch, cmd.PathEnglishDb)) 205 | backupPath := "" 206 | if flagBackup { 207 | backupPath = path.Join(scratch, "backup") 208 | err = os.MkdirAll(backupPath, 0777) 209 | if complain(backupPath, err) { 210 | backupPath = "" 211 | } 212 | } 213 | if !complain("", err) { 214 | var errs []error 215 | for _, translation := range flagTranslations { 216 | win32 := path.Join(pso2path, "data/win32") 217 | errs = append(errs, transcmd.PatchFiles(db, win32, translation, backupPath, win32, runtime.NumCPU() + 1)...) 218 | } 219 | 220 | for _, err := range errs { 221 | complain("", err) 222 | } 223 | } 224 | db.Close() 225 | } 226 | 227 | if flagLaunch { 228 | config, _ := cmd.LoadTranslationConfig(pso2path) 229 | configChanged := false 230 | 231 | if config == nil { 232 | config = make(cmd.TranslationConfig) 233 | } 234 | 235 | setConfig := func(key, value string) { 236 | if config[key] != value { 237 | config[key] = value 238 | configChanged = true 239 | } 240 | } 241 | 242 | if flagItemTranslation { 243 | setConfig(cmd.ConfigTranslationPath, path.Join("download", cmd.PathTranslationBin)) 244 | } else { 245 | setConfig(cmd.ConfigTranslationPath, "") 246 | } 247 | 248 | if flagDumpPublicKey != "" { 249 | setConfig(cmd.ConfigPublicKeyPath, flagDumpPublicKey) 250 | setConfig(cmd.ConfigPublicKeyDump, "1") 251 | } else { 252 | setConfig(cmd.ConfigPublicKeyPath, flagPublicKey) 253 | setConfig(cmd.ConfigPublicKeyDump, "") 254 | } 255 | 256 | if configChanged { 257 | cmd.SaveTranslationConfig(pso2path, config) 258 | } 259 | 260 | fmt.Fprintln(os.Stderr, "Launching PSO2...") 261 | 262 | command, err := cmd.LaunchGame(pso2path) 263 | ragequit("", err) 264 | 265 | loadDll := flagItemTranslation || flagPublicKey != "" || flagDumpPublicKey != "" 266 | ddraw := path.Join(pso2path, "ddraw.dll") 267 | 268 | if loadDll { 269 | err = ioutil.WriteFile(ddraw, cmd.DdrawDll[:], 0777) 270 | complain(ddraw, err) 271 | } 272 | 273 | err = command.Wait() 274 | 275 | if loadDll { 276 | time.Sleep(time.Second * 15) 277 | err = os.Remove(ddraw) 278 | complain(ddraw, err) 279 | err = nil 280 | } 281 | 282 | ragequit("", err) 283 | } else { 284 | wait() 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /cmd/pso2-ice/ice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "io" 7 | "flag" 8 | "bufio" 9 | "strings" 10 | "errors" 11 | "path" 12 | "aaronlindsay.com/go/pkg/pso2/ice" 13 | "aaronlindsay.com/go/pkg/pso2/util" 14 | ) 15 | 16 | func usage() { 17 | fmt.Fprintln(os.Stderr, "usage: pso2-ice [flags] archive.ice") 18 | flag.PrintDefaults() 19 | os.Exit(2) 20 | } 21 | 22 | func ragequit(apath string, err error) { 23 | if err != nil { 24 | if apath != "" { 25 | fmt.Fprintf(os.Stderr, "error with file `%s`\n", apath) 26 | } 27 | fmt.Fprintln(os.Stderr, err); 28 | os.Exit(1) 29 | } 30 | } 31 | 32 | type flagReplaceType map[string]string 33 | 34 | func (f flagReplaceType) String() (value string) { 35 | value = `"` 36 | first := true 37 | for i, s := range f { 38 | if !first { 39 | value += "," 40 | } 41 | first = false 42 | value += i + ":" + s 43 | } 44 | value += `"` 45 | return 46 | } 47 | 48 | func (f *flagReplaceType) Set(value string) error { 49 | *f = make(map[string]string) 50 | 51 | values := strings.Split(value, ",") 52 | 53 | for _, v := range values { 54 | value := strings.Split(v, ":") 55 | 56 | if len(value) != 2 { 57 | return errors.New("invalid replacement format") 58 | } 59 | 60 | (*f)[value[0]] = value[1] 61 | } 62 | 63 | return nil 64 | } 65 | 66 | func main() { 67 | var flagPrint bool 68 | var flagExtract string 69 | var flagWrite string 70 | var flagReplace flagReplaceType 71 | 72 | flag.Usage = usage 73 | flag.BoolVar(&flagPrint, "p", false, "print details about the archive") 74 | flag.StringVar(&flagExtract, "x", "", "extract the archive to a folder") 75 | flag.StringVar(&flagWrite, "w", "", "write a repacked archive") 76 | flag.Var(&flagReplace, "r", `replace a file while repacking, use with -w (comma-separated, entry format is "filename:path". an empty path deletes the file from the archive)`) 77 | flag.Parse() 78 | 79 | if flag.NArg() != 1 { 80 | fmt.Fprintln(os.Stderr, "no archive provided") 81 | flag.Usage() 82 | flag.PrintDefaults() 83 | } 84 | 85 | apath := flag.Arg(0) 86 | fmt.Fprintf(os.Stderr, "Opening archive `%s`...\n", apath) 87 | f, err := os.OpenFile(apath, os.O_RDONLY, 0); 88 | ragequit(apath, err) 89 | 90 | a, err := ice.NewArchive(util.BufReader(f)) 91 | ragequit(apath, err) 92 | 93 | if flagPrint { 94 | for i := 0; i < a.GroupCount(); i++ { 95 | group := a.Group(i) 96 | 97 | fmt.Printf("Archive Group %d (0x%04x files)\n", i, len(group.Files)) 98 | for _, file := range group.Files { 99 | fmt.Printf("\t%s (%s):\t0x%08x\n", file.Name, file.Type, file.Size); 100 | } 101 | } 102 | } 103 | 104 | if flagExtract != "" { 105 | for i := 0; i < a.GroupCount(); i++ { 106 | extPath := path.Join(flagExtract, fmt.Sprintf("%d", i)) 107 | os.MkdirAll(extPath, 0777); 108 | 109 | group := a.Group(i) 110 | 111 | for _, file := range group.Files { 112 | fmt.Println("Extracting", file.Name, "...") 113 | 114 | f, err := os.Create(path.Join(extPath, file.Name)); 115 | ragequit(file.Name, err) 116 | 117 | io.Copy(f, file.Data) 118 | f.Close() 119 | } 120 | } 121 | } 122 | 123 | if flagWrite != "" { 124 | ofile, err := os.Create(flagWrite) 125 | ragequit(flagWrite, err) 126 | 127 | for i := 0; i < a.GroupCount(); i++ { 128 | group := a.Group(i) 129 | 130 | for _, file := range group.Files { 131 | if newpath, ok := flagReplace[file.Name]; ok { 132 | if newpath == "" { 133 | a.ReplaceFile(&file, nil, 0) 134 | } else { 135 | newfile, err := os.Open(newpath) 136 | ragequit(newpath, err) 137 | 138 | st, err := newfile.Stat() 139 | ragequit(newpath, err) 140 | 141 | if st.Size() > int64(^uint32(0)) { 142 | ragequit(newpath, errors.New("file too large")) 143 | } 144 | 145 | a.ReplaceFile(&file, newfile, uint32(st.Size())) 146 | } 147 | } 148 | } 149 | } 150 | 151 | fmt.Fprintf(os.Stderr, "Writing to archive `%s`...\n", flagWrite) 152 | writer := bufio.NewWriter(ofile) 153 | a.Write(writer) 154 | writer.Flush() 155 | ofile.Close() 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /cmd/pso2-net/net.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "net" 6 | "fmt" 7 | "flag" 8 | "io/ioutil" 9 | "crypto/rsa" 10 | pso2net "aaronlindsay.com/go/pkg/pso2/net" 11 | "aaronlindsay.com/go/pkg/pso2/net/packets" 12 | "aaronlindsay.com/go/pkg/pso2/util" 13 | "github.com/juju/loggo" 14 | ) 15 | 16 | var Logger loggo.Logger = loggo.GetLogger("pso2.cmd.pso2-net") 17 | 18 | func usage() { 19 | fmt.Fprintln(os.Stderr, "usage: pso2-net [flags]") 20 | flag.PrintDefaults() 21 | os.Exit(2) 22 | } 23 | 24 | func ragequit(apath string, err error) { 25 | if err != nil { 26 | if apath != "" { 27 | Logger.Errorf("error with file %s", apath) 28 | } 29 | Logger.Errorf("%s", err) 30 | os.Exit(1) 31 | } 32 | } 33 | 34 | func findaddr() (addr string) { 35 | addr = "127.0.0.1" 36 | 37 | as, err := net.InterfaceAddrs() 38 | if err != nil { 39 | return 40 | } 41 | 42 | for _, a := range as { 43 | if ip, ok := a.(*net.IPNet); ok { 44 | ip := ip.IP.To4() 45 | if ip != nil && !ip.IsLoopback() && !ip.IsMulticast() { 46 | return ip.String() 47 | } 48 | } 49 | } 50 | 51 | return 52 | } 53 | 54 | type EndpointMap map[uint16]*pso2net.Proxy 55 | func (e EndpointMap) EndpointAnnouncement(ip net.IP, port uint16) { 56 | if m, ok := e[port]; ok { 57 | m.ClientEndpoint = fmt.Sprintf("%s:%d", ip, port) 58 | } else { 59 | Logger.Warningf("Unknown endpoint announcement for %s:%d", ip, port) 60 | } 61 | } 62 | 63 | func main() { 64 | var flagPrivateKey, flagPublicKey, flagIP, flagBind, flagProxy, flagLog, flagDump, flagReplay string 65 | var keyPrivate *rsa.PrivateKey 66 | var keyPublic *rsa.PublicKey 67 | 68 | flag.Usage = usage 69 | flag.StringVar(&flagPrivateKey, "priv", "", "server private key") 70 | flag.StringVar(&flagPublicKey, "pub", "", "client public key") 71 | flag.StringVar(&flagLog, "log", "info", "log level (trace, debug, info, warning, error, critical)") 72 | flag.StringVar(&flagProxy, "proxy", "", "proxy server to connect to instead of PSO2") 73 | flag.StringVar(&flagBind, "bind", "", "interface address to bind on") 74 | flag.StringVar(&flagIP, "addr", findaddr(), "external IPv4 address") 75 | flag.StringVar(&flagDump, "dump", "", "dump packets to folder") 76 | flag.StringVar(&flagReplay, "replay", "", "replay packets from a dump") 77 | flag.Parse() 78 | 79 | ip := net.IPv4(127, 0, 0, 1) 80 | if flagIP != "" { 81 | ip = net.ParseIP(flagIP) 82 | } 83 | 84 | if flagLog != "" { 85 | lvl, ok := loggo.ParseLevel(flagLog) 86 | if ok { 87 | Logger.SetLogLevel(lvl) 88 | } else { 89 | Logger.Warningf("Invalid log level %s specified", flagLog) 90 | } 91 | } 92 | pso2net.Logger.SetLogLevel(Logger.LogLevel()) 93 | 94 | if flagPrivateKey != "" { 95 | Logger.Infof("Loading private key") 96 | f, err := os.Open(flagPrivateKey) 97 | ragequit(flagPrivateKey, err) 98 | 99 | keyPrivate, err = pso2net.LoadPrivateKey(f) 100 | f.Close() 101 | 102 | ragequit(flagPrivateKey, err) 103 | } 104 | 105 | if flagPublicKey != "" { 106 | Logger.Infof("Loading public key") 107 | f, err := os.Open(flagPublicKey) 108 | ragequit(flagPublicKey, err) 109 | 110 | keyPublic, err = pso2net.LoadPublicKey(f) 111 | f.Close() 112 | ragequit(flagPublicKey, err) 113 | } 114 | 115 | if flagReplay != "" { 116 | Logger.Infof("Replaying packets from %s", flagReplay) 117 | 118 | f, err := os.Open(flagReplay) 119 | ragequit(flagReplay, err) 120 | 121 | c := pso2net.NewConnection(util.ReadWriter(f, ioutil.Discard)) 122 | var r pso2net.PacketRoute 123 | err = c.RoutePackets(&r) 124 | ragequit(flagReplay, err) 125 | } else { 126 | Logger.Infof("Starting proxy servers on %s", ip) 127 | 128 | fallbackRoute := func(p *pso2net.Proxy) *pso2net.PacketRoute { 129 | r := &pso2net.PacketRoute{} 130 | r.RouteMask(0xffff, pso2net.RoutePriorityLow, pso2net.ProxyHandlerFallback(p)) 131 | if flagDump != "" { 132 | r.RouteMask(0xffff, pso2net.RoutePriorityHigh, pso2net.HandlerIgnore(pso2net.HandlerDump(flagDump))) 133 | } 134 | return r 135 | } 136 | 137 | newProxy := func(host string, port uint16) *pso2net.Proxy { 138 | return pso2net.NewProxy(fmt.Sprintf("%s:%d", flagBind, port), fmt.Sprintf("%s:%d", host, port)) 139 | } 140 | 141 | startProxy := func(p *pso2net.Proxy, s *pso2net.PacketRoute, c *pso2net.PacketRoute) { 142 | l, err := p.Listen() 143 | ragequit(p.String(), err) 144 | 145 | go p.Start(l, s, c) 146 | } 147 | 148 | hostname := func(ship int) string { 149 | if flagProxy != "" { 150 | return flagProxy 151 | } 152 | 153 | return packets.ShipHostnames[ship] 154 | } 155 | 156 | endpoints := make(EndpointMap) 157 | 158 | for i := 0; i < packets.ShipCount; i++ { 159 | blockPort := uint16(12000 + (100 * i)) 160 | shipPort := uint16(blockPort + 99) 161 | 162 | // Set up ship proxy, rewrites IPs 163 | proxy := newProxy(hostname(i), shipPort) 164 | route := &pso2net.PacketRoute{} 165 | route.Route(packets.TypeShip, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerShip(proxy, endpoints, ip)) 166 | route.RouteMask(0xffff, pso2net.RoutePriorityLow, pso2net.ProxyHandlerFallback(proxy)) 167 | if flagDump != "" { 168 | route.RouteMask(0xffff, pso2net.RoutePriorityHigh, pso2net.HandlerIgnore(pso2net.HandlerDump(flagDump))) 169 | } 170 | endpoints[shipPort] = proxy 171 | startProxy(proxy, fallbackRoute(proxy), route) 172 | 173 | // Set up block proxy, rewrites IPs 174 | proxy = newProxy(hostname(i), blockPort) 175 | route = &pso2net.PacketRoute{} 176 | route.Route(packets.TypeBlock, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerBlock(proxy, endpoints, ip)) 177 | route.RouteMask(0xffff, pso2net.RoutePriorityLow, pso2net.ProxyHandlerFallback(proxy)) 178 | if flagDump != "" { 179 | route.RouteMask(0xffff, pso2net.RoutePriorityHigh, pso2net.HandlerIgnore(pso2net.HandlerDump(flagDump))) 180 | } 181 | endpoints[blockPort] = proxy 182 | startProxy(proxy, fallbackRoute(proxy), route) 183 | 184 | for b := uint16(1); b < 99; b++ { 185 | port := blockPort + b 186 | proxy = newProxy(hostname(i), port) 187 | 188 | // Set up client route (messages from the PSO2 server) 189 | route = &pso2net.PacketRoute{} 190 | route.Route(packets.TypeRoom, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerRoom(proxy, endpoints, ip)) 191 | route.Route(packets.TypeRoomTeam, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerRoom(proxy, endpoints, ip)) 192 | route.Route(packets.TypeBlockResponse, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerBlockResponse(proxy, endpoints, ip)) 193 | route.Route(packets.TypeBlocks, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerBlocks(proxy, endpoints, ip)) 194 | route.Route(packets.TypeBlocks2, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerBlocks(proxy, endpoints, ip)) 195 | route.RouteMask(0xffff, pso2net.RoutePriorityLow, pso2net.ProxyHandlerFallback(proxy)) 196 | if flagDump != "" { 197 | route.RouteMask(0xffff, pso2net.RoutePriorityHigh, pso2net.HandlerIgnore(pso2net.HandlerDump(flagDump))) 198 | } 199 | 200 | // Set up server route (messages from the client) 201 | sroute := &pso2net.PacketRoute{} 202 | sroute.Route(packets.TypeCipher, pso2net.RoutePriorityHigh, pso2net.HandlerIgnore(pso2net.HandlerCipher(keyPrivate))) 203 | sroute.Route(packets.TypeCipher, pso2net.RoutePriorityNormal, pso2net.ProxyHandlerCipher(proxy, keyPrivate, keyPublic)) 204 | sroute.RouteMask(0xffff, pso2net.RoutePriorityLow, pso2net.ProxyHandlerFallback(proxy)) 205 | if flagDump != "" { 206 | sroute.RouteMask(0xffff, pso2net.RoutePriorityHigh, pso2net.HandlerIgnore(pso2net.HandlerDump(flagDump))) 207 | } 208 | 209 | endpoints[port] = proxy 210 | startProxy(proxy, sroute, route) 211 | } 212 | } 213 | } 214 | 215 | // Stop foreverz 216 | <-make(chan int) 217 | } 218 | -------------------------------------------------------------------------------- /cmd/pso2-text/text.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "flag" 7 | "aaronlindsay.com/go/pkg/pso2/text" 8 | ) 9 | 10 | func usage() { 11 | fmt.Fprintln(os.Stderr, "usage: text.go [flags] file.text") 12 | flag.PrintDefaults() 13 | os.Exit(2) 14 | } 15 | 16 | func ragequit(apath string, err error) { 17 | if err != nil { 18 | if apath != "" { 19 | fmt.Fprintf(os.Stderr, "error with file `%s`\n", apath) 20 | } 21 | fmt.Fprintln(os.Stderr, err); 22 | os.Exit(1) 23 | } 24 | } 25 | 26 | func main() { 27 | var flagPrint bool 28 | var flagWrite string 29 | 30 | flag.Usage = usage 31 | flag.BoolVar(&flagPrint, "p", false, "print details about the file") 32 | flag.StringVar(&flagWrite, "w", "", "write a repacked file") 33 | flag.Parse() 34 | 35 | if flag.NArg() != 1 { 36 | fmt.Fprintln(os.Stderr, "no filename provided") 37 | flag.Usage() 38 | flag.PrintDefaults() 39 | } 40 | 41 | tpath := flag.Arg(0) 42 | fmt.Fprintf(os.Stderr, "Opening file `%s`...\n", tpath) 43 | f, err := os.OpenFile(tpath, os.O_RDONLY, 0); 44 | ragequit(tpath, err) 45 | 46 | t, err := text.NewTextFile(f) 47 | ragequit(tpath, err) 48 | 49 | if flagPrint { 50 | for i, entry := range t.Entries { 51 | fmt.Printf("%08x: %s\n", entry.Value, entry.Text) 52 | 53 | if entry.TextStatus == text.TextEntryString { 54 | t.Entries[i].Text = "LOLOLOL" 55 | } 56 | } 57 | 58 | for _, p := range t.Pairs { 59 | fmt.Printf("%s: %s\n", p.Identifier, p.String) 60 | } 61 | } 62 | 63 | if flagWrite != "" { 64 | ofile, err := os.Create(flagWrite) 65 | ragequit(flagWrite, err) 66 | 67 | fmt.Fprintf(os.Stderr, "Writing to `%s`...\n", flagWrite) 68 | t.Write(ofile) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cmd/pso2-trans-apply/trans.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "flag" 7 | "runtime" 8 | "aaronlindsay.com/go/pkg/pso2/trans" 9 | "aaronlindsay.com/go/pkg/pso2/trans/cmd" 10 | ) 11 | 12 | func usage() { 13 | os.Exit(2) 14 | } 15 | 16 | func complain(apath string, err error) bool { 17 | if err != nil { 18 | if apath != "" { 19 | fmt.Fprintf(os.Stderr, "error with file `%s`\n", apath) 20 | } 21 | fmt.Fprintln(os.Stderr, err); 22 | return true 23 | } 24 | 25 | return false 26 | } 27 | 28 | func ragequit(apath string, err error) { 29 | if complain(apath, err) { 30 | os.Exit(1) 31 | } 32 | } 33 | 34 | func main() { 35 | defer func() { 36 | if r := recover(); r != nil { 37 | fmt.Fprintln(os.Stderr, r) 38 | } 39 | }() 40 | 41 | var flagTrans, flagBackup, flagOutput string 42 | var flagParallel int 43 | 44 | flag.Usage = usage 45 | flag.IntVar(&flagParallel, "p", runtime.NumCPU() + 1, "max parallel tasks") 46 | flag.StringVar(&flagTrans, "t", "", "translation name") 47 | flag.StringVar(&flagBackup, "b", "", "backup files to this path before modifying them") 48 | flag.StringVar(&flagOutput, "o", "", "alternate output directory") 49 | flag.Parse() 50 | 51 | if flag.NArg() < 1 { 52 | fmt.Fprintln(os.Stderr, "no database provided") 53 | flag.Usage() 54 | flag.PrintDefaults() 55 | } 56 | 57 | maxprocs := runtime.GOMAXPROCS(0) 58 | if maxprocs < flagParallel { 59 | runtime.GOMAXPROCS(flagParallel) 60 | } 61 | 62 | dbpath := flag.Arg(0) 63 | db, err := trans.NewDatabase(dbpath) 64 | ragequit(dbpath, err) 65 | 66 | if flagTrans != "" { 67 | if flag.NArg() < 2 { 68 | fmt.Fprintln(os.Stderr, "no pso2 dir provided") 69 | return 70 | } 71 | 72 | if flagBackup != "" { 73 | err := os.MkdirAll(flagBackup, 0777) 74 | ragequit(flagBackup, err) 75 | } 76 | 77 | pso2dir := flag.Arg(1) 78 | if flagOutput == "" { 79 | flagOutput = pso2dir 80 | } else { 81 | err := os.MkdirAll(flagOutput, 0777) 82 | ragequit(flagOutput, err) 83 | } 84 | 85 | errs := cmd.PatchFiles(db, pso2dir, flagTrans, flagBackup, flagOutput, flagParallel) 86 | 87 | for _, err := range errs { 88 | complain("", err) 89 | } 90 | } else { 91 | fmt.Fprintln(os.Stderr, "no translation name provided") 92 | } 93 | 94 | db.Close() 95 | } 96 | -------------------------------------------------------------------------------- /cmd/pso2-trans/trans.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "fmt" 7 | "flag" 8 | "path" 9 | "errors" 10 | "strings" 11 | "runtime" 12 | "encoding/csv" 13 | "aaronlindsay.com/go/pkg/pso2/ice" 14 | "aaronlindsay.com/go/pkg/pso2/text" 15 | "aaronlindsay.com/go/pkg/pso2/util" 16 | "aaronlindsay.com/go/pkg/pso2/trans" 17 | "aaronlindsay.com/go/pkg/pso2/trans/cmd" 18 | ) 19 | 20 | func usage() { 21 | fmt.Fprintln(os.Stderr, "usage: pso2-trans [flags] pso2.db [...]") 22 | flag.PrintDefaults() 23 | os.Exit(2) 24 | } 25 | 26 | func complain(apath string, err error) bool { 27 | if err != nil { 28 | if apath != "" { 29 | fmt.Fprintf(os.Stderr, "error with file `%s`\n", apath) 30 | } 31 | fmt.Fprintln(os.Stderr, err); 32 | return true 33 | } 34 | 35 | return false 36 | } 37 | 38 | func ragequit(apath string, err error) { 39 | if complain(apath, err) { 40 | os.Exit(1) 41 | } 42 | } 43 | 44 | func main() { 45 | var flagTrans, flagBackup, flagOutput, flagStrip string 46 | var flagAidaSkits, flagAidaStrings string 47 | var flagImport, flagParallel int 48 | 49 | flag.Usage = usage 50 | flag.IntVar(&flagImport, "i", 0, "import files with the specified version") 51 | flag.IntVar(&flagParallel, "p", runtime.NumCPU() + 1, "max parallel tasks") 52 | flag.StringVar(&flagTrans, "t", "", "translation name (eng, story-eng, etc.)") 53 | flag.StringVar(&flagBackup, "b", "", "backup files to this path before modifying them") 54 | flag.StringVar(&flagStrip, "s", "", "write out a stripped database") 55 | flag.StringVar(&flagOutput, "o", "", "alternate output directory for repacked files") 56 | flag.StringVar(&flagAidaSkits, "aidaskits", "", "skit list file") 57 | flag.StringVar(&flagAidaStrings, "aidastrings", "", "translation csv file") 58 | flag.Parse() 59 | 60 | if flag.NArg() < 1 { 61 | fmt.Fprintln(os.Stderr, "no database provided") 62 | flag.Usage() 63 | flag.PrintDefaults() 64 | } 65 | 66 | maxprocs := runtime.GOMAXPROCS(0) 67 | if maxprocs < flagParallel { 68 | runtime.GOMAXPROCS(flagParallel) 69 | } 70 | 71 | dbpath := flag.Arg(0) 72 | fmt.Fprintf(os.Stderr, "Opening database `%s`...\n", dbpath) 73 | db, err := trans.NewDatabase(dbpath) 74 | ragequit(dbpath, err) 75 | 76 | if flagImport != 0 { 77 | if flagAidaSkits != "" || flagAidaStrings != "" { 78 | if flagAidaSkits == "" || flagAidaStrings == "" || flagTrans == "" { 79 | ragequit("", errors.New("-aidaskits, -aidsstrings, and -t must all be specified together")) 80 | } 81 | 82 | fmt.Fprintln(os.Stderr, "Importing from AIDA files...") 83 | archiveMap := make(map[string]trans.ArchiveName) 84 | 85 | sf, err := os.Open(flagAidaSkits) 86 | ragequit(flagAidaSkits, err) 87 | 88 | for err != io.EOF { 89 | var scanArchive, scanHdr, scanGroup, scanName string 90 | var n int 91 | n, err = fmt.Fscanln(sf, &scanArchive, &scanHdr, &scanGroup, &scanName) 92 | 93 | if err != nil || n != 4 { 94 | continue 95 | } 96 | 97 | aname, err := trans.ArchiveNameFromString(scanArchive) 98 | if complain(scanArchive, err) { 99 | continue 100 | } 101 | 102 | archiveMap[scanName] = *aname 103 | } 104 | 105 | sf.Close() 106 | 107 | f, err := os.Open(flagAidaStrings) 108 | ragequit(flagAidaStrings, err) 109 | 110 | r := csv.NewReader(f) 111 | r.TrimLeadingSpace = true 112 | r.FieldsPerRecord = 5 113 | 114 | t, err := db.QueryTranslation(flagTrans) 115 | if t == nil { 116 | t, err = db.InsertTranslation(flagTrans) 117 | ragequit(flagTrans, err) 118 | } 119 | 120 | db.Begin() 121 | collisions := make(map[string]map[string]int) 122 | for { 123 | var line []string // {filename, type, zeroUnk, identifier, string} 124 | line, err = r.Read() 125 | 126 | if err != nil { 127 | break 128 | } 129 | 130 | line[0] = strings.Replace(line[0], "\\", "/", -1) 131 | archive := path.Dir(line[0]) 132 | 133 | aname, ok := archiveMap[archive] 134 | if !ok { 135 | ragequit(archive, errors.New("unknown archive name")) 136 | } 137 | 138 | fname := path.Base(line[0]) 139 | 140 | translation := line[4] 141 | identifier := line[3] 142 | 143 | c := collisions[line[0]] 144 | if c == nil { 145 | collisions[line[0]] = make(map[string]int) 146 | c = collisions[line[0]] 147 | } 148 | 149 | collision := c[identifier] 150 | c[identifier] = collision + 1 151 | 152 | a, err := db.QueryArchive(&aname) 153 | if complain(archive, err) { 154 | continue 155 | } 156 | 157 | if a == nil && complain(archive, errors.New("archive not found in database")) { 158 | continue 159 | } 160 | 161 | f, err := db.QueryFile(a, fname) 162 | if complain(fname, err) { 163 | continue 164 | } 165 | 166 | if f == nil && complain(archive + ": " + fname, errors.New("file not found in database")) { 167 | continue 168 | } 169 | 170 | s, err := db.QueryString(f, collision, identifier) 171 | if complain(fname + ": " + identifier, err) { 172 | continue 173 | } 174 | 175 | if s == nil { 176 | complain(fname + ": " + identifier, errors.New("string not found in database")) 177 | continue 178 | } 179 | 180 | if s.Value != translation { 181 | ts, _ := db.QueryTranslationString(t, s) 182 | if ts != nil { 183 | _, err = db.UpdateTranslationString(ts, translation) 184 | } else { 185 | _, err = db.InsertTranslationString(t, s, translation) 186 | } 187 | } 188 | } 189 | 190 | if err != io.EOF { 191 | ragequit(flagAidaStrings, err) 192 | } 193 | 194 | f.Close() 195 | 196 | db.End() 197 | 198 | fmt.Fprintln(os.Stderr, "Import complete!") 199 | } else { 200 | for i := 1; i < flag.NArg(); i++ { 201 | name := flag.Arg(i) 202 | aname, err := trans.ArchiveNameFromString(path.Base(name)) 203 | if complain(name, err) { 204 | continue 205 | } 206 | 207 | fmt.Fprintf(os.Stderr, "Opening archive `%s`...\n", name) 208 | af, err := os.OpenFile(name, os.O_RDONLY, 0); 209 | if complain(name, err) { 210 | continue 211 | } 212 | 213 | archive, err := ice.NewArchive(util.BufReader(af)) 214 | if complain(name, err) { 215 | af.Close() 216 | continue 217 | } 218 | 219 | var a *trans.Archive 220 | var translation *trans.Translation 221 | 222 | for i := 0; i < archive.GroupCount(); i++ { 223 | group := archive.Group(i) 224 | 225 | for _, file := range group.Files { 226 | if file.Type == "text" { 227 | fmt.Fprintf(os.Stderr, "Importing file `%s`...\n", file.Name) 228 | 229 | t, err := text.NewTextFile(file.Data) 230 | if complain(file.Name, err) { 231 | continue 232 | } 233 | 234 | if a == nil { 235 | a, err = db.QueryArchive(aname) 236 | if complain(name, err) { 237 | continue 238 | } 239 | 240 | if a == nil { 241 | a, err = db.InsertArchive(aname) 242 | if complain(name, err) { 243 | continue 244 | } 245 | } 246 | } 247 | 248 | f, err := db.QueryFile(a, file.Name) 249 | if complain(file.Name, err) { 250 | continue 251 | } 252 | 253 | if f == nil { 254 | f, err = db.InsertFile(a, file.Name) 255 | if complain(file.Name, err) { 256 | continue 257 | } 258 | } 259 | 260 | collisions := make(map[string]int) 261 | 262 | db.Begin() 263 | for _, p := range t.Pairs { 264 | collision := collisions[p.Identifier] 265 | collisions[p.Identifier] = collision + 1 266 | 267 | s, err := db.QueryString(f, collision, p.Identifier) 268 | if complain(f.Name + ": " + p.Identifier, err) { 269 | break 270 | } 271 | 272 | if s != nil { 273 | if s.Value != p.String { 274 | if flagTrans != "" { 275 | if translation == nil { 276 | translation, err = db.QueryTranslation(flagTrans) 277 | if translation == nil { 278 | translation, err = db.InsertTranslation(flagTrans) 279 | if complain(flagTrans, err) { 280 | break 281 | } 282 | } 283 | } 284 | 285 | ts, _ := db.QueryTranslationString(translation, s) 286 | if ts != nil { 287 | _, err = db.UpdateTranslationString(ts, p.String) 288 | } else { 289 | _, err = db.InsertTranslationString(translation, s, p.String) 290 | } 291 | } else { 292 | _, err = db.UpdateString(s, flagImport, p.String) 293 | } 294 | 295 | if complain(f.Name + ": " + p.Identifier, err) { 296 | break 297 | } 298 | } 299 | } else { 300 | if flagTrans != "" { 301 | complain(f.Name + ": " + p.Identifier + ": " + p.String, errors.New("translated identifier does not exist")) 302 | } else { 303 | _, err := db.InsertString(f, flagImport, collision, p.Identifier, p.String) 304 | if complain(f.Name + ": " + p.Identifier, err) { 305 | break 306 | } 307 | } 308 | } 309 | } 310 | db.End() 311 | } 312 | } 313 | } 314 | 315 | af.Close() 316 | } 317 | } 318 | } else if flagTrans != "" { 319 | if flag.NArg() < 2 { 320 | fmt.Fprintln(os.Stderr, "no pso2 dir provided") 321 | return 322 | } 323 | 324 | if flagBackup != "" { 325 | err := os.MkdirAll(flagBackup, 0777) 326 | ragequit(flagBackup, err) 327 | } 328 | 329 | pso2dir := flag.Arg(1) 330 | if flagOutput == "" { 331 | flagOutput = pso2dir 332 | } else { 333 | err := os.MkdirAll(flagOutput, 0777) 334 | ragequit(flagOutput, err) 335 | } 336 | 337 | errs := cmd.PatchFiles(db, pso2dir, flagTrans, flagBackup, flagOutput, flagParallel) 338 | 339 | for _, err := range errs { 340 | complain("", err) 341 | } 342 | } 343 | 344 | db.Close() 345 | 346 | if flagStrip != "" { 347 | err := cmd.StripDatabase(dbpath, flagStrip) 348 | complain(flagStrip, err) 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /download/cmd/config.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "io" 5 | "bufio" 6 | "strings" 7 | ) 8 | 9 | type TranslationConfig map[string]string 10 | 11 | const ( 12 | ConfigTranslationPath = "TranslationPath" 13 | ConfigPublicKeyPath = "PublicKeyPath" 14 | ConfigPublicKeyDump = "PublicKeyDump" 15 | ) 16 | 17 | func NewTranslationConfig(reader io.Reader) (t TranslationConfig, err error) { 18 | t = make(TranslationConfig) 19 | 20 | r := bufio.NewReader(reader) 21 | for err != io.EOF { 22 | var line string 23 | line, err = r.ReadString('\n') 24 | line = strings.Replace(line, "\r", "", -1) 25 | s := strings.SplitN(line, ":", 2) 26 | if len(s) == 2 { 27 | t[s[0]] = s[1] 28 | } 29 | } 30 | 31 | return 32 | } 33 | 34 | func (t TranslationConfig) Write(w io.Writer) (err error) { 35 | for k, v := range t { 36 | line := k + ":" + v + "\n" 37 | 38 | _, err = w.Write([]byte(line)) 39 | if err != nil { 40 | return 41 | } 42 | } 43 | 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /download/cmd/ddraw.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | var DdrawDll = [...]byte{ 4 | 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 5 | 0xff, 0xff, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6 | 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 7 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 9 | 0x80, 0x00, 0x00, 0x00, 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 10 | 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 11 | 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 12 | 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 13 | 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x0d, 0x0d, 0x0a, 14 | 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 15 | 0x4c, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 16 | 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x0e, 0x23, 0x0b, 0x01, 0x02, 0x18, 17 | 0x00, 0x16, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 18 | 0x20, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 19 | 0x00, 0x00, 0xac, 0x66, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 20 | 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 22 | 0x92, 0x3a, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 23 | 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 25 | 0x32, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x7c, 0x04, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | 0x00, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x90, 0x00, 0x00, 31 | 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 32 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x70, 0x00, 0x00, 33 | 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 35 | 0x00, 0x00, 0x00, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 36 | 0xa4, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 37 | 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x50, 0x60, 0x2e, 0x64, 0x61, 0x74, 39 | 0x61, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 40 | 0x00, 0x02, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x30, 0xc0, 42 | 0x2e, 0x72, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 43 | 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x40, 0x00, 0x30, 0x40, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 46 | 0xc4, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x60, 0xc0, 0x2e, 0x65, 0x64, 0x61, 49 | 0x74, 0x61, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 50 | 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x30, 0x40, 52 | 0x2e, 0x69, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x7c, 0x04, 0x00, 0x00, 53 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x40, 0x00, 0x30, 0xc0, 0x2e, 0x43, 0x52, 0x54, 0x00, 0x00, 0x00, 0x00, 56 | 0x2c, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 57 | 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x30, 0xc0, 0x2e, 0x74, 0x6c, 0x73, 59 | 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 60 | 0x00, 0x02, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x30, 0xc0, 62 | 0x2e, 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 63 | 0x00, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 65 | 0x40, 0x00, 0x30, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 75 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 77 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 79 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 80 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 81 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x53, 0x83, 0xec, 0x18, 0xc7, 0x04, 0x24, 0x80, 90 | 0x00, 0x00, 0x00, 0xe8, 0xe0, 0x13, 0x00, 0x00, 0x89, 0x04, 0x24, 0x89, 91 | 0xc3, 0xe8, 0x96, 0x06, 0x00, 0x00, 0x85, 0xdb, 0xa3, 0xa8, 0x53, 0xac, 92 | 0x66, 0xa3, 0xa4, 0x53, 0xac, 0x66, 0x74, 0x0d, 0xc7, 0x03, 0x00, 0x00, 93 | 0x00, 0x00, 0x83, 0xc4, 0x18, 0x31, 0xc0, 0x5b, 0xc3, 0x83, 0xc4, 0x18, 94 | 0xb8, 0x01, 0x00, 0x00, 0x00, 0x5b, 0xc3, 0x90, 0x55, 0x89, 0xe5, 0x57, 95 | 0x56, 0x53, 0x83, 0xec, 0x1c, 0x8b, 0x55, 0x0c, 0x85, 0xd2, 0x75, 0x68, 96 | 0xa1, 0x00, 0x50, 0xac, 0x66, 0x85, 0xc0, 0x0f, 0x8e, 0x06, 0x01, 0x00, 97 | 0x00, 0x83, 0xe8, 0x01, 0x31, 0xdb, 0x8b, 0x35, 0x14, 0x71, 0xac, 0x66, 98 | 0xa3, 0x00, 0x50, 0xac, 0x66, 0xeb, 0x0d, 0x90, 0xc7, 0x04, 0x24, 0xe8, 99 | 0x03, 0x00, 0x00, 0xff, 0xd6, 0x83, 0xec, 0x04, 0xba, 0x01, 0x00, 0x00, 100 | 0x00, 0x89, 0xd8, 0xf0, 0x0f, 0xb1, 0x15, 0xac, 0x53, 0xac, 0x66, 0x85, 101 | 0xc0, 0x75, 0xe1, 0xa1, 0xb0, 0x53, 0xac, 0x66, 0x83, 0xf8, 0x02, 0x0f, 102 | 0x84, 0xd3, 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x1f, 0x00, 0x00, 0x00, 103 | 0xe8, 0x4f, 0x13, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x8d, 0x65, 104 | 0xf4, 0x5b, 0x5e, 0x5f, 0x5d, 0xc2, 0x0c, 0x00, 0x83, 0xfa, 0x01, 0xb8, 105 | 0x01, 0x00, 0x00, 0x00, 0x75, 0xec, 0x64, 0xa1, 0x18, 0x00, 0x00, 0x00, 106 | 0x31, 0xf6, 0x8b, 0x58, 0x04, 0x8b, 0x3d, 0x14, 0x71, 0xac, 0x66, 0xeb, 107 | 0x14, 0x39, 0xd8, 0x0f, 0x84, 0x08, 0x01, 0x00, 0x00, 0xc7, 0x04, 0x24, 108 | 0xe8, 0x03, 0x00, 0x00, 0xff, 0xd7, 0x83, 0xec, 0x04, 0x89, 0xf0, 0xf0, 109 | 0x0f, 0xb1, 0x1d, 0xac, 0x53, 0xac, 0x66, 0x85, 0xc0, 0x75, 0xde, 0x31, 110 | 0xdb, 0xa1, 0xb0, 0x53, 0xac, 0x66, 0x83, 0xf8, 0x01, 0x0f, 0x84, 0x1c, 111 | 0x01, 0x00, 0x00, 0xa1, 0xb0, 0x53, 0xac, 0x66, 0x85, 0xc0, 0x0f, 0x84, 112 | 0xec, 0x00, 0x00, 0x00, 0xa1, 0xb0, 0x53, 0xac, 0x66, 0x83, 0xf8, 0x01, 113 | 0x0f, 0x84, 0x12, 0x01, 0x00, 0x00, 0x85, 0xdb, 0x0f, 0x84, 0xc6, 0x00, 114 | 0x00, 0x00, 0xa1, 0x3c, 0x40, 0xac, 0x66, 0x85, 0xc0, 0x74, 0x1a, 0x8b, 115 | 0x55, 0x10, 0xc7, 0x44, 0x24, 0x04, 0x02, 0x00, 0x00, 0x00, 0x89, 0x54, 116 | 0x24, 0x08, 0x8b, 0x55, 0x08, 0x89, 0x14, 0x24, 0xff, 0xd0, 0x83, 0xec, 117 | 0x0c, 0x83, 0x05, 0x00, 0x50, 0xac, 0x66, 0x01, 0xb8, 0x01, 0x00, 0x00, 118 | 0x00, 0x8d, 0x65, 0xf4, 0x5b, 0x5e, 0x5f, 0x5d, 0xc2, 0x0c, 0x00, 0x31, 119 | 0xc0, 0xe9, 0x44, 0xff, 0xff, 0xff, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 120 | 0xa1, 0xa8, 0x53, 0xac, 0x66, 0x89, 0x04, 0x24, 0xe8, 0x23, 0x05, 0x00, 121 | 0x00, 0x85, 0xc0, 0x89, 0xc6, 0x74, 0x41, 0xa1, 0xa4, 0x53, 0xac, 0x66, 122 | 0x89, 0x04, 0x24, 0xe8, 0x10, 0x05, 0x00, 0x00, 0x89, 0xc3, 0x83, 0xeb, 123 | 0x04, 0x39, 0xde, 0x77, 0x0f, 0x8b, 0x13, 0x85, 0xd2, 0x74, 0xf3, 0x83, 124 | 0xeb, 0x04, 0xff, 0xd2, 0x39, 0xde, 0x76, 0xf1, 0x89, 0x34, 0x24, 0xe8, 125 | 0x50, 0x12, 0x00, 0x00, 0xc7, 0x05, 0xa4, 0x53, 0xac, 0x66, 0x00, 0x00, 126 | 0x00, 0x00, 0xc7, 0x05, 0xa8, 0x53, 0xac, 0x66, 0x00, 0x00, 0x00, 0x00, 127 | 0x31, 0xc0, 0xc7, 0x05, 0xb0, 0x53, 0xac, 0x66, 0x00, 0x00, 0x00, 0x00, 128 | 0x87, 0x05, 0xac, 0x53, 0xac, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x8d, 129 | 0x65, 0xf4, 0x5b, 0x5e, 0x5f, 0x5d, 0xc2, 0x0c, 0x00, 0xbb, 0x01, 0x00, 130 | 0x00, 0x00, 0xe9, 0x0a, 0xff, 0xff, 0xff, 0x90, 0x87, 0x1d, 0xac, 0x53, 131 | 0xac, 0x66, 0xe9, 0x2f, 0xff, 0xff, 0xff, 0x90, 0x8d, 0x74, 0x26, 0x00, 132 | 0xc7, 0x44, 0x24, 0x04, 0x10, 0x80, 0xac, 0x66, 0xc7, 0x04, 0x24, 0x08, 133 | 0x80, 0xac, 0x66, 0xc7, 0x05, 0xb0, 0x53, 0xac, 0x66, 0x01, 0x00, 0x00, 134 | 0x00, 0xe8, 0xea, 0x11, 0x00, 0x00, 0xe9, 0xf1, 0xfe, 0xff, 0xff, 0xc7, 135 | 0x04, 0x24, 0x1f, 0x00, 0x00, 0x00, 0xe8, 0xc9, 0x11, 0x00, 0x00, 0xe9, 136 | 0xe0, 0xfe, 0xff, 0xff, 0xc7, 0x44, 0x24, 0x04, 0x04, 0x80, 0xac, 0x66, 137 | 0xc7, 0x04, 0x24, 0x00, 0x80, 0xac, 0x66, 0xe8, 0xc0, 0x11, 0x00, 0x00, 138 | 0xc7, 0x05, 0xb0, 0x53, 0xac, 0x66, 0x02, 0x00, 0x00, 0x00, 0xe9, 0xcb, 139 | 0xfe, 0xff, 0xff, 0x89, 0xf6, 0x8d, 0xbc, 0x27, 0x00, 0x00, 0x00, 0x00, 140 | 0x55, 0x89, 0xe5, 0x57, 0x89, 0xcf, 0x56, 0x89, 0xc6, 0x53, 0x89, 0xd3, 141 | 0x83, 0xec, 0x1c, 0x85, 0xd2, 0x89, 0x15, 0x04, 0x30, 0xac, 0x66, 0x75, 142 | 0x4c, 0xa1, 0x00, 0x50, 0xac, 0x66, 0x85, 0xc0, 0x74, 0x6e, 0xe8, 0xd9, 143 | 0x06, 0x00, 0x00, 0x89, 0x7c, 0x24, 0x08, 0xc7, 0x44, 0x24, 0x04, 0x00, 144 | 0x00, 0x00, 0x00, 0x89, 0x34, 0x24, 0xe8, 0x25, 0x02, 0x00, 0x00, 0x83, 145 | 0xec, 0x0c, 0x89, 0xc2, 0x83, 0xfb, 0x03, 0x0f, 0x84, 0xce, 0x00, 0x00, 146 | 0x00, 0x85, 0xdb, 0x0f, 0x84, 0xc6, 0x00, 0x00, 0x00, 0xc7, 0x05, 0x04, 147 | 0x30, 0xac, 0x66, 0xff, 0xff, 0xff, 0xff, 0x8d, 0x65, 0xf4, 0x89, 0xd0, 148 | 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0xe8, 0x96, 0x06, 0x00, 0x00, 0x8d, 0x43, 149 | 0xff, 0x83, 0xf8, 0x01, 0x77, 0x33, 0x89, 0x7c, 0x24, 0x08, 0x89, 0x5c, 150 | 0x24, 0x04, 0x89, 0x34, 0x24, 0xe8, 0x5e, 0xfd, 0xff, 0xff, 0x83, 0xec, 151 | 0x0c, 0x85, 0xc0, 0x0f, 0x85, 0xc5, 0x00, 0x00, 0x00, 0x8d, 0x76, 0x00, 152 | 0x31, 0xd2, 0xeb, 0xbd, 0x83, 0xfb, 0x01, 0x0f, 0x85, 0xfb, 0x00, 0x00, 153 | 0x00, 0x8d, 0x76, 0x00, 0xe8, 0xbb, 0x09, 0x00, 0x00, 0x89, 0x7c, 0x24, 154 | 0x08, 0x89, 0x5c, 0x24, 0x04, 0x89, 0x34, 0x24, 0xe8, 0xab, 0x01, 0x00, 155 | 0x00, 0x83, 0xec, 0x0c, 0x85, 0xc0, 0x89, 0xc2, 0x75, 0x82, 0x83, 0xfb, 156 | 0x01, 0x0f, 0x85, 0x79, 0xff, 0xff, 0xff, 0x89, 0x7c, 0x24, 0x08, 0xc7, 157 | 0x44, 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x89, 0x34, 0x24, 0xe8, 0x85, 158 | 0x01, 0x00, 0x00, 0x83, 0xec, 0x0c, 0x89, 0x7c, 0x24, 0x08, 0xc7, 0x44, 159 | 0x24, 0x04, 0x00, 0x00, 0x00, 0x00, 0x89, 0x34, 0x24, 0xe8, 0x8e, 0x10, 160 | 0x00, 0x00, 0x83, 0xec, 0x0c, 0x89, 0x7c, 0x24, 0x08, 0xc7, 0x44, 0x24, 161 | 0x04, 0x00, 0x00, 0x00, 0x00, 0x89, 0x34, 0x24, 0xe8, 0xd7, 0xfc, 0xff, 162 | 0xff, 0x31, 0xd2, 0x83, 0xec, 0x0c, 0x83, 0xfb, 0x03, 0x0f, 0x85, 0x32, 163 | 0xff, 0xff, 0xff, 0x89, 0x7c, 0x24, 0x08, 0x89, 0x5c, 0x24, 0x04, 0x89, 164 | 0x34, 0x24, 0xe8, 0x59, 0x10, 0x00, 0x00, 0x83, 0xec, 0x0c, 0x89, 0x7c, 165 | 0x24, 0x08, 0x89, 0x5c, 0x24, 0x04, 0x89, 0x34, 0x24, 0x89, 0x45, 0xe4, 166 | 0xe8, 0xa3, 0xfc, 0xff, 0xff, 0x8b, 0x55, 0xe4, 0x83, 0xec, 0x0c, 0x85, 167 | 0xc0, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x44, 0xd0, 0xe9, 0xff, 0xfe, 168 | 0xff, 0xff, 0x89, 0x7c, 0x24, 0x08, 0x89, 0x5c, 0x24, 0x04, 0x89, 0x34, 169 | 0x24, 0xe8, 0x1e, 0x10, 0x00, 0x00, 0x83, 0xec, 0x0c, 0x85, 0xc0, 0x0f, 170 | 0x85, 0x27, 0xff, 0xff, 0xff, 0x83, 0xfb, 0x01, 0x0f, 0x85, 0x1a, 0xff, 171 | 0xff, 0xff, 0x89, 0x7c, 0x24, 0x08, 0xc7, 0x44, 0x24, 0x04, 0x00, 0x00, 172 | 0x00, 0x00, 0x89, 0x34, 0x24, 0x89, 0x45, 0xe4, 0xe8, 0x53, 0xfc, 0xff, 173 | 0xff, 0x8b, 0x55, 0xe4, 0x83, 0xec, 0x0c, 0xe9, 0xb9, 0xfe, 0xff, 0xff, 174 | 0x89, 0x7c, 0x24, 0x08, 0xc7, 0x44, 0x24, 0x04, 0x02, 0x00, 0x00, 0x00, 175 | 0x89, 0x34, 0x24, 0xe8, 0xb4, 0x00, 0x00, 0x00, 0x83, 0xec, 0x0c, 0x89, 176 | 0xc2, 0xe9, 0x8a, 0xfe, 0xff, 0xff, 0x8d, 0x76, 0x00, 0x8d, 0xbc, 0x27, 177 | 0x00, 0x00, 0x00, 0x00, 0x83, 0xec, 0x1c, 0x8b, 0x54, 0x24, 0x24, 0xc7, 178 | 0x05, 0x14, 0x50, 0xac, 0x66, 0x00, 0x00, 0x00, 0x00, 0x83, 0xfa, 0x01, 179 | 0x74, 0x1a, 0x8b, 0x4c, 0x24, 0x28, 0x8b, 0x44, 0x24, 0x20, 0xe8, 0x1d, 180 | 0xfe, 0xff, 0xff, 0x83, 0xc4, 0x1c, 0xc2, 0x0c, 0x00, 0x8d, 0xb4, 0x26, 181 | 0x00, 0x00, 0x00, 0x00, 0x89, 0x54, 0x24, 0x0c, 0xe8, 0x87, 0x08, 0x00, 182 | 0x00, 0x8b, 0x54, 0x24, 0x0c, 0xeb, 0xd7, 0x90, 0xa1, 0x18, 0x30, 0xac, 183 | 0x66, 0x85, 0xc0, 0x74, 0x43, 0x55, 0x89, 0xe5, 0x83, 0xec, 0x18, 0xc7, 184 | 0x04, 0x24, 0x00, 0x40, 0xac, 0x66, 0xff, 0x15, 0xf0, 0x70, 0xac, 0x66, 185 | 0x83, 0xec, 0x04, 0x85, 0xc0, 0xba, 0x00, 0x00, 0x00, 0x00, 0x74, 0x16, 186 | 0xc7, 0x44, 0x24, 0x04, 0x0e, 0x40, 0xac, 0x66, 0x89, 0x04, 0x24, 0xff, 187 | 0x15, 0xf4, 0x70, 0xac, 0x66, 0x83, 0xec, 0x08, 0x89, 0xc2, 0x85, 0xd2, 188 | 0x74, 0x09, 0xc7, 0x04, 0x24, 0x18, 0x30, 0xac, 0x66, 0xff, 0xd2, 0xc9, 189 | 0xf3, 0xc3, 0x66, 0x90, 0x55, 0x89, 0xe5, 0x5d, 0xc3, 0x90, 0x90, 0x90, 190 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x55, 0x89, 0xe5, 0x83, 191 | 0xec, 0x18, 0x83, 0x7d, 0x0c, 0x01, 0x75, 0x0e, 0xc7, 0x04, 0x24, 0x24, 192 | 0x40, 0xac, 0x66, 0xff, 0x15, 0x08, 0x71, 0xac, 0x66, 0x50, 0x31, 0xc0, 193 | 0xc9, 0xc2, 0x0c, 0x00, 0x53, 0x83, 0xec, 0x28, 0xa1, 0xa8, 0x53, 0xac, 194 | 0x66, 0x89, 0x04, 0x24, 0xe8, 0xaf, 0x01, 0x00, 0x00, 0x83, 0xf8, 0xff, 195 | 0x89, 0x44, 0x24, 0x18, 0x0f, 0x84, 0x82, 0x00, 0x00, 0x00, 0xc7, 0x04, 196 | 0x24, 0x08, 0x00, 0x00, 0x00, 0xe8, 0x06, 0x0f, 0x00, 0x00, 0xa1, 0xa8, 197 | 0x53, 0xac, 0x66, 0x89, 0x04, 0x24, 0xe8, 0x89, 0x01, 0x00, 0x00, 0x89, 198 | 0x44, 0x24, 0x18, 0xa1, 0xa4, 0x53, 0xac, 0x66, 0x89, 0x04, 0x24, 0xe8, 199 | 0x78, 0x01, 0x00, 0x00, 0x89, 0x44, 0x24, 0x1c, 0x8d, 0x44, 0x24, 0x1c, 200 | 0x89, 0x44, 0x24, 0x08, 0x8d, 0x44, 0x24, 0x18, 0x89, 0x44, 0x24, 0x04, 201 | 0x8b, 0x44, 0x24, 0x30, 0x89, 0x04, 0x24, 0xe8, 0xd0, 0x0e, 0x00, 0x00, 202 | 0x89, 0xc3, 0x8b, 0x44, 0x24, 0x18, 0x89, 0x04, 0x24, 0xe8, 0x5a, 0x01, 203 | 0x00, 0x00, 0xa3, 0xa8, 0x53, 0xac, 0x66, 0x8b, 0x44, 0x24, 0x1c, 0x89, 204 | 0x04, 0x24, 0xe8, 0x49, 0x01, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x08, 0x00, 205 | 0x00, 0x00, 0xa3, 0xa4, 0x53, 0xac, 0x66, 0xe8, 0xa8, 0x0e, 0x00, 0x00, 206 | 0x83, 0xc4, 0x28, 0x89, 0xd8, 0x5b, 0xc3, 0x90, 0x8b, 0x44, 0x24, 0x30, 207 | 0x89, 0x04, 0x24, 0xff, 0x15, 0x44, 0x71, 0xac, 0x66, 0x83, 0xc4, 0x28, 208 | 0x5b, 0xc3, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xbc, 0x27, 209 | 0x00, 0x00, 0x00, 0x00, 0x83, 0xec, 0x1c, 0x8b, 0x44, 0x24, 0x20, 0x89, 210 | 0x04, 0x24, 0xe8, 0x31, 0xff, 0xff, 0xff, 0x85, 0xc0, 0x0f, 0x94, 0xc0, 211 | 0x83, 0xc4, 0x1c, 0x0f, 0xb6, 0xc0, 0xf7, 0xd8, 0xc3, 0x90, 0x90, 0x90, 212 | 0x83, 0xec, 0x1c, 0x8b, 0x44, 0x24, 0x24, 0x85, 0xc0, 0x74, 0x15, 0x83, 213 | 0xf8, 0x03, 0x74, 0x10, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x83, 0xc4, 0x1c, 214 | 0xc2, 0x0c, 0x00, 0x90, 0x8d, 0x74, 0x26, 0x00, 0x8b, 0x54, 0x24, 0x28, 215 | 0x89, 0x44, 0x24, 0x04, 0x8b, 0x44, 0x24, 0x20, 0x89, 0x54, 0x24, 0x08, 216 | 0x89, 0x04, 0x24, 0xe8, 0xd8, 0x09, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 217 | 0x00, 0x83, 0xc4, 0x1c, 0xc2, 0x0c, 0x00, 0x8d, 0xb6, 0x00, 0x00, 0x00, 218 | 0x00, 0x8d, 0xbc, 0x27, 0x00, 0x00, 0x00, 0x00, 0x53, 0x83, 0xec, 0x18, 219 | 0x83, 0x3d, 0x0c, 0x30, 0xac, 0x66, 0x02, 0x8b, 0x44, 0x24, 0x24, 0x74, 220 | 0x0a, 0xc7, 0x05, 0x0c, 0x30, 0xac, 0x66, 0x02, 0x00, 0x00, 0x00, 0x83, 221 | 0xf8, 0x02, 0x74, 0x11, 0x83, 0xf8, 0x01, 0x74, 0x3b, 0x83, 0xc4, 0x18, 222 | 0xb8, 0x01, 0x00, 0x00, 0x00, 0x5b, 0xc2, 0x0c, 0x00, 0xbb, 0x28, 0x80, 223 | 0xac, 0x66, 0x81, 0xfb, 0x28, 0x80, 0xac, 0x66, 0x74, 0xe7, 0x66, 0x90, 224 | 0x8b, 0x03, 0x85, 0xc0, 0x74, 0x02, 0xff, 0xd0, 0x83, 0xc3, 0x04, 0x81, 225 | 0xfb, 0x28, 0x80, 0xac, 0x66, 0x75, 0xed, 0x83, 0xc4, 0x18, 0xb8, 0x01, 226 | 0x00, 0x00, 0x00, 0x5b, 0xc2, 0x0c, 0x00, 0x90, 0x8b, 0x44, 0x24, 0x28, 227 | 0xc7, 0x44, 0x24, 0x04, 0x01, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x08, 228 | 0x8b, 0x44, 0x24, 0x20, 0x89, 0x04, 0x24, 0xe8, 0x44, 0x09, 0x00, 0x00, 229 | 0xeb, 0xa7, 0x66, 0x90, 0x31, 0xc0, 0xc3, 0x90, 0x90, 0x90, 0x90, 0x90, 230 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8b, 0x44, 0x24, 0x04, 231 | 0xc3, 0x8d, 0x74, 0x26, 0x00, 0x8d, 0xbc, 0x27, 0x00, 0x00, 0x00, 0x00, 232 | 0x8b, 0x44, 0x24, 0x04, 0xc3, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 233 | 0x90, 0x90, 0x90, 0x90, 0x53, 0x83, 0xec, 0x18, 0xa1, 0x3c, 0x71, 0xac, 234 | 0x66, 0xc7, 0x44, 0x24, 0x08, 0x1b, 0x00, 0x00, 0x00, 0x8d, 0x5c, 0x24, 235 | 0x24, 0xc7, 0x44, 0x24, 0x04, 0x01, 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 236 | 0x40, 0x40, 0xac, 0x66, 0x83, 0xc0, 0x40, 0x89, 0x44, 0x24, 0x0c, 0xe8, 237 | 0x38, 0x0d, 0x00, 0x00, 0x8b, 0x44, 0x24, 0x20, 0x89, 0x5c, 0x24, 0x08, 238 | 0x89, 0x44, 0x24, 0x04, 0xa1, 0x3c, 0x71, 0xac, 0x66, 0x83, 0xc0, 0x40, 239 | 0x89, 0x04, 0x24, 0xe8, 0x24, 0x0d, 0x00, 0x00, 0xe8, 0x27, 0x0d, 0x00, 240 | 0x00, 0xeb, 0x0d, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 241 | 0x90, 0x90, 0x90, 0x90, 0x55, 0x89, 0xe5, 0x57, 0x56, 0x89, 0xc6, 0x53, 242 | 0x83, 0xec, 0x4c, 0x89, 0x4d, 0xc0, 0x8b, 0x0d, 0x1c, 0x50, 0xac, 0x66, 243 | 0x89, 0x55, 0xc4, 0x85, 0xc9, 0x0f, 0x8e, 0xd1, 0x01, 0x00, 0x00, 0x8b, 244 | 0x15, 0x20, 0x50, 0xac, 0x66, 0x31, 0xdb, 0x8b, 0x42, 0x04, 0x39, 0xc6, 245 | 0x72, 0x0e, 0x8b, 0x7a, 0x08, 0x03, 0x47, 0x08, 0x39, 0xc6, 0x0f, 0x82, 246 | 0xd4, 0x00, 0x00, 0x00, 0x83, 0xc3, 0x01, 0x83, 0xc2, 0x0c, 0x39, 0xcb, 247 | 0x75, 0xe1, 0x89, 0x34, 0x24, 0xe8, 0x22, 0x0a, 0x00, 0x00, 0x85, 0xc0, 248 | 0x89, 0xc7, 0x0f, 0x84, 0xbf, 0x01, 0x00, 0x00, 0x8d, 0x0c, 0x5b, 0xc1, 249 | 0xe1, 0x02, 0x89, 0xcb, 0x03, 0x1d, 0x20, 0x50, 0xac, 0x66, 0x89, 0x4d, 250 | 0xbc, 0x89, 0x43, 0x08, 0xc7, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xe9, 251 | 0x0a, 0x00, 0x00, 0x8b, 0x4d, 0xbc, 0x8d, 0x55, 0xcc, 0x89, 0x55, 0xb8, 252 | 0x03, 0x47, 0x0c, 0x89, 0x43, 0x04, 0xa1, 0x20, 0x50, 0xac, 0x66, 0x89, 253 | 0x54, 0x24, 0x04, 0x8b, 0x1d, 0x28, 0x71, 0xac, 0x66, 0xc7, 0x44, 0x24, 254 | 0x08, 0x1c, 0x00, 0x00, 0x00, 0x8b, 0x44, 0x08, 0x04, 0x89, 0x04, 0x24, 255 | 0xff, 0xd3, 0x8b, 0x4d, 0xbc, 0x8b, 0x55, 0xb8, 0x83, 0xec, 0x0c, 0x85, 256 | 0xc0, 0x0f, 0x84, 0x40, 0x01, 0x00, 0x00, 0x8b, 0x45, 0xe0, 0x83, 0xf8, 257 | 0x04, 0x0f, 0x85, 0xd1, 0x00, 0x00, 0x00, 0x83, 0x05, 0x1c, 0x50, 0xac, 258 | 0x66, 0x01, 0xc7, 0x44, 0x24, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x89, 0x54, 259 | 0x24, 0x04, 0x89, 0x34, 0x24, 0xff, 0xd3, 0x83, 0xec, 0x0c, 0x85, 0xc0, 260 | 0x0f, 0x84, 0x41, 0x01, 0x00, 0x00, 0x8b, 0x45, 0xe0, 0x83, 0xf8, 0x04, 261 | 0x75, 0x32, 0x8b, 0x45, 0xc0, 0x89, 0x34, 0x24, 0x89, 0x44, 0x24, 0x08, 262 | 0x8b, 0x45, 0xc4, 0x89, 0x44, 0x24, 0x04, 0xe8, 0x1c, 0x0c, 0x00, 0x00, 263 | 0x8d, 0x65, 0xf4, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0x8d, 0x74, 0x26, 0x00, 264 | 0x8d, 0x55, 0xcc, 0x8b, 0x1d, 0x28, 0x71, 0xac, 0x66, 0xeb, 0xaf, 0x90, 265 | 0x8d, 0x74, 0x26, 0x00, 0x83, 0xf8, 0x40, 0x74, 0xc9, 0x8b, 0x45, 0xd8, 266 | 0x8d, 0x7d, 0xc8, 0x8b, 0x1d, 0x24, 0x71, 0xac, 0x66, 0x89, 0x7c, 0x24, 267 | 0x0c, 0xc7, 0x44, 0x24, 0x08, 0x40, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 268 | 0x04, 0x8b, 0x45, 0xcc, 0x89, 0x04, 0x24, 0xff, 0xd3, 0x8b, 0x45, 0xc0, 269 | 0x83, 0xec, 0x10, 0x89, 0x44, 0x24, 0x08, 0x8b, 0x45, 0xc4, 0x89, 0x34, 270 | 0x24, 0x89, 0x44, 0x24, 0x04, 0xe8, 0xbe, 0x0b, 0x00, 0x00, 0x8b, 0x45, 271 | 0xe0, 0x83, 0xf8, 0x40, 0x74, 0x9a, 0x83, 0xf8, 0x04, 0x74, 0x95, 0x8b, 272 | 0x45, 0xc8, 0x89, 0x7c, 0x24, 0x0c, 0x89, 0x44, 0x24, 0x08, 0x8b, 0x45, 273 | 0xd8, 0x89, 0x44, 0x24, 0x04, 0x8b, 0x45, 0xcc, 0x89, 0x04, 0x24, 0xff, 274 | 0xd3, 0x83, 0xec, 0x10, 0x8d, 0x65, 0xf4, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 275 | 0x83, 0xf8, 0x40, 0x0f, 0x84, 0x26, 0xff, 0xff, 0xff, 0x8b, 0x45, 0xd8, 276 | 0x03, 0x0d, 0x20, 0x50, 0xac, 0x66, 0x89, 0x55, 0xbc, 0xc7, 0x44, 0x24, 277 | 0x08, 0x40, 0x00, 0x00, 0x00, 0x89, 0x44, 0x24, 0x04, 0x8b, 0x45, 0xcc, 278 | 0x89, 0x4c, 0x24, 0x0c, 0x89, 0x04, 0x24, 0xff, 0x15, 0x24, 0x71, 0xac, 279 | 0x66, 0x8b, 0x55, 0xbc, 0x83, 0xec, 0x10, 0x85, 0xc0, 0x0f, 0x85, 0xf0, 280 | 0xfe, 0xff, 0xff, 0xff, 0x15, 0xec, 0x70, 0xac, 0x66, 0xc7, 0x04, 0x24, 281 | 0xb0, 0x40, 0xac, 0x66, 0x89, 0x44, 0x24, 0x04, 0xe8, 0xb7, 0xfd, 0xff, 282 | 0xff, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x31, 0xdb, 0xe9, 0x4f, 283 | 0xfe, 0xff, 0xff, 0xa1, 0x20, 0x50, 0xac, 0x66, 0x8b, 0x44, 0x08, 0x04, 284 | 0x89, 0x44, 0x24, 0x08, 0x8b, 0x47, 0x08, 0xc7, 0x04, 0x24, 0x7c, 0x40, 285 | 0xac, 0x66, 0x89, 0x44, 0x24, 0x04, 0xe8, 0x89, 0xfd, 0xff, 0xff, 0x89, 286 | 0x74, 0x24, 0x04, 0xc7, 0x04, 0x24, 0x5c, 0x40, 0xac, 0x66, 0xe8, 0x79, 287 | 0xfd, 0xff, 0xff, 0x89, 0x74, 0x24, 0x08, 0xc7, 0x44, 0x24, 0x04, 0x1c, 288 | 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x7c, 0x40, 0xac, 0x66, 0xe8, 0x61, 289 | 0xfd, 0xff, 0xff, 0x90, 0xa1, 0x18, 0x50, 0xac, 0x66, 0x85, 0xc0, 0x74, 290 | 0x07, 0xc3, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xe5, 0x57, 291 | 0x56, 0x53, 0x83, 0xec, 0x4c, 0xc7, 0x05, 0x18, 0x50, 0xac, 0x66, 0x01, 292 | 0x00, 0x00, 0x00, 0xe8, 0x58, 0x08, 0x00, 0x00, 0x8d, 0x04, 0x40, 0x8d, 293 | 0x04, 0x85, 0x1e, 0x00, 0x00, 0x00, 0x83, 0xe0, 0xf0, 0xe8, 0x16, 0x0a, 294 | 0x00, 0x00, 0xc7, 0x05, 0x1c, 0x50, 0xac, 0x66, 0x00, 0x00, 0x00, 0x00, 295 | 0x29, 0xc4, 0x8d, 0x44, 0x24, 0x1f, 0x83, 0xe0, 0xf0, 0xa3, 0x20, 0x50, 296 | 0xac, 0x66, 0xb8, 0xa8, 0x42, 0xac, 0x66, 0x2d, 0xa8, 0x42, 0xac, 0x66, 297 | 0x83, 0xf8, 0x07, 0x0f, 0x8e, 0x9c, 0x00, 0x00, 0x00, 0x83, 0xf8, 0x0b, 298 | 0x0f, 0x8e, 0x65, 0x01, 0x00, 0x00, 0xa1, 0xa8, 0x42, 0xac, 0x66, 0x85, 299 | 0xc0, 0x0f, 0x85, 0x8e, 0x00, 0x00, 0x00, 0xa1, 0xac, 0x42, 0xac, 0x66, 300 | 0x85, 0xc0, 0x0f, 0x85, 0x81, 0x00, 0x00, 0x00, 0x8b, 0x3d, 0xb0, 0x42, 301 | 0xac, 0x66, 0xbb, 0xb4, 0x42, 0xac, 0x66, 0x85, 0xff, 0x0f, 0x84, 0x3d, 302 | 0x01, 0x00, 0x00, 0xbb, 0xa8, 0x42, 0xac, 0x66, 0x8b, 0x43, 0x08, 0x83, 303 | 0xf8, 0x01, 0x0f, 0x85, 0x17, 0x02, 0x00, 0x00, 0x83, 0xc3, 0x0c, 0x81, 304 | 0xfb, 0xa8, 0x42, 0xac, 0x66, 0x73, 0x4a, 0x8b, 0x13, 0x8b, 0x7b, 0x04, 305 | 0x8b, 0x8a, 0x00, 0x00, 0xac, 0x66, 0x8d, 0x87, 0x00, 0x00, 0xac, 0x66, 306 | 0x89, 0x4d, 0xc4, 0x0f, 0xb6, 0x4b, 0x08, 0x83, 0xf9, 0x10, 0x0f, 0x84, 307 | 0x1a, 0x01, 0x00, 0x00, 0x83, 0xf9, 0x20, 0x0f, 0x84, 0x98, 0x01, 0x00, 308 | 0x00, 0x83, 0xf9, 0x08, 0x0f, 0x84, 0x57, 0x01, 0x00, 0x00, 0x89, 0x4c, 309 | 0x24, 0x04, 0xc7, 0x04, 0x24, 0x0c, 0x41, 0xac, 0x66, 0xc7, 0x45, 0xcc, 310 | 0x00, 0x00, 0x00, 0x00, 0xe8, 0x5f, 0xfc, 0xff, 0xff, 0x8d, 0x65, 0xf4, 311 | 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0xbb, 0xa8, 0x42, 0xac, 0x66, 0x81, 0xfb, 312 | 0xa8, 0x42, 0xac, 0x66, 0x73, 0xeb, 0x8d, 0x45, 0xcc, 0x89, 0x45, 0xc4, 313 | 0x8d, 0x74, 0x26, 0x00, 0x8b, 0x53, 0x04, 0xb9, 0x04, 0x00, 0x00, 0x00, 314 | 0x83, 0xc3, 0x08, 0x8d, 0x82, 0x00, 0x00, 0xac, 0x66, 0x8b, 0x92, 0x00, 315 | 0x00, 0xac, 0x66, 0x03, 0x53, 0xf8, 0x89, 0x55, 0xcc, 0x8b, 0x55, 0xc4, 316 | 0xe8, 0x7b, 0xfc, 0xff, 0xff, 0x81, 0xfb, 0xa8, 0x42, 0xac, 0x66, 0x72, 317 | 0xd3, 0xa1, 0x1c, 0x50, 0xac, 0x66, 0x31, 0xdb, 0x85, 0xc0, 0x7f, 0x13, 318 | 0xeb, 0xa7, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x83, 0xc3, 0x01, 0x3b, 319 | 0x1d, 0x1c, 0x50, 0xac, 0x66, 0x7d, 0x96, 0xa1, 0x20, 0x50, 0xac, 0x66, 320 | 0x8d, 0x34, 0x5b, 0x8d, 0x3c, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 321 | 0x8b, 0x10, 0x85, 0xd2, 0x74, 0xde, 0x8b, 0x4d, 0xc4, 0xc7, 0x44, 0x24, 322 | 0x08, 0x1c, 0x00, 0x00, 0x00, 0x89, 0x4c, 0x24, 0x04, 0x8b, 0x40, 0x04, 323 | 0x89, 0x04, 0x24, 0xff, 0x15, 0x28, 0x71, 0xac, 0x66, 0x83, 0xec, 0x0c, 324 | 0x85, 0xc0, 0x0f, 0x84, 0xf6, 0x00, 0x00, 0x00, 0x8d, 0x45, 0xc8, 0x89, 325 | 0x44, 0x24, 0x0c, 0xa1, 0x20, 0x50, 0xac, 0x66, 0x8b, 0x04, 0xb0, 0x89, 326 | 0x44, 0x24, 0x08, 0x8b, 0x45, 0xd8, 0x89, 0x44, 0x24, 0x04, 0x8b, 0x45, 327 | 0xcc, 0x89, 0x04, 0x24, 0xff, 0x15, 0x24, 0x71, 0xac, 0x66, 0x83, 0xec, 328 | 0x10, 0xeb, 0x8d, 0xbb, 0xa8, 0x42, 0xac, 0x66, 0x8b, 0x33, 0x85, 0xf6, 329 | 0x0f, 0x85, 0x2c, 0xff, 0xff, 0xff, 0x8b, 0x4b, 0x04, 0x85, 0xc9, 0x0f, 330 | 0x84, 0xb3, 0xfe, 0xff, 0xff, 0xe9, 0x1c, 0xff, 0xff, 0xff, 0x0f, 0xb7, 331 | 0x8f, 0x00, 0x00, 0xac, 0x66, 0x0f, 0xb7, 0xf9, 0x89, 0xfe, 0x81, 0xce, 332 | 0x00, 0x00, 0xff, 0xff, 0x66, 0x85, 0xc9, 0x0f, 0x48, 0xfe, 0x8b, 0x75, 333 | 0xc4, 0x29, 0xd7, 0x8d, 0x4d, 0xcc, 0x81, 0xef, 0x00, 0x00, 0xac, 0x66, 334 | 0x89, 0x4d, 0xc4, 0x8d, 0x55, 0xcc, 0xb9, 0x02, 0x00, 0x00, 0x00, 0x01, 335 | 0xfe, 0x89, 0x75, 0xcc, 0xe8, 0x93, 0xfb, 0xff, 0xff, 0x83, 0xc3, 0x0c, 336 | 0x81, 0xfb, 0xa8, 0x42, 0xac, 0x66, 0x0f, 0x82, 0x7b, 0xfe, 0xff, 0xff, 337 | 0xe9, 0x0c, 0xff, 0xff, 0xff, 0x0f, 0xb6, 0x08, 0x0f, 0xb6, 0xf9, 0x89, 338 | 0xfe, 0x81, 0xce, 0x00, 0xff, 0xff, 0xff, 0x84, 0xc9, 0x0f, 0x48, 0xfe, 339 | 0x8b, 0x75, 0xc4, 0x81, 0xef, 0x00, 0x00, 0xac, 0x66, 0x29, 0xd7, 0x8d, 340 | 0x4d, 0xcc, 0x01, 0xfe, 0x89, 0x4d, 0xc4, 0x8d, 0x55, 0xcc, 0xb9, 0x01, 341 | 0x00, 0x00, 0x00, 0x89, 0x75, 0xcc, 0xe8, 0x49, 0xfb, 0xff, 0xff, 0xeb, 342 | 0xb4, 0x8b, 0x75, 0xc4, 0x81, 0xc2, 0x00, 0x00, 0xac, 0x66, 0x8d, 0x4d, 343 | 0xcc, 0x89, 0x4d, 0xc4, 0xb9, 0x04, 0x00, 0x00, 0x00, 0x29, 0xd6, 0x03, 344 | 0x30, 0x8d, 0x55, 0xcc, 0x89, 0x75, 0xcc, 0xe8, 0x24, 0xfb, 0xff, 0xff, 345 | 0xeb, 0x8f, 0x8b, 0x0d, 0x20, 0x50, 0xac, 0x66, 0x01, 0xf9, 0x8b, 0x41, 346 | 0x04, 0x89, 0x44, 0x24, 0x08, 0x8b, 0x41, 0x08, 0x8b, 0x40, 0x08, 0xc7, 347 | 0x04, 0x24, 0x7c, 0x40, 0xac, 0x66, 0x89, 0x44, 0x24, 0x04, 0xe8, 0x9d, 348 | 0xfa, 0xff, 0xff, 0x89, 0x44, 0x24, 0x04, 0xc7, 0x04, 0x24, 0xd8, 0x40, 349 | 0xac, 0x66, 0xe8, 0x8d, 0xfa, 0xff, 0xff, 0x90, 0x90, 0x90, 0x90, 0x90, 350 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xa1, 0x08, 0x30, 0xac, 351 | 0x66, 0x8b, 0x00, 0x85, 0xc0, 0x74, 0x1f, 0x83, 0xec, 0x0c, 0x66, 0x90, 352 | 0xff, 0xd0, 0xa1, 0x08, 0x30, 0xac, 0x66, 0x8d, 0x50, 0x04, 0x8b, 0x40, 353 | 0x04, 0x89, 0x15, 0x08, 0x30, 0xac, 0x66, 0x85, 0xc0, 0x75, 0xe9, 0x83, 354 | 0xc4, 0x0c, 0xf3, 0xc3, 0x8d, 0x74, 0x26, 0x00, 0x53, 0x83, 0xec, 0x18, 355 | 0x8b, 0x1d, 0x90, 0x24, 0xac, 0x66, 0x83, 0xfb, 0xff, 0x74, 0x24, 0x85, 356 | 0xdb, 0x74, 0x0f, 0xff, 0x14, 0x9d, 0x90, 0x24, 0xac, 0x66, 0x83, 0xeb, 357 | 0x01, 0x8d, 0x76, 0x00, 0x75, 0xf1, 0xc7, 0x04, 0x24, 0x40, 0x1c, 0xac, 358 | 0x66, 0xe8, 0x02, 0xf9, 0xff, 0xff, 0x83, 0xc4, 0x18, 0x5b, 0xc3, 0x31, 359 | 0xdb, 0xeb, 0x02, 0x89, 0xc3, 0x8d, 0x43, 0x01, 0x8b, 0x14, 0x85, 0x90, 360 | 0x24, 0xac, 0x66, 0x85, 0xd2, 0x75, 0xf0, 0xeb, 0xc6, 0x8d, 0xb4, 0x26, 361 | 0x00, 0x00, 0x00, 0x00, 0x8b, 0x0d, 0x24, 0x50, 0xac, 0x66, 0x85, 0xc9, 362 | 0x74, 0x06, 0xf3, 0xc3, 0x8d, 0x74, 0x26, 0x00, 0xc7, 0x05, 0x24, 0x50, 363 | 0xac, 0x66, 0x01, 0x00, 0x00, 0x00, 0xeb, 0x94, 0x90, 0x90, 0x90, 0x90, 364 | 0x55, 0x89, 0xe5, 0x57, 0x56, 0x53, 0x83, 0xec, 0x2c, 0xa1, 0x10, 0x30, 365 | 0xac, 0x66, 0xc7, 0x45, 0xd8, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x45, 0xdc, 366 | 0x00, 0x00, 0x00, 0x00, 0x3d, 0x4e, 0xe6, 0x40, 0xbb, 0x74, 0x0f, 0xf7, 367 | 0xd0, 0xa3, 0x14, 0x30, 0xac, 0x66, 0x8d, 0x65, 0xf4, 0x5b, 0x5e, 0x5f, 368 | 0x5d, 0xc3, 0x8d, 0x45, 0xd8, 0x89, 0x04, 0x24, 0xff, 0x15, 0xf8, 0x70, 369 | 0xac, 0x66, 0x8b, 0x75, 0xd8, 0x8b, 0x7d, 0xdc, 0x31, 0xfe, 0x83, 0xec, 370 | 0x04, 0xff, 0x15, 0xe4, 0x70, 0xac, 0x66, 0x89, 0xc3, 0xff, 0x15, 0xe8, 371 | 0x70, 0xac, 0x66, 0x89, 0x45, 0xd4, 0xff, 0x15, 0xfc, 0x70, 0xac, 0x66, 372 | 0x89, 0x45, 0xd0, 0x8d, 0x45, 0xe0, 0x89, 0x04, 0x24, 0xff, 0x15, 0x0c, 373 | 0x71, 0xac, 0x66, 0x33, 0x75, 0xe0, 0x33, 0x75, 0xe4, 0x31, 0xde, 0x33, 374 | 0x75, 0xd4, 0x83, 0xec, 0x04, 0x33, 0x75, 0xd0, 0x81, 0xfe, 0x4e, 0xe6, 375 | 0x40, 0xbb, 0x74, 0x18, 0x89, 0xf0, 0xf7, 0xd0, 0x89, 0x35, 0x10, 0x30, 376 | 0xac, 0x66, 0xa3, 0x14, 0x30, 0xac, 0x66, 0x8d, 0x65, 0xf4, 0x5b, 0x5e, 377 | 0x5f, 0x5d, 0xc3, 0x90, 0xb8, 0xb0, 0x19, 0xbf, 0x44, 0xbe, 0x4f, 0xe6, 378 | 0x40, 0xbb, 0xeb, 0xe0, 0x8d, 0x74, 0x26, 0x00, 0x55, 0x89, 0xe5, 0x83, 379 | 0xec, 0x28, 0x8b, 0x45, 0x04, 0x8d, 0x55, 0x04, 0x89, 0x15, 0x04, 0x51, 380 | 0xac, 0x66, 0xc7, 0x05, 0x20, 0x53, 0xac, 0x66, 0x09, 0x04, 0x00, 0xc0, 381 | 0xc7, 0x05, 0x24, 0x53, 0xac, 0x66, 0x01, 0x00, 0x00, 0x00, 0xa3, 0xf8, 382 | 0x50, 0xac, 0x66, 0xa3, 0x2c, 0x53, 0xac, 0x66, 0x8b, 0x45, 0x08, 0xc7, 383 | 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0xa3, 0xec, 0x50, 0xac, 0x66, 0xa1, 384 | 0x10, 0x30, 0xac, 0x66, 0x89, 0x45, 0xf0, 0xa1, 0x14, 0x30, 0xac, 0x66, 385 | 0x89, 0x45, 0xf4, 0xff, 0x15, 0x10, 0x71, 0xac, 0x66, 0x83, 0xec, 0x04, 386 | 0xc7, 0x04, 0x24, 0x38, 0x41, 0xac, 0x66, 0xff, 0x15, 0x20, 0x71, 0xac, 387 | 0x66, 0x83, 0xec, 0x04, 0xff, 0x15, 0xe0, 0x70, 0xac, 0x66, 0xc7, 0x44, 388 | 0x24, 0x04, 0x09, 0x04, 0x00, 0xc0, 0x89, 0x04, 0x24, 0xff, 0x15, 0x18, 389 | 0x71, 0xac, 0x66, 0x83, 0xec, 0x08, 0xe8, 0x21, 0x06, 0x00, 0x00, 0x90, 390 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x55, 0x89, 0xe5, 0x57, 391 | 0x56, 0x53, 0x83, 0xec, 0x1c, 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 392 | 0xff, 0x15, 0xdc, 0x70, 0xac, 0x66, 0x8b, 0x3d, 0x80, 0x53, 0xac, 0x66, 393 | 0x8b, 0x35, 0xec, 0x70, 0xac, 0x66, 0x83, 0xec, 0x04, 0x85, 0xff, 0x74, 394 | 0x30, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x07, 0x89, 0x04, 395 | 0x24, 0xff, 0x15, 0x1c, 0x71, 0xac, 0x66, 0x83, 0xec, 0x04, 0x89, 0xc3, 396 | 0xff, 0xd6, 0x85, 0xc0, 0x75, 0x0c, 0x85, 0xdb, 0x74, 0x08, 0x8b, 0x47, 397 | 0x04, 0x89, 0x1c, 0x24, 0xff, 0xd0, 0x8b, 0x7f, 0x08, 0x85, 0xff, 0x75, 398 | 0xd7, 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 0xff, 0x15, 0x04, 0x71, 399 | 0xac, 0x66, 0x83, 0xec, 0x04, 0x8d, 0x65, 0xf4, 0x5b, 0x5e, 0x5f, 0x5d, 400 | 0xc3, 0xeb, 0x0d, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 401 | 0x90, 0x90, 0x90, 0x90, 0x55, 0x89, 0xe5, 0x56, 0x31, 0xf6, 0x53, 0x83, 402 | 0xec, 0x10, 0xa1, 0x84, 0x53, 0xac, 0x66, 0x85, 0xc0, 0x75, 0x0d, 0x8d, 403 | 0x65, 0xf8, 0x89, 0xf0, 0x5b, 0x5e, 0x5d, 0xc3, 0x8d, 0x74, 0x26, 0x00, 404 | 0xc7, 0x44, 0x24, 0x04, 0x0c, 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x01, 405 | 0x00, 0x00, 0x00, 0xe8, 0x74, 0x05, 0x00, 0x00, 0x85, 0xc0, 0x89, 0xc3, 406 | 0x74, 0x42, 0x8b, 0x45, 0x08, 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 407 | 0x89, 0x03, 0x8b, 0x45, 0x0c, 0x89, 0x43, 0x04, 0xff, 0x15, 0xdc, 0x70, 408 | 0xac, 0x66, 0xa1, 0x80, 0x53, 0xac, 0x66, 0x89, 0x1d, 0x80, 0x53, 0xac, 409 | 0x66, 0x89, 0x43, 0x08, 0x83, 0xec, 0x04, 0xc7, 0x04, 0x24, 0x88, 0x53, 410 | 0xac, 0x66, 0xff, 0x15, 0x04, 0x71, 0xac, 0x66, 0x89, 0xf0, 0x83, 0xec, 411 | 0x04, 0x8d, 0x65, 0xf8, 0x5b, 0x5e, 0x5d, 0xc3, 0xbe, 0xff, 0xff, 0xff, 412 | 0xff, 0xeb, 0x90, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xbc, 0x27, 413 | 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xe5, 0x53, 0x83, 0xec, 0x14, 0xa1, 414 | 0x84, 0x53, 0xac, 0x66, 0x8b, 0x5d, 0x08, 0x85, 0xc0, 0x75, 0x0d, 0x31, 415 | 0xc0, 0x8b, 0x5d, 0xfc, 0xc9, 0xc3, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 416 | 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 0xff, 0x15, 0xdc, 0x70, 0xac, 417 | 0x66, 0x8b, 0x15, 0x80, 0x53, 0xac, 0x66, 0x83, 0xec, 0x04, 0x85, 0xd2, 418 | 0x74, 0x17, 0x8b, 0x02, 0x39, 0xd8, 0x75, 0x0a, 0xeb, 0x46, 0x8b, 0x08, 419 | 0x39, 0xd9, 0x74, 0x20, 0x89, 0xc2, 0x8b, 0x42, 0x08, 0x85, 0xc0, 0x75, 420 | 0xf1, 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 0xff, 0x15, 0x04, 0x71, 421 | 0xac, 0x66, 0x83, 0xec, 0x04, 0x31, 0xc0, 0x8b, 0x5d, 0xfc, 0xc9, 0xc3, 422 | 0x8b, 0x48, 0x08, 0x89, 0x4a, 0x08, 0x89, 0x04, 0x24, 0xe8, 0x5a, 0x04, 423 | 0x00, 0x00, 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 0xff, 0x15, 0x04, 424 | 0x71, 0xac, 0x66, 0x83, 0xec, 0x04, 0xeb, 0xd9, 0x8b, 0x42, 0x08, 0xa3, 425 | 0x80, 0x53, 0xac, 0x66, 0x89, 0xd0, 0xeb, 0xda, 0x8d, 0xb6, 0x00, 0x00, 426 | 0x00, 0x00, 0x8d, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xe5, 0x83, 427 | 0xec, 0x18, 0x8b, 0x45, 0x0c, 0x83, 0xf8, 0x01, 0x74, 0x46, 0x72, 0x15, 428 | 0x83, 0xf8, 0x03, 0x75, 0x09, 0xa1, 0x84, 0x53, 0xac, 0x66, 0x85, 0xc0, 429 | 0x75, 0x64, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xc9, 0xc3, 0xa1, 0x84, 0x53, 430 | 0xac, 0x66, 0x85, 0xc0, 0x75, 0x62, 0xa1, 0x84, 0x53, 0xac, 0x66, 0x83, 431 | 0xf8, 0x01, 0x75, 0xe6, 0xc7, 0x05, 0x84, 0x53, 0xac, 0x66, 0x00, 0x00, 432 | 0x00, 0x00, 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 0xff, 0x15, 0xd8, 433 | 0x70, 0xac, 0x66, 0x83, 0xec, 0x04, 0xeb, 0xca, 0xa1, 0x84, 0x53, 0xac, 434 | 0x66, 0x85, 0xc0, 0x74, 0x13, 0xc7, 0x05, 0x84, 0x53, 0xac, 0x66, 0x01, 435 | 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xc9, 0xc3, 0x66, 0x90, 436 | 0xc7, 0x04, 0x24, 0x88, 0x53, 0xac, 0x66, 0xff, 0x15, 0x00, 0x71, 0xac, 437 | 0x66, 0x83, 0xec, 0x04, 0xeb, 0xdb, 0xe8, 0xc9, 0xfd, 0xff, 0xff, 0xeb, 438 | 0x95, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xbb, 0xfd, 0xff, 439 | 0xff, 0xeb, 0x97, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 440 | 0x03, 0x40, 0x3c, 0x81, 0x38, 0x50, 0x45, 0x00, 0x00, 0x74, 0x05, 0x31, 441 | 0xc0, 0xc3, 0x66, 0x90, 0x66, 0x81, 0x78, 0x18, 0x0b, 0x01, 0x0f, 0x94, 442 | 0xc0, 0x0f, 0xb6, 0xc0, 0xc3, 0x8d, 0x76, 0x00, 0x8b, 0x44, 0x24, 0x04, 443 | 0x66, 0x81, 0x38, 0x4d, 0x5a, 0x74, 0x05, 0x31, 0xc0, 0xc3, 0x66, 0x90, 444 | 0xeb, 0xce, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xbc, 0x27, 445 | 0x00, 0x00, 0x00, 0x00, 0x56, 0x53, 0x8b, 0x54, 0x24, 0x0c, 0x8b, 0x5c, 446 | 0x24, 0x10, 0x03, 0x52, 0x3c, 0x0f, 0xb7, 0x72, 0x06, 0x0f, 0xb7, 0x42, 447 | 0x14, 0x85, 0xf6, 0x8d, 0x44, 0x02, 0x18, 0x74, 0x1b, 0x31, 0xd2, 0x90, 448 | 0x8b, 0x48, 0x0c, 0x39, 0xd9, 0x77, 0x07, 0x03, 0x48, 0x08, 0x39, 0xcb, 449 | 0x72, 0x0c, 0x83, 0xc2, 0x01, 0x83, 0xc0, 0x28, 0x39, 0xf2, 0x72, 0xe8, 450 | 0x31, 0xc0, 0x5b, 0x5e, 0xc3, 0x8d, 0x76, 0x00, 0x55, 0x57, 0x56, 0x31, 451 | 0xf6, 0x53, 0x83, 0xec, 0x1c, 0x8b, 0x7c, 0x24, 0x30, 0x89, 0x3c, 0x24, 452 | 0xe8, 0x4b, 0x03, 0x00, 0x00, 0x83, 0xf8, 0x08, 0x77, 0x0b, 0x66, 0x81, 453 | 0x3d, 0x00, 0x00, 0xac, 0x66, 0x4d, 0x5a, 0x74, 0x0b, 0x83, 0xc4, 0x1c, 454 | 0x89, 0xf0, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0x90, 0xb8, 0x00, 0x00, 0xac, 455 | 0x66, 0xe8, 0x46, 0xff, 0xff, 0xff, 0x85, 0xc0, 0x74, 0xe7, 0xa1, 0x3c, 456 | 0x00, 0xac, 0x66, 0x8d, 0x90, 0x00, 0x00, 0xac, 0x66, 0x0f, 0xb7, 0x80, 457 | 0x14, 0x00, 0xac, 0x66, 0x0f, 0xb7, 0x6a, 0x06, 0x8d, 0x5c, 0x02, 0x18, 458 | 0x85, 0xed, 0x75, 0x0e, 0xeb, 0xc7, 0x66, 0x90, 0x83, 0xc6, 0x01, 0x83, 459 | 0xc3, 0x28, 0x39, 0xee, 0x73, 0x26, 0xc7, 0x44, 0x24, 0x08, 0x08, 0x00, 460 | 0x00, 0x00, 0x89, 0x7c, 0x24, 0x04, 0x89, 0x1c, 0x24, 0xe8, 0xea, 0x02, 461 | 0x00, 0x00, 0x85, 0xc0, 0x75, 0xde, 0x83, 0xc4, 0x1c, 0x89, 0xde, 0x89, 462 | 0xf0, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0x66, 0x90, 0x83, 0xc4, 0x1c, 0x31, 463 | 0xf6, 0x5b, 0x89, 0xf0, 0x5e, 0x5f, 0x5d, 0xc3, 0x8d, 0x74, 0x26, 0x00, 464 | 0x53, 0x31, 0xdb, 0x83, 0xec, 0x08, 0x66, 0x81, 0x3d, 0x00, 0x00, 0xac, 465 | 0x66, 0x4d, 0x5a, 0x74, 0x07, 0x83, 0xc4, 0x08, 0x89, 0xd8, 0x5b, 0xc3, 466 | 0xb8, 0x00, 0x00, 0xac, 0x66, 0xe8, 0xbe, 0xfe, 0xff, 0xff, 0x85, 0xc0, 467 | 0x74, 0xeb, 0x8b, 0x44, 0x24, 0x10, 0xc7, 0x04, 0x24, 0x00, 0x00, 0xac, 468 | 0x66, 0x2d, 0x00, 0x00, 0xac, 0x66, 0x89, 0x44, 0x24, 0x04, 0xe8, 0xe1, 469 | 0xfe, 0xff, 0xff, 0x83, 0xc4, 0x08, 0x89, 0xc3, 0x89, 0xd8, 0x5b, 0xc3, 470 | 0x90, 0x8d, 0xb4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x53, 0x31, 0xdb, 0x66, 471 | 0x81, 0x3d, 0x00, 0x00, 0xac, 0x66, 0x4d, 0x5a, 0x74, 0x04, 0x89, 0xd8, 472 | 0x5b, 0xc3, 0xb8, 0x00, 0x00, 0xac, 0x66, 0xe8, 0x74, 0xfe, 0xff, 0xff, 473 | 0x85, 0xc0, 0x74, 0xee, 0xa1, 0x3c, 0x00, 0xac, 0x66, 0x0f, 0xb7, 0x98, 474 | 0x06, 0x00, 0xac, 0x66, 0x89, 0xd8, 0x5b, 0xc3, 0x56, 0x31, 0xf6, 0x66, 475 | 0x81, 0x3d, 0x00, 0x00, 0xac, 0x66, 0x4d, 0x5a, 0x53, 0x8b, 0x5c, 0x24, 476 | 0x0c, 0x74, 0x05, 0x89, 0xf0, 0x5b, 0x5e, 0xc3, 0xb8, 0x00, 0x00, 0xac, 477 | 0x66, 0xe8, 0x3e, 0xfe, 0xff, 0xff, 0x85, 0xc0, 0x74, 0xed, 0xa1, 0x3c, 478 | 0x00, 0xac, 0x66, 0x8d, 0x88, 0x00, 0x00, 0xac, 0x66, 0x0f, 0xb7, 0x80, 479 | 0x14, 0x00, 0xac, 0x66, 0x8d, 0x54, 0x01, 0x18, 0x0f, 0xb7, 0x49, 0x06, 480 | 0x85, 0xc9, 0x74, 0xcf, 0x31, 0xc0, 0xf6, 0x42, 0x27, 0x20, 0x74, 0x07, 481 | 0x85, 0xdb, 0x74, 0x14, 0x83, 0xeb, 0x01, 0x83, 0xc0, 0x01, 0x83, 0xc2, 482 | 0x28, 0x39, 0xc8, 0x72, 0xe9, 0x31, 0xf6, 0x89, 0xf0, 0x5b, 0x5e, 0xc3, 483 | 0x89, 0xd6, 0x89, 0xf0, 0x5b, 0x5e, 0xc3, 0x90, 0x8d, 0x74, 0x26, 0x00, 484 | 0x66, 0x81, 0x3d, 0x00, 0x00, 0xac, 0x66, 0x4d, 0x5a, 0x74, 0x05, 0x31, 485 | 0xc0, 0xc3, 0x66, 0x90, 0xb8, 0x00, 0x00, 0xac, 0x66, 0xe8, 0xd6, 0xfd, 486 | 0xff, 0xff, 0x85, 0xc0, 0x74, 0xed, 0xb8, 0x00, 0x00, 0xac, 0x66, 0xc3, 487 | 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xbf, 0x00, 0x00, 0x00, 0x00, 488 | 0x31, 0xc0, 0x66, 0x81, 0x3d, 0x00, 0x00, 0xac, 0x66, 0x4d, 0x5a, 0x74, 489 | 0x03, 0xc3, 0x66, 0x90, 0x83, 0xec, 0x08, 0xb8, 0x00, 0x00, 0xac, 0x66, 490 | 0xe8, 0xa3, 0xfd, 0xff, 0xff, 0x85, 0xc0, 0x74, 0x25, 0x8b, 0x44, 0x24, 491 | 0x0c, 0xc7, 0x04, 0x24, 0x00, 0x00, 0xac, 0x66, 0x2d, 0x00, 0x00, 0xac, 492 | 0x66, 0x89, 0x44, 0x24, 0x04, 0xe8, 0xc6, 0xfd, 0xff, 0xff, 0x85, 0xc0, 493 | 0x74, 0x12, 0x8b, 0x40, 0x24, 0xf7, 0xd0, 0xc1, 0xe8, 0x1f, 0x83, 0xc4, 494 | 0x08, 0xc3, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x31, 0xc0, 0xeb, 0xf2, 495 | 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xbf, 0x00, 0x00, 0x00, 0x00, 496 | 0x57, 0x31, 0xff, 0x56, 0x53, 0x83, 0xec, 0x08, 0x66, 0x81, 0x3d, 0x00, 497 | 0x00, 0xac, 0x66, 0x4d, 0x5a, 0x8b, 0x5c, 0x24, 0x18, 0x74, 0x09, 0x83, 498 | 0xc4, 0x08, 0x89, 0xf8, 0x5b, 0x5e, 0x5f, 0xc3, 0xb8, 0x00, 0x00, 0xac, 499 | 0x66, 0xe8, 0x36, 0xfd, 0xff, 0xff, 0x85, 0xc0, 0x74, 0xe9, 0xa1, 0x3c, 500 | 0x00, 0xac, 0x66, 0x8b, 0xb0, 0x80, 0x00, 0xac, 0x66, 0x85, 0xf6, 0x74, 501 | 0xda, 0x89, 0x74, 0x24, 0x04, 0xc7, 0x04, 0x24, 0x00, 0x00, 0xac, 0x66, 502 | 0xe8, 0x53, 0xfd, 0xff, 0xff, 0x85, 0xc0, 0x74, 0xc6, 0x81, 0xc6, 0x00, 503 | 0x00, 0xac, 0x66, 0x89, 0xf2, 0x75, 0x0b, 0xeb, 0xba, 0x8d, 0x76, 0x00, 504 | 0x83, 0xeb, 0x01, 0x83, 0xc2, 0x14, 0x8b, 0x4a, 0x04, 0x85, 0xc9, 0x75, 505 | 0x07, 0x8b, 0x42, 0x0c, 0x85, 0xc0, 0x74, 0x1c, 0x85, 0xdb, 0x7f, 0xe8, 506 | 0x8b, 0x7a, 0x0c, 0x83, 0xc4, 0x08, 0x5b, 0x5e, 0x81, 0xc7, 0x00, 0x00, 507 | 0xac, 0x66, 0x89, 0xf8, 0x5f, 0xc3, 0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, 508 | 0x83, 0xc4, 0x08, 0x31, 0xff, 0x89, 0xf8, 0x5b, 0x5e, 0x5f, 0xc3, 0x90, 509 | 0x90, 0x90, 0x90, 0x90, 0x51, 0x50, 0x3d, 0x00, 0x10, 0x00, 0x00, 0x8d, 510 | 0x4c, 0x24, 0x0c, 0x72, 0x15, 0x81, 0xe9, 0x00, 0x10, 0x00, 0x00, 0x83, 511 | 0x09, 0x00, 0x2d, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x00, 0x10, 0x00, 0x00, 512 | 0x77, 0xeb, 0x29, 0xc1, 0x83, 0x09, 0x00, 0x58, 0x59, 0xc3, 0x90, 0x90, 513 | 0x66, 0x90, 0x66, 0x90, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xc2, 0x0c, 0x00, 514 | 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xff, 0x25, 0x54, 0x71, 515 | 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x34, 0x71, 0xac, 0x66, 0x90, 0x90, 516 | 0xff, 0x25, 0x4c, 0x71, 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x38, 0x71, 517 | 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x40, 0x71, 0xac, 0x66, 0x90, 0x90, 518 | 0xff, 0x25, 0x30, 0x71, 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x64, 0x71, 519 | 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x50, 0x71, 0xac, 0x66, 0x90, 0x90, 520 | 0xff, 0x25, 0x6c, 0x71, 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x68, 0x71, 521 | 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x58, 0x71, 0xac, 0x66, 0x90, 0x90, 522 | 0xff, 0x25, 0x48, 0x71, 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x5c, 0x71, 523 | 0xac, 0x66, 0x90, 0x90, 0xff, 0x25, 0x60, 0x71, 0xac, 0x66, 0x90, 0x90, 524 | 0xff, 0x25, 0x08, 0x71, 0xac, 0x66, 0x90, 0x90, 0x66, 0x90, 0x66, 0x90, 525 | 0x66, 0x90, 0x66, 0x90, 0x55, 0x89, 0xe5, 0x83, 0xec, 0x18, 0xe8, 0xe5, 526 | 0xef, 0xff, 0xff, 0xc7, 0x04, 0x24, 0xb0, 0x14, 0xac, 0x66, 0xe8, 0x19, 527 | 0xf1, 0xff, 0xff, 0xc9, 0xc3, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 528 | 0xff, 0xff, 0xff, 0xff, 0x70, 0x24, 0xac, 0x66, 0x00, 0x00, 0x00, 0x00, 529 | 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 530 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 531 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 532 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 533 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 534 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 535 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 536 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 537 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 538 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 539 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 540 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 541 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 542 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 543 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 544 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 545 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 546 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 547 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 548 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 549 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 550 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 551 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 552 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 553 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 554 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 555 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 556 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 557 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 558 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 559 | 0xff, 0xff, 0xff, 0xff, 0xa0, 0x24, 0xac, 0x66, 0x02, 0x00, 0x00, 0x00, 560 | 0x4e, 0xe6, 0x40, 0xbb, 0xb1, 0x19, 0xbf, 0x44, 0x00, 0x00, 0x00, 0x00, 561 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 562 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 563 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 564 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 565 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 566 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 567 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 568 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 569 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 570 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 571 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 572 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 573 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 574 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 575 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 576 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 577 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 578 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 579 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 580 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 581 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 582 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 583 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 584 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 585 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 586 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 587 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 588 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 589 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 590 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 591 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 592 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 593 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 594 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 595 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 596 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 597 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 598 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 599 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 600 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 601 | 0x00, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x62, 0x67, 0x63, 0x6a, 0x2d, 0x31, 602 | 0x33, 0x2e, 0x64, 0x6c, 0x6c, 0x00, 0x5f, 0x4a, 0x76, 0x5f, 0x52, 0x65, 603 | 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 604 | 0x73, 0x00, 0x00, 0x00, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 605 | 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x2e, 0x64, 606 | 0x6c, 0x6c, 0x00, 0x00, 0x10, 0x16, 0xac, 0x66, 0x4d, 0x69, 0x6e, 0x67, 607 | 0x77, 0x2d, 0x77, 0x36, 0x34, 0x20, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 608 | 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x3a, 0x0a, 0x00, 609 | 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x25, 0x70, 0x20, 0x68, 610 | 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2d, 611 | 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x20, 0x20, 0x56, 0x69, 612 | 0x72, 0x74, 0x75, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x20, 0x66, 613 | 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x25, 0x64, 614 | 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x61, 0x74, 0x20, 0x61, 0x64, 615 | 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x25, 0x70, 0x00, 0x00, 0x00, 0x00, 616 | 0x20, 0x20, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x72, 0x6f, 617 | 0x74, 0x65, 0x63, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 618 | 0x77, 0x69, 0x74, 0x68, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x30, 0x78, 619 | 0x25, 0x78, 0x00, 0x00, 0x20, 0x20, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 620 | 0x6e, 0x20, 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 0x20, 0x72, 0x65, 0x6c, 621 | 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x74, 622 | 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 623 | 0x20, 0x25, 0x64, 0x2e, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x20, 0x55, 0x6e, 624 | 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x70, 0x73, 0x65, 0x75, 0x64, 0x6f, 625 | 0x20, 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 626 | 0x62, 0x69, 0x74, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x2e, 627 | 0x0a, 0x00, 0x00, 0x00, 0x20, 0x53, 0xac, 0x66, 0x40, 0x50, 0xac, 0x66, 628 | 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 629 | 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 630 | 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 0x2e, 0x39, 0x2e, 0x31, 631 | 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 632 | 0x55, 0x29, 0x20, 0x34, 0x2e, 0x39, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 633 | 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 634 | 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 635 | 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 636 | 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 637 | 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 638 | 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 639 | 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 640 | 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 641 | 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 642 | 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 643 | 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 644 | 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 645 | 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 646 | 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 647 | 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 648 | 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 649 | 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 650 | 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 651 | 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 652 | 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 0x00, 0x00, 0x00, 0x00, 653 | 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 654 | 0x2e, 0x39, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 655 | 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x34, 0x2e, 0x38, 0x2e, 0x32, 656 | 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 657 | 0x55, 0x29, 0x20, 0x34, 0x2e, 0x39, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 658 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 659 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 660 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 661 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 662 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 663 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 664 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 665 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 666 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 667 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 668 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 669 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 670 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 671 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 672 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 673 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 674 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 675 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 676 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 677 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 678 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 679 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 680 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 681 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 682 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 683 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 684 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 685 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 686 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 687 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x60, 0x00, 0x00, 688 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 689 | 0x28, 0x60, 0x00, 0x00, 0x28, 0x60, 0x00, 0x00, 0x28, 0x60, 0x00, 0x00, 690 | 0x64, 0x64, 0x72, 0x61, 0x77, 0x2e, 0x64, 0x6c, 0x6c, 0x00, 0x00, 0x00, 691 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 692 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 693 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 694 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 695 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 696 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 697 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 698 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 699 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 700 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 701 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 702 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 703 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 704 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 705 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 706 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 707 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 708 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 709 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 710 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 711 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 712 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 713 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 714 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 715 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 716 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 717 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 718 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 719 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 720 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 721 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 722 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 723 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 724 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 725 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 726 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 727 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 728 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 729 | 0x00, 0x00, 0x00, 0x00, 0x3c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 730 | 0x00, 0x00, 0x00, 0x00, 0x20, 0x74, 0x00, 0x00, 0xd8, 0x70, 0x00, 0x00, 731 | 0x94, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 732 | 0x70, 0x74, 0x00, 0x00, 0x30, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 733 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 734 | 0x00, 0x00, 0x00, 0x00, 0x74, 0x71, 0x00, 0x00, 0x8c, 0x71, 0x00, 0x00, 735 | 0xa4, 0x71, 0x00, 0x00, 0xb8, 0x71, 0x00, 0x00, 0xce, 0x71, 0x00, 0x00, 736 | 0xe4, 0x71, 0x00, 0x00, 0xf4, 0x71, 0x00, 0x00, 0x08, 0x72, 0x00, 0x00, 737 | 0x1a, 0x72, 0x00, 0x00, 0x34, 0x72, 0x00, 0x00, 0x44, 0x72, 0x00, 0x00, 738 | 0x60, 0x72, 0x00, 0x00, 0x78, 0x72, 0x00, 0x00, 0x88, 0x72, 0x00, 0x00, 739 | 0xa2, 0x72, 0x00, 0x00, 0xc0, 0x72, 0x00, 0x00, 0xc8, 0x72, 0x00, 0x00, 740 | 0xdc, 0x72, 0x00, 0x00, 0xea, 0x72, 0x00, 0x00, 0x06, 0x73, 0x00, 0x00, 741 | 0x18, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x73, 0x00, 0x00, 742 | 0x36, 0x73, 0x00, 0x00, 0x44, 0x73, 0x00, 0x00, 0x50, 0x73, 0x00, 0x00, 743 | 0x58, 0x73, 0x00, 0x00, 0x60, 0x73, 0x00, 0x00, 0x6a, 0x73, 0x00, 0x00, 744 | 0x74, 0x73, 0x00, 0x00, 0x7c, 0x73, 0x00, 0x00, 0x86, 0x73, 0x00, 0x00, 745 | 0x90, 0x73, 0x00, 0x00, 0x9a, 0x73, 0x00, 0x00, 0xa4, 0x73, 0x00, 0x00, 746 | 0xae, 0x73, 0x00, 0x00, 0xb8, 0x73, 0x00, 0x00, 0xc0, 0x73, 0x00, 0x00, 747 | 0x00, 0x00, 0x00, 0x00, 0x74, 0x71, 0x00, 0x00, 0x8c, 0x71, 0x00, 0x00, 748 | 0xa4, 0x71, 0x00, 0x00, 0xb8, 0x71, 0x00, 0x00, 0xce, 0x71, 0x00, 0x00, 749 | 0xe4, 0x71, 0x00, 0x00, 0xf4, 0x71, 0x00, 0x00, 0x08, 0x72, 0x00, 0x00, 750 | 0x1a, 0x72, 0x00, 0x00, 0x34, 0x72, 0x00, 0x00, 0x44, 0x72, 0x00, 0x00, 751 | 0x60, 0x72, 0x00, 0x00, 0x78, 0x72, 0x00, 0x00, 0x88, 0x72, 0x00, 0x00, 752 | 0xa2, 0x72, 0x00, 0x00, 0xc0, 0x72, 0x00, 0x00, 0xc8, 0x72, 0x00, 0x00, 753 | 0xdc, 0x72, 0x00, 0x00, 0xea, 0x72, 0x00, 0x00, 0x06, 0x73, 0x00, 0x00, 754 | 0x18, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x73, 0x00, 0x00, 755 | 0x36, 0x73, 0x00, 0x00, 0x44, 0x73, 0x00, 0x00, 0x50, 0x73, 0x00, 0x00, 756 | 0x58, 0x73, 0x00, 0x00, 0x60, 0x73, 0x00, 0x00, 0x6a, 0x73, 0x00, 0x00, 757 | 0x74, 0x73, 0x00, 0x00, 0x7c, 0x73, 0x00, 0x00, 0x86, 0x73, 0x00, 0x00, 758 | 0x90, 0x73, 0x00, 0x00, 0x9a, 0x73, 0x00, 0x00, 0xa4, 0x73, 0x00, 0x00, 759 | 0xae, 0x73, 0x00, 0x00, 0xb8, 0x73, 0x00, 0x00, 0xc0, 0x73, 0x00, 0x00, 760 | 0x00, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 761 | 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x74, 762 | 0x69, 0x6f, 0x6e, 0x00, 0xef, 0x00, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x43, 763 | 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x74, 0x69, 764 | 0x6f, 0x6e, 0x00, 0x00, 0xc4, 0x01, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 765 | 0x72, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x00, 766 | 0xc5, 0x01, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 767 | 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x64, 0x00, 0xc9, 0x01, 768 | 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x68, 769 | 0x72, 0x65, 0x61, 0x64, 0x49, 0x64, 0x00, 0x00, 0x03, 0x02, 0x47, 0x65, 770 | 0x74, 0x4c, 0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x00, 771 | 0x15, 0x02, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x48, 772 | 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x41, 0x00, 0x00, 0x45, 0x02, 0x47, 0x65, 773 | 0x74, 0x50, 0x72, 0x6f, 0x63, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 774 | 0x00, 0x00, 0x7b, 0x02, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 775 | 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x54, 776 | 0x69, 0x6d, 0x65, 0x00, 0x97, 0x02, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 777 | 0x6b, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x00, 0x00, 0xeb, 0x02, 0x49, 0x6e, 778 | 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x43, 0x72, 0x69, 0x74, 779 | 0x69, 0x63, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 780 | 0x26, 0x03, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x43, 0x72, 0x69, 0x74, 0x69, 781 | 0x63, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 782 | 0x29, 0x03, 0x4c, 0x6f, 0x61, 0x64, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 783 | 0x79, 0x41, 0x00, 0x00, 0x93, 0x03, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 784 | 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x6f, 785 | 0x75, 0x6e, 0x74, 0x65, 0x72, 0x00, 0x67, 0x04, 0x53, 0x65, 0x74, 0x55, 786 | 0x6e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 787 | 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x00, 788 | 0x74, 0x04, 0x53, 0x6c, 0x65, 0x65, 0x70, 0x00, 0x82, 0x04, 0x54, 0x65, 789 | 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 790 | 0x73, 0x73, 0x00, 0x00, 0x89, 0x04, 0x54, 0x6c, 0x73, 0x47, 0x65, 0x74, 791 | 0x56, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x96, 0x04, 0x55, 0x6e, 0x68, 0x61, 792 | 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 793 | 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x00, 0x00, 0xb6, 0x04, 794 | 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x65, 795 | 0x63, 0x74, 0x00, 0x00, 0xb9, 0x04, 0x56, 0x69, 0x72, 0x74, 0x75, 0x61, 796 | 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x00, 0x00, 0x37, 0x00, 0x5f, 0x5f, 797 | 0x64, 0x6c, 0x6c, 0x6f, 0x6e, 0x65, 0x78, 0x69, 0x74, 0x00, 0x8e, 0x00, 798 | 0x5f, 0x61, 0x6d, 0x73, 0x67, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x00, 0x00, 799 | 0x31, 0x01, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x74, 0x65, 0x72, 0x6d, 0x00, 800 | 0x35, 0x01, 0x5f, 0x69, 0x6f, 0x62, 0x00, 0x00, 0x96, 0x01, 0x5f, 0x6c, 801 | 0x6f, 0x63, 0x6b, 0x00, 0x33, 0x02, 0x5f, 0x6f, 0x6e, 0x65, 0x78, 0x69, 802 | 0x74, 0x00, 0x4b, 0x03, 0x63, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x00, 0x00, 803 | 0x6c, 0x03, 0x66, 0x72, 0x65, 0x65, 0x00, 0x00, 0x77, 0x03, 0x66, 0x77, 804 | 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0xa3, 0x03, 0x6d, 0x61, 0x6c, 0x6c, 805 | 0x6f, 0x63, 0x00, 0x00, 0xab, 0x03, 0x6d, 0x65, 0x6d, 0x63, 0x70, 0x79, 806 | 0x00, 0x00, 0xd8, 0x03, 0x73, 0x74, 0x72, 0x6c, 0x65, 0x6e, 0x00, 0x00, 807 | 0xdb, 0x03, 0x73, 0x74, 0x72, 0x6e, 0x63, 0x6d, 0x70, 0x00, 0xef, 0x03, 808 | 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x00, 0x40, 0x04, 0x61, 0x62, 809 | 0x6f, 0x72, 0x74, 0x00, 0x5c, 0x04, 0x76, 0x66, 0x70, 0x72, 0x69, 0x6e, 810 | 0x74, 0x66, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 811 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 812 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 813 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 814 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 815 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 816 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 817 | 0x00, 0x70, 0x00, 0x00, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x33, 0x32, 818 | 0x2e, 0x64, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 819 | 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 820 | 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 821 | 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 822 | 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 823 | 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 0x14, 0x70, 0x00, 0x00, 824 | 0x6d, 0x73, 0x76, 0x63, 0x72, 0x74, 0x2e, 0x64, 0x6c, 0x6c, 0x00, 0x00, 825 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 826 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 827 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 828 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 829 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 830 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 831 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 832 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 833 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 834 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 835 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 836 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 837 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 838 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 839 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 840 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 841 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 842 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 843 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 844 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 845 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 846 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 847 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 848 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 849 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 850 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 851 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 852 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 853 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 854 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 855 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 856 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 857 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 858 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xac, 0x66, 0x00, 0x00, 0x00, 0x00, 859 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x16, 0xac, 0x66, 0xc0, 0x15, 0xac, 0x66, 860 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 861 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 862 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 863 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 864 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 865 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 866 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 867 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 868 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 869 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 870 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 871 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 872 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 873 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 874 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 875 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 876 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 877 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 878 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 879 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 880 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 881 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 882 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 883 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 884 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 885 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 886 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 887 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 888 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 889 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 890 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 891 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 892 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 893 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 894 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 895 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 896 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 897 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 898 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 899 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 900 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xac, 0x66, 0x1c, 0x90, 0xac, 0x66, 901 | 0x10, 0x50, 0xac, 0x66, 0x18, 0x80, 0xac, 0x66, 0x00, 0x00, 0x00, 0x00, 902 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 903 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 904 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 905 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 906 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 907 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 908 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 909 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 910 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 911 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 912 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 913 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 914 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 915 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 916 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 917 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 918 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 919 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 920 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 921 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 922 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 923 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 924 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 925 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 926 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 927 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 928 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 929 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 930 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 931 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 932 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 933 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 934 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 935 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 936 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 937 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 938 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 939 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 940 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 941 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 942 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 943 | 0x4c, 0x01, 0x00, 0x00, 0x1d, 0x30, 0x22, 0x30, 0x51, 0x30, 0x64, 0x30, 944 | 0x69, 0x30, 0x87, 0x30, 0x90, 0x30, 0xcf, 0x30, 0xef, 0x30, 0xfa, 0x30, 945 | 0x08, 0x31, 0x15, 0x31, 0x2b, 0x31, 0x4f, 0x31, 0x71, 0x31, 0x84, 0x31, 946 | 0xb2, 0x31, 0xbc, 0x31, 0xc8, 0x31, 0xd2, 0x31, 0xf2, 0x31, 0x04, 0x32, 947 | 0x0b, 0x32, 0x11, 0x32, 0x38, 0x32, 0x3f, 0x32, 0x4a, 0x32, 0x73, 0x32, 948 | 0x7a, 0x32, 0xb3, 0x32, 0x29, 0x34, 0x61, 0x34, 0x72, 0x34, 0x78, 0x34, 949 | 0x8c, 0x34, 0x95, 0x34, 0xa5, 0x34, 0xcf, 0x34, 0xd5, 0x34, 0xe5, 0x34, 950 | 0x0b, 0x35, 0x1c, 0x35, 0x57, 0x35, 0x6f, 0x35, 0x89, 0x35, 0x16, 0x36, 951 | 0x23, 0x36, 0x42, 0x36, 0x48, 0x36, 0x5d, 0x36, 0xc5, 0x36, 0xe0, 0x36, 952 | 0xfd, 0x36, 0x30, 0x37, 0x41, 0x37, 0x82, 0x37, 0xa7, 0x37, 0xb1, 0x37, 953 | 0xe5, 0x37, 0x35, 0x38, 0x4d, 0x38, 0xc2, 0x38, 0xe1, 0x38, 0xf5, 0x38, 954 | 0xfc, 0x38, 0x18, 0x39, 0x2a, 0x39, 0x3e, 0x39, 0x56, 0x39, 0x61, 0x39, 955 | 0x7b, 0x39, 0x9c, 0x39, 0xae, 0x39, 0xb3, 0x39, 0xb8, 0x39, 0xcf, 0x39, 956 | 0xdc, 0x39, 0xea, 0x39, 0xef, 0x39, 0xfc, 0x39, 0x11, 0x3a, 0x1e, 0x3a, 957 | 0x24, 0x3a, 0x51, 0x3a, 0x6a, 0x3a, 0x70, 0x3a, 0x8d, 0x3a, 0x93, 0x3a, 958 | 0xa7, 0x3a, 0xae, 0x3a, 0xc5, 0x3a, 0xcc, 0x3a, 0xf9, 0x3a, 0x10, 0x3b, 959 | 0x2a, 0x3b, 0x34, 0x3b, 0x55, 0x3b, 0x74, 0x3b, 0x92, 0x3b, 0xb9, 0x3b, 960 | 0xde, 0x3b, 0x00, 0x3c, 0x16, 0x3c, 0x2a, 0x3c, 0x41, 0x3c, 0x53, 0x3c, 961 | 0x5f, 0x3c, 0x76, 0x3c, 0x86, 0x3c, 0x95, 0x3c, 0xaf, 0x3c, 0xc2, 0x3c, 962 | 0xd2, 0x3c, 0xea, 0x3c, 0x06, 0x3d, 0x1a, 0x3d, 0x2b, 0x3d, 0x33, 0x3d, 963 | 0x3c, 0x3d, 0x4b, 0x3d, 0x6e, 0x3d, 0x73, 0x3d, 0x9e, 0x3d, 0xa4, 0x3d, 964 | 0xae, 0x3d, 0xb7, 0x3d, 0xbc, 0x3d, 0xcb, 0x3d, 0xd0, 0x3d, 0xd8, 0x3d, 965 | 0xe1, 0x3d, 0xeb, 0x3d, 0xf1, 0x3d, 0xfa, 0x3d, 0x0b, 0x3e, 0x2c, 0x3e, 966 | 0x32, 0x3e, 0x38, 0x3e, 0x3e, 0x3e, 0x57, 0x3e, 0x7c, 0x3e, 0x82, 0x3e, 967 | 0xab, 0x3e, 0xe0, 0x3e, 0xee, 0x3e, 0xf3, 0x3e, 0xf9, 0x3e, 0x06, 0x3f, 968 | 0x0c, 0x3f, 0x38, 0x3f, 0x53, 0x3f, 0x59, 0x3f, 0x5f, 0x3f, 0x84, 0x3f, 969 | 0x8a, 0x3f, 0xa9, 0x3f, 0xaf, 0x3f, 0xbc, 0x3f, 0xe6, 0x3f, 0xf6, 0x3f, 970 | 0xff, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 971 | 0x0a, 0x30, 0x15, 0x30, 0x1b, 0x30, 0x25, 0x30, 0x2f, 0x30, 0x43, 0x30, 972 | 0x49, 0x30, 0x0d, 0x31, 0x21, 0x31, 0x2f, 0x31, 0x35, 0x31, 0x3c, 0x31, 973 | 0x99, 0x31, 0xa9, 0x31, 0xbd, 0x31, 0xc2, 0x31, 0xe6, 0x31, 0xf3, 0x31, 974 | 0x01, 0x32, 0x08, 0x32, 0x16, 0x32, 0x29, 0x32, 0x37, 0x32, 0x3d, 0x32, 975 | 0x44, 0x32, 0x83, 0x32, 0x91, 0x32, 0x9f, 0x32, 0xb5, 0x32, 0xc4, 0x32, 976 | 0xd8, 0x32, 0xdd, 0x32, 0x1b, 0x33, 0x31, 0x33, 0x3f, 0x33, 0x45, 0x33, 977 | 0x54, 0x33, 0x63, 0x33, 0x92, 0x33, 0xf2, 0x33, 0xfa, 0x33, 0x02, 0x34, 978 | 0x0a, 0x34, 0x12, 0x34, 0x1a, 0x34, 0x22, 0x34, 0x2a, 0x34, 0x32, 0x34, 979 | 0x3a, 0x34, 0x42, 0x34, 0x4a, 0x34, 0x52, 0x34, 0x5a, 0x34, 0x62, 0x34, 980 | 0x7e, 0x34, 0x94, 0x34, 0x00, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 981 | 0x08, 0x30, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 982 | 0x3c, 0x30, 0x38, 0x31, 0x3c, 0x31, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 983 | 0x10, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x18, 0x30, 0x1c, 0x30, 0x00, 0x00, 984 | 0x00, 0x90, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x30, 0x08, 0x30, 985 | 0x0c, 0x30, 0x10, 0x30, 986 | } 987 | -------------------------------------------------------------------------------- /download/cmd/download.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "path" 7 | "time" 8 | "sync" 9 | "bufio" 10 | "bytes" 11 | "errors" 12 | "runtime" 13 | "net/url" 14 | "os/exec" 15 | "io/ioutil" 16 | "crypto/md5" 17 | "encoding/json" 18 | "github.com/cheggaaa/pb" 19 | "aaronlindsay.com/go/pkg/pso2/download" 20 | "aaronlindsay.com/go/pkg/pso2/util" 21 | ) 22 | 23 | const ( 24 | PathPatchlist = "patchlist.txt" 25 | PathPatchlistOld = "patchlist-old.txt" 26 | PathPatchlistInstalled = "patchlist-installed.txt" 27 | PathVersion = "version.ver" 28 | PathVersionInstalled = "version-installed.ver" 29 | PathTranslateDll = "translate.dll" 30 | PathTranslationBin = "translation.bin" 31 | PathEnglishDb = "english.db" 32 | PathTranslationCfg = "translation.cfg" 33 | EnglishUpdateURL = "http://aaronlindsay.com/pso2/download.json" 34 | ) 35 | 36 | func PathScratch(pso2path string) string { 37 | return path.Join(pso2path, "download") 38 | } 39 | 40 | func CommitInstalled(pso2path string, patchlist *download.PatchList) error { 41 | scratch := PathScratch(pso2path) 42 | 43 | pathPatchlistInstalled := path.Join(scratch, PathPatchlistInstalled) 44 | f, err := os.Create(pathPatchlistInstalled) 45 | if err != nil { 46 | return err 47 | } 48 | err = patchlist.Write(f) 49 | f.Close() 50 | if err != nil { 51 | return err 52 | } 53 | 54 | pathVersionInstalled := path.Join(scratch, PathVersionInstalled) 55 | err = util.CopyFile(path.Join(scratch, PathVersion), pathVersionInstalled) 56 | 57 | return err 58 | } 59 | 60 | func LoadVersion(r io.Reader) (string, error) { 61 | ver, err := ioutil.ReadAll(r) 62 | if err != nil { 63 | return "", err 64 | } 65 | return string(ver), nil 66 | } 67 | 68 | func DownloadProductionVersion() (s string, err error) { 69 | resp, err := download.Request(download.ProductionVersion) 70 | if err != nil { 71 | return 72 | } 73 | 74 | s, err = LoadVersion(resp.Body) 75 | resp.Body.Close() 76 | return 77 | } 78 | 79 | func LoadVersionFile(filename string) (s string, err error) { 80 | f, err := os.Open(filename) 81 | if err != nil { 82 | return 83 | } 84 | 85 | s, err = LoadVersion(f) 86 | f.Close() 87 | return 88 | } 89 | 90 | func LoadPatchlistFile(filename, urlStr string) (p *download.PatchList, err error) { 91 | f, err := os.Open(filename) 92 | if err != nil { 93 | return 94 | } 95 | 96 | p, err = download.ParseListCap(bufio.NewReader(f), urlStr, 20000) 97 | f.Close() 98 | return 99 | } 100 | 101 | func LoadPatchlist(pso2path string) (patchlist *download.PatchList, err error) { 102 | scratch := PathScratch(pso2path) 103 | 104 | patchlist, err = LoadPatchlistFile(path.Join(scratch, PathPatchlist), download.ProductionPatchlist) 105 | if err != nil { 106 | return 107 | } 108 | 109 | patchlistOld, err := LoadPatchlistFile(path.Join(scratch, PathPatchlistOld), download.ProductionPatchlistOld) 110 | if err != nil { 111 | return 112 | } 113 | 114 | patchlist = patchlist.MergeOld(patchlistOld) 115 | 116 | return 117 | } 118 | 119 | func DownloadPatchlist(pso2path, version string) (patchlist *download.PatchList, err error) { 120 | scratch := PathScratch(pso2path) 121 | 122 | pathPatchlist := path.Join(scratch, PathPatchlist) 123 | pathPatchlistOld := path.Join(scratch, PathPatchlistOld) 124 | pathVersion := path.Join(scratch, PathVersion) 125 | 126 | patchlist, err = download.DownloadList(download.ProductionPatchlist) 127 | if err != nil { 128 | return 129 | } 130 | 131 | patchlistOld, err := download.DownloadList(download.ProductionPatchlistOld) 132 | if err != nil { 133 | return 134 | } 135 | 136 | launcherlist, err := download.DownloadList(download.ProductionLauncherlist) 137 | if err != nil { 138 | return 139 | } 140 | 141 | patchlist.Append(launcherlist) 142 | 143 | f, err := os.Create(pathPatchlist) 144 | if err != nil { 145 | return 146 | } 147 | err = patchlist.Write(f) 148 | f.Close() 149 | if err != nil { 150 | return 151 | } 152 | 153 | f, err = os.Create(pathPatchlistOld) 154 | if err != nil { 155 | return 156 | } 157 | err = patchlistOld.Write(f) 158 | f.Close() 159 | if err != nil { 160 | return 161 | } 162 | 163 | patchlist = patchlist.MergeOld(patchlistOld) 164 | 165 | err = ioutil.WriteFile(pathVersion, []byte(version), 0666) 166 | 167 | return 168 | } 169 | 170 | func DownloadEnglishFiles(pso2path string) (translationChanged bool, err error) { 171 | resp, err := download.Request(EnglishUpdateURL) 172 | if err != nil { 173 | return 174 | } 175 | 176 | var data struct { 177 | ItemTimestamp, EnglishTimestamp, TranslationTimestamp int64 178 | ItemURL, EnglishURL, TranslationURL string 179 | } 180 | 181 | d := json.NewDecoder(resp.Body) 182 | err = d.Decode(&data) 183 | resp.Body.Close() 184 | 185 | if err != nil { 186 | return 187 | } 188 | 189 | scratch := PathScratch(pso2path) 190 | itemPath := path.Join(scratch, PathTranslateDll) 191 | translationPath := path.Join(scratch, PathTranslationBin) 192 | englishPath := path.Join(scratch, PathEnglishDb) 193 | rootUrl, _ := url.Parse(EnglishUpdateURL) 194 | 195 | updateEnglishFile := func(path string, timestamp int64, urlStr string) (bool, error) { 196 | url, err := url.Parse(urlStr) 197 | if err != nil { 198 | return false, err 199 | } 200 | 201 | st, err := os.Stat(path) 202 | 203 | if os.IsNotExist(err) || (err == nil && st.ModTime().Unix() < timestamp) { 204 | resp, err := download.Request(rootUrl.ResolveReference(url).String()) 205 | if err != nil { 206 | return false, err 207 | } 208 | 209 | f, err := os.Create(path) 210 | if err != nil { 211 | return false, err 212 | } 213 | 214 | _, err = io.Copy(f, resp.Body) 215 | f.Close() 216 | resp.Body.Close() 217 | 218 | return true, err 219 | } 220 | 221 | return false, err 222 | } 223 | 224 | translationChanged, err = updateEnglishFile(englishPath, data.EnglishTimestamp, data.EnglishURL) 225 | if err != nil { 226 | return 227 | } 228 | 229 | _, err = updateEnglishFile(translationPath, data.TranslationTimestamp, data.TranslationURL) 230 | if err != nil { 231 | return 232 | } 233 | 234 | _, err = updateEnglishFile(itemPath, data.ItemTimestamp, data.ItemURL) 235 | 236 | return 237 | } 238 | 239 | func LoadTranslationConfig(pso2dir string) (t TranslationConfig, err error) { 240 | f, err := os.Open(path.Join(pso2dir, PathTranslationCfg)) 241 | if err != nil { 242 | return 243 | } 244 | 245 | t, err = NewTranslationConfig(f) 246 | f.Close() 247 | 248 | return 249 | } 250 | 251 | func SaveTranslationConfig(pso2dir string, t TranslationConfig) (err error) { 252 | f, err := os.Create(path.Join(pso2dir, PathTranslationCfg)) 253 | if err != nil { 254 | return 255 | } 256 | 257 | err = t.Write(f) 258 | f.Close() 259 | 260 | return 261 | } 262 | 263 | func CheckFiles(pso2path string, checkHash bool, patches *download.PatchList) (changes []*download.PatchEntry, err error) { 264 | pbar := pb.New(len(patches.Entries)) 265 | pbar.SetRefreshRate(time.Second / 30) 266 | pbar.Start() 267 | 268 | for i := range patches.Entries { 269 | e := &patches.Entries[i] 270 | filepath := path.Join(pso2path, download.RemoveExtension(e.Path)) 271 | 272 | st, err := os.Stat(filepath) 273 | 274 | if os.IsNotExist(err) { 275 | changes = append(changes, e) 276 | } else { 277 | if err != nil { 278 | return nil, err 279 | } 280 | 281 | if st.Size() != e.Size { 282 | changes = append(changes, e) 283 | } else if checkHash { 284 | f, err := os.Open(filepath) 285 | if err != nil { 286 | return nil, err 287 | } 288 | h := md5.New() 289 | _, err = io.Copy(h, f) 290 | f.Close() 291 | if err != nil { 292 | return nil, err 293 | } 294 | if bytes.Compare(h.Sum(nil), e.MD5[:]) != 0 { 295 | changes = append(changes, e) 296 | } 297 | } 298 | } 299 | 300 | pbar.Increment() 301 | runtime.Gosched() 302 | } 303 | 304 | pbar.Finish() 305 | 306 | return 307 | } 308 | 309 | func DownloadChanges(pso2path string, changes []*download.PatchEntry, parallel int) (errs []error) { 310 | if parallel <= 0 { 311 | parallel = 1 312 | } 313 | 314 | changesSize := int64(0) 315 | for _, e := range changes { 316 | changesSize += e.Size 317 | } 318 | 319 | pbar := pb.New64(changesSize) 320 | pbar.SetUnits(pb.U_BYTES) 321 | pbar.SetRefreshRate(time.Second / 30) 322 | pbar.ShowSpeed = true 323 | pbar.Start() 324 | 325 | queue := make(chan *download.PatchEntry) 326 | done := make(chan bool) 327 | 328 | errlock := sync.Mutex{} 329 | 330 | complain := func(err error) bool { 331 | if err != nil { 332 | errlock.Lock() 333 | errs = append(errs, err) 334 | errlock.Unlock() 335 | return true 336 | } 337 | 338 | return false 339 | } 340 | 341 | for i := 0; i < parallel; i++ { 342 | go func() { 343 | h := md5.New() 344 | 345 | for { 346 | e, ok := <-queue 347 | if !ok { 348 | break 349 | } 350 | 351 | filepath := path.Join(pso2path, download.RemoveExtension(e.Path)) 352 | 353 | err := os.MkdirAll(path.Dir(filepath), 0777) 354 | if complain(err) { 355 | break 356 | } 357 | 358 | pathUrl, err := e.URL() 359 | if complain(err) { 360 | continue 361 | } 362 | 363 | resp, err := download.Request(pathUrl.String()) 364 | if complain(err) { 365 | continue 366 | } 367 | 368 | if resp.StatusCode != 200 { 369 | complain(errors.New(pathUrl.String() + ": " + resp.Status)) 370 | continue 371 | } 372 | 373 | if resp.ContentLength >= 0 && resp.ContentLength != e.Size { 374 | resp.Body.Close() 375 | complain(errors.New(e.Path + ": invalid file size")) 376 | continue 377 | } 378 | 379 | f, err := os.Create(filepath) 380 | if complain(err) { 381 | resp.Body.Close() 382 | continue 383 | } 384 | 385 | h.Reset() 386 | n, err := io.Copy(io.MultiWriter(f, h, pbar), resp.Body) 387 | 388 | resp.Body.Close() 389 | f.Close() 390 | 391 | if !complain(err) { 392 | if n != e.Size { 393 | complain(errors.New(pathUrl.String() + ": download finished prematurely")) 394 | } else if bytes.Compare(h.Sum(nil), e.MD5[:]) != 0 { 395 | complain(errors.New(pathUrl.String() + ": download hash mismatch")) 396 | } 397 | } 398 | } 399 | 400 | done <-true 401 | }() 402 | } 403 | 404 | for _, e := range changes { 405 | queue <-e 406 | } 407 | close(queue) 408 | 409 | for i := 0; i < parallel; i++ { 410 | <-done 411 | } 412 | 413 | pbar.Finish() 414 | 415 | return 416 | } 417 | 418 | func PruneFiles(pso2path string, patchlist *download.PatchList) (size int64, err error) { 419 | win32 := path.Join(pso2path, "data/win32") 420 | 421 | f, err := os.Open(win32) 422 | if err != nil { 423 | return 424 | } 425 | 426 | for err == nil { 427 | var files []os.FileInfo 428 | files, err = f.Readdir(0x80) 429 | for _, f := range files { 430 | if f.IsDir() { 431 | continue 432 | } 433 | 434 | e := patchlist.EntryMap[f.Name() + ".pat"] 435 | 436 | if e == nil { 437 | size += f.Size() 438 | win32 := path.Join(win32, f.Name()) 439 | err = os.Remove(win32) 440 | if err != nil { 441 | break 442 | } 443 | } 444 | } 445 | } 446 | 447 | f.Close() 448 | 449 | if err == io.EOF { 450 | err = nil 451 | } 452 | 453 | return 454 | } 455 | 456 | func LaunchGame(pso2path string) (cmd *exec.Cmd, err error) { 457 | cmd = exec.Command("./pso2.exe", "+0x33aca2b9", "-pso2") 458 | cmd.Env = append(os.Environ(), "-pso2=+0x01e3f1e9") 459 | cmd.Dir = pso2path 460 | err = cmd.Start() 461 | 462 | return 463 | } 464 | -------------------------------------------------------------------------------- /download/download.go: -------------------------------------------------------------------------------- 1 | package download 2 | 3 | import ( 4 | "io" 5 | "fmt" 6 | "errors" 7 | "strings" 8 | "net/url" 9 | "net/http" 10 | ) 11 | 12 | const ( 13 | ProductionRoot = "http://download.pso2.jp/patch_prod/" 14 | ProductionPatchlist = ProductionRoot + "patches/patchlist.txt" 15 | ProductionPatchlistOld = ProductionRoot + "patches_old/patchlist.txt" 16 | ProductionLauncherlist = ProductionRoot + "patches/launcherlist.txt" 17 | ProductionVersion = ProductionRoot + "patches/version.ver" 18 | ) 19 | 20 | var httpClient http.Client 21 | 22 | func Request(urlStr string) (*http.Response, error) { 23 | req, err := http.NewRequest("GET", urlStr, nil) 24 | if err != nil { 25 | return nil, err 26 | } 27 | req.Header.Add("User-Agent", "AQUA_HTTP") 28 | 29 | resp, err := httpClient.Do(req) 30 | if err == nil && resp.StatusCode != 200 { 31 | return resp, errors.New(urlStr + " " + resp.Status) 32 | } 33 | 34 | return resp, err 35 | } 36 | 37 | func DownloadList(urlStr string) (p *PatchList, err error) { 38 | resp, err := Request(urlStr) 39 | 40 | if err != nil { 41 | return 42 | } 43 | 44 | capHint := 20 45 | if strings.Contains(urlStr, "patchlist") { 46 | capHint = 20000 47 | } 48 | 49 | p, err = ParseListCap(resp.Body, urlStr, capHint) 50 | 51 | resp.Body.Close() 52 | 53 | return 54 | } 55 | 56 | func ParseList(r io.Reader, urlStr string) (p *PatchList, err error) { 57 | return ParseListCap(r, urlStr, 20) 58 | } 59 | 60 | func ParseListCap(r io.Reader, urlStr string, capHint int) (p *PatchList, err error) { 61 | url, err := url.Parse(urlStr) 62 | if err != nil { 63 | return 64 | } 65 | p = &PatchList{URL: url, Entries: make([]PatchEntry, 0, capHint)} 66 | 67 | for err != io.EOF { 68 | var filename, hash string 69 | var filesize int64 70 | var n int 71 | n, err = fmt.Fscanln(r, &filename, &filesize, &hash) 72 | 73 | if err != nil || n != 3 { 74 | continue 75 | } 76 | 77 | var filehash []uint8 78 | n, err := fmt.Sscanf(hash, "%x", &filehash) 79 | if err != nil || n != 1 || len(filehash) != 0x10 { 80 | continue 81 | } 82 | 83 | e := PatchEntry{PatchList: p, Path: filename, Size: filesize} 84 | copy(e.MD5[:], filehash) 85 | 86 | p.Entries = append(p.Entries, e) 87 | } 88 | 89 | err = nil 90 | 91 | p.fillMap() 92 | 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /download/patchlist.go: -------------------------------------------------------------------------------- 1 | package download 2 | 3 | import ( 4 | "io" 5 | "fmt" 6 | "path" 7 | "bytes" 8 | "strings" 9 | "net/url" 10 | ) 11 | 12 | type PatchList struct { 13 | URL *url.URL 14 | 15 | Entries []PatchEntry 16 | 17 | EntryMap map[string]*PatchEntry 18 | } 19 | 20 | type PatchEntry struct { 21 | PatchList *PatchList 22 | Path string 23 | Size int64 24 | MD5 [0x10]uint8 25 | } 26 | 27 | func (p *PatchList) fillMap() { 28 | p.EntryMap = make(map[string]*PatchEntry) 29 | 30 | for i, _ := range p.Entries { 31 | e := &p.Entries[i] 32 | 33 | p.EntryMap[e.BaseName()] = e 34 | } 35 | } 36 | 37 | func (p *PatchEntry) URL() (ourl *url.URL, err error) { 38 | url, err := url.Parse(p.Path) 39 | if err != nil { 40 | return 41 | } 42 | 43 | return p.PatchList.URL.ResolveReference(url), nil 44 | } 45 | 46 | func (p *PatchEntry) BaseName() string { 47 | return path.Base(p.Path) 48 | } 49 | 50 | func RemoveExtension(s string) string { 51 | return strings.TrimSuffix(s, ".pat") 52 | } 53 | 54 | func (p *PatchList) Write(w io.Writer) error { 55 | for _, e := range p.Entries { 56 | _, err := fmt.Fprintf(w, "%s\t%d\t%X\n", e.Path, e.Size, e.MD5) 57 | 58 | if err != nil { 59 | return err 60 | } 61 | } 62 | 63 | return nil 64 | } 65 | 66 | func (p *PatchList) Diff(po *PatchList) (pn *PatchList) { 67 | if po == nil { 68 | return p 69 | } 70 | 71 | pn = &PatchList{EntryMap: make(map[string]*PatchEntry)} 72 | 73 | for _, e := range p.Entries { 74 | eo := po.EntryMap[e.BaseName()] 75 | 76 | if eo == nil || e.Size != eo.Size || bytes.Compare(e.MD5[:], eo.MD5[:]) != 0 { 77 | pn.Entries = append(pn.Entries, e) 78 | } 79 | } 80 | 81 | pn.fillMap() 82 | 83 | return 84 | } 85 | 86 | func (p *PatchList) MergeOld(po *PatchList) (pn *PatchList) { 87 | if po == nil { 88 | return p 89 | } 90 | 91 | pn = &PatchList{Entries: p.Entries, EntryMap: make(map[string]*PatchEntry)} 92 | 93 | for _, e := range po.Entries { 94 | eo := p.EntryMap[e.BaseName()] 95 | 96 | if eo == nil { 97 | pn.Entries = append(pn.Entries, e) 98 | } 99 | } 100 | 101 | pn.fillMap() 102 | 103 | return 104 | } 105 | 106 | func (p *PatchList) Append(po *PatchList) { 107 | p.Entries = append(p.Entries, po.Entries...) 108 | p.fillMap() 109 | } 110 | -------------------------------------------------------------------------------- /ice/prsreader.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import ( 4 | "io" 5 | "errors" 6 | ) 7 | 8 | // Reference: https://github.com/Grumbel/rfactortools/blob/master/other/quickbms/src/compression/prs.cpp 9 | // (modification: long copy sizes are readByte() + 10, not +1 as that source mentions) 10 | 11 | const prsBufferSize = 0x80000 12 | const prsBufferThreshold = prsBufferSize - 0x400 13 | const prsBufferLookbehind = 0x2000 14 | 15 | var prsEOF = errors.New("prsEOF") 16 | 17 | type prsReader struct { 18 | reader io.ReadSeeker 19 | controlPos, controlByte uint8 20 | 21 | buffer [prsBufferSize]uint8 22 | byteBuffer [1]uint8 23 | outputBufferPosition int 24 | outputPosition int 25 | size, position int64 26 | err error 27 | } 28 | 29 | func (s *prsReader) readByte() (ret uint8, err error) { 30 | n, err := s.reader.Read(s.byteBuffer[:]) 31 | ret = s.byteBuffer[0] 32 | if n == 1 { 33 | err = nil 34 | } 35 | return 36 | } 37 | 38 | func (s *prsReader) consumeControlStream() (ret bool, err error) { 39 | s.controlPos-- 40 | if s.controlPos == 0 { 41 | s.controlByte, err = s.readByte() 42 | s.controlPos = 8 43 | } 44 | 45 | ret = (s.controlByte & 1) == 1 46 | s.controlByte >>= 1 47 | return 48 | } 49 | 50 | func (s *prsReader) decompress() error { 51 | if flag, err := s.consumeControlStream(); err == nil && flag { 52 | // Read byte directly from bytestream 53 | 54 | b, err := s.readByte() 55 | if err != nil { 56 | return err 57 | } else { 58 | s.queueOutput(b, true) 59 | } 60 | } else if err == nil { 61 | // Copy from sliding output window 62 | 63 | var offset int 64 | var size int 65 | if flag, err = s.consumeControlStream(); err == nil && flag { 66 | // Long copy 67 | var b, lsb, msb uint8 68 | 69 | lsb, err = s.readByte() 70 | msb, err = s.readByte() 71 | offset = int((uint16(msb) << 8) | uint16(lsb)) 72 | 73 | if err != nil { 74 | return err 75 | } 76 | 77 | if offset == 0 { 78 | return prsEOF 79 | } 80 | 81 | size = int(lsb & 0x07) 82 | offset = int(int32(uint32(offset >> 3) | 0xffffe000)) 83 | 84 | if size == 0 { 85 | b, err = s.readByte() 86 | size = int(b) + 10 87 | } else { 88 | size += 2 89 | } 90 | } else if err == nil { 91 | // Short copy 92 | for i := 0; i < 2; i++ { 93 | size <<= 1 94 | if flag, err = s.consumeControlStream(); flag { 95 | size |= 1 96 | } 97 | } 98 | size += 2 99 | 100 | var b uint8 101 | b, err = s.readByte() 102 | offset = int(int32(uint32(b) | 0xffffff00)) 103 | } 104 | 105 | if err == nil { 106 | bufferPos := s.outputBufferPosition 107 | for i := 0; i < size; i++ { 108 | var b uint8 = 0 109 | pos := offset + i + bufferPos 110 | if pos < s.outputBufferPosition { 111 | b = s.buffer[offset + i + bufferPos] 112 | } 113 | s.queueOutput(b, false) 114 | } 115 | s.flushOutput() 116 | } else { 117 | return err 118 | } 119 | } else { 120 | return err 121 | } 122 | 123 | return nil 124 | } 125 | 126 | func (s *prsReader) queueOutput(b uint8, flush bool) { 127 | s.buffer[s.outputBufferPosition] = b 128 | s.outputBufferPosition++ 129 | 130 | if flush { 131 | s.flushOutput() 132 | } 133 | } 134 | 135 | func (s *prsReader) flushOutput() { 136 | // Flush our buffer every 16KB 137 | if s.outputBufferPosition > prsBufferThreshold { 138 | diff := s.outputBufferPosition - prsBufferLookbehind 139 | 140 | copy(s.buffer[:prsBufferLookbehind], s.buffer[diff:]) 141 | s.outputPosition -= diff 142 | s.outputBufferPosition = prsBufferLookbehind 143 | } 144 | } 145 | 146 | func (s *prsReader) Read(p []byte) (n int, err error) { 147 | err = s.err 148 | 149 | if s.position >= s.size { 150 | err = io.EOF 151 | } 152 | 153 | // Buffer decompressed output 154 | for len(p) > 0 && err == nil { 155 | if s.outputBufferPosition <= s.outputPosition { 156 | if err = s.decompress(); err != nil { 157 | break 158 | } 159 | } 160 | 161 | if int64(len(p)) > s.size - s.position { 162 | p = p[:int(s.size - s.position)] 163 | } 164 | 165 | read := copy(p, s.buffer[s.outputPosition:s.outputBufferPosition]) 166 | n += read 167 | s.outputPosition += read 168 | s.position += int64(read) 169 | p = p[read:] 170 | 171 | if s.position >= s.size { 172 | break 173 | } 174 | } 175 | 176 | s.err = err 177 | 178 | if err == io.EOF { 179 | err = io.ErrUnexpectedEOF 180 | } else if err == prsEOF { 181 | err = io.EOF 182 | } 183 | 184 | if err == io.EOF && s.position < s.size { 185 | read := int(s.size - s.position) 186 | if read > len(p) { 187 | read = len(p) 188 | } 189 | p = p[:read] 190 | 191 | for i := range p { 192 | p[i] = 0 193 | } 194 | 195 | n += read 196 | s.position += int64(read) 197 | 198 | err = nil 199 | } 200 | 201 | return 202 | } 203 | 204 | func (s *prsReader) Seek(offset int64, whence int) (pos int64, err error) { 205 | // Determine offset... 206 | switch whence { 207 | case 1: 208 | offset += int64(s.position) 209 | case 2: 210 | offset += s.Size() 211 | } 212 | 213 | // Rewind to beginning... 214 | if offset < int64(s.position) { 215 | s.reader.Seek(0, 0) 216 | s.controlPos = 1 217 | s.outputBufferPosition = 0 218 | s.outputPosition = 0 219 | s.position = 0 220 | s.err = nil 221 | } 222 | 223 | // Then read forward until we reach our destination 224 | for offset > int64(s.position) && err == nil { 225 | diff := offset - int64(s.position) 226 | var buffer [0x1000]uint8 227 | 228 | if diff > int64(len(buffer)) { 229 | diff = int64(len(buffer)) 230 | } 231 | 232 | _, err = s.Read(buffer[:diff]) 233 | } 234 | 235 | pos = int64(s.position) 236 | return 237 | } 238 | 239 | func (s *prsReader) Size() int64 { 240 | return s.size 241 | } 242 | 243 | func newPrsReader(reader io.ReadSeeker, size int64) *prsReader { 244 | return &prsReader{reader, 1, 0, [prsBufferSize]uint8{}, [1]uint8{}, 0, 0, size, 0, nil} 245 | } 246 | -------------------------------------------------------------------------------- /ice/prswriter.go: -------------------------------------------------------------------------------- 1 | package ice 2 | 3 | import "io" 4 | 5 | // TODO: This is incorrect. Need to buffer writes until the control byte is full 6 | 7 | type prsWriter struct { 8 | writer io.Writer 9 | controlPos, controlByte uint8 10 | } 11 | 12 | func (s *prsWriter) writeByte(b uint8) (err error) { 13 | _, err = s.writer.Write([]uint8 { b }) 14 | return 15 | } 16 | 17 | func (s *prsWriter) writeControlStream(b, save bool) error { 18 | s.controlByte >>= 1 19 | if b { 20 | s.controlByte |= 0x80 21 | } 22 | s.controlPos++ 23 | 24 | if save { 25 | return s.saveControlStream() 26 | } 27 | 28 | return nil 29 | } 30 | 31 | func (s *prsWriter) saveControlStream() (err error) { 32 | if s.controlPos >= 8 { 33 | err = s.writeByte(s.controlByte) 34 | 35 | s.controlPos = 0 36 | s.controlByte = 0 37 | } 38 | 39 | return 40 | } 41 | 42 | func (s *prsWriter) Write(p []byte) (n int, err error) { 43 | for _, b := range p { 44 | s.writeControlStream(true, false) 45 | err = s.writeByte(b) 46 | if err == nil { 47 | err = s.saveControlStream() 48 | } 49 | 50 | if err != nil { 51 | break 52 | } else { 53 | n++ 54 | } 55 | } 56 | 57 | return 58 | } 59 | 60 | func (s *prsWriter) Close() (err error) { 61 | err = s.writeControlStream(false, true) 62 | err = s.writeControlStream(true, true) 63 | if s.controlPos > 0 { 64 | s.controlByte = (s.controlByte << s.controlPos) >> 8 65 | } 66 | err = s.writeByte(0) 67 | err = s.writeByte(0) 68 | 69 | if c, ok := s.writer.(io.Closer); ok && err == nil { 70 | err = c.Close() 71 | } 72 | 73 | return 74 | } 75 | 76 | func newPrsWriter(writer io.Writer) *prsWriter { 77 | return &prsWriter{writer, 0, 0} 78 | } 79 | -------------------------------------------------------------------------------- /net/cipher.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "errors" 7 | "crypto/rsa" 8 | "crypto/x509" 9 | "encoding/pem" 10 | ) 11 | 12 | func loadKey(reader io.Reader) (interface{}, error) { 13 | data, err := ioutil.ReadAll(reader) 14 | 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | for len(data) > 0 { 20 | var block *pem.Block 21 | block, data = pem.Decode(data) 22 | 23 | if block == nil { 24 | break 25 | } 26 | 27 | switch block.Type { 28 | case "RSA PRIVATE KEY": 29 | return x509.ParsePKCS1PrivateKey(block.Bytes) 30 | 31 | case "PRIVATE KEY": 32 | return x509.ParsePKCS8PrivateKey(block.Bytes) 33 | 34 | case "RSA PUBLIC KEY": fallthrough 35 | case "PUBLIC KEY": 36 | return x509.ParsePKIXPublicKey(block.Bytes) 37 | } 38 | } 39 | 40 | return nil, errors.New("no key found") 41 | } 42 | 43 | func LoadPublicKey(reader io.Reader) (*rsa.PublicKey, error) { 44 | key, err := loadKey(reader) 45 | 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | switch k := key.(type) { 51 | case *rsa.PublicKey: 52 | return k, nil 53 | case *rsa.PrivateKey: 54 | return &k.PublicKey, nil 55 | } 56 | 57 | return nil, errors.New("public key not found") 58 | } 59 | 60 | func LoadPrivateKey(reader io.Reader) (*rsa.PrivateKey, error) { 61 | key, err := loadKey(reader) 62 | 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | if k, ok := key.(*rsa.PrivateKey); ok { 68 | return k, nil 69 | } 70 | 71 | return nil, errors.New("private key not found") 72 | } 73 | -------------------------------------------------------------------------------- /net/connection.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "fmt" 7 | "errors" 8 | "crypto/cipher" 9 | "crypto/rc4" 10 | "encoding/binary" 11 | "aaronlindsay.com/go/pkg/pso2/net/packets" 12 | ) 13 | 14 | const maxPacketSize = 0x04000000 15 | 16 | type Connection struct { 17 | stream io.ReadWriter 18 | 19 | icipher cipher.Stream 20 | ocipher cipher.Stream 21 | } 22 | 23 | func NewConnection(stream io.ReadWriter) *Connection { 24 | c := &Connection{} 25 | c.stream = stream 26 | return c 27 | } 28 | 29 | func (c *Connection) RemoteAddr() net.Addr { 30 | return c.stream.(net.Conn).RemoteAddr() 31 | } 32 | 33 | func (c *Connection) LocalAddr() net.Addr { 34 | return c.stream.(net.Conn).LocalAddr() 35 | } 36 | 37 | func (c *Connection) SetCipher(key []uint8) (err error) { 38 | if key == nil { 39 | c.icipher = nil 40 | c.ocipher = nil 41 | } else { 42 | c.icipher, err = rc4.NewCipher(key) 43 | c.ocipher, err = rc4.NewCipher(key) 44 | } 45 | 46 | return 47 | } 48 | 49 | func (c *Connection) ReadPacket() (*packets.Packet, error) { 50 | var headerData [8]uint8 51 | 52 | read := 0 53 | for read < len(headerData) { 54 | n, err := c.stream.Read(headerData[read:]) 55 | read += n 56 | 57 | if err != nil { 58 | return nil, err 59 | } 60 | } 61 | 62 | if c.icipher != nil { 63 | c.icipher.XORKeyStream(headerData[:], headerData[:]) 64 | } 65 | 66 | p, size := &packets.Packet{binary.LittleEndian.Uint16(headerData[4:6]), binary.LittleEndian.Uint16(headerData[6:]), nil}, binary.LittleEndian.Uint32(headerData[:4]) 67 | 68 | if size < 8 || size > maxPacketSize { 69 | return nil, errors.New("invalid packet size") 70 | } 71 | 72 | data := make([]uint8, size - 8) 73 | p.Data = data 74 | 75 | for len(data) > 0 { 76 | n, err := c.stream.Read(data) 77 | 78 | if err != nil { 79 | return p, err 80 | } 81 | 82 | data = data[n:] 83 | } 84 | 85 | if c.icipher != nil { 86 | c.icipher.XORKeyStream(p.Data, p.Data) 87 | } 88 | 89 | Logger.Tracef("%s read %s", c, p) 90 | 91 | return p, nil 92 | } 93 | 94 | func (c *Connection) WritePacket(p *packets.Packet) error { 95 | Logger.Tracef("%s writing %s", c, p) 96 | 97 | data := make([]uint8, 8 + len(p.Data)) 98 | binary.LittleEndian.PutUint32(data[:4], uint32(len(data))) 99 | binary.LittleEndian.PutUint16(data[4:6], p.Type) 100 | binary.LittleEndian.PutUint16(data[6:8], p.Flags) 101 | copy(data[8:], p.Data) 102 | 103 | if c.ocipher != nil { 104 | c.ocipher.XORKeyStream(data, data) 105 | } 106 | 107 | for len(data) > 0 { 108 | w, err := c.stream.Write(data) 109 | 110 | if err != nil { 111 | return err 112 | } 113 | 114 | data = data[w:] 115 | } 116 | 117 | return nil 118 | } 119 | 120 | func (c *Connection) RoutePackets(r *PacketRoute) error { 121 | for { 122 | p, err := c.ReadPacket() 123 | 124 | if err != nil { 125 | return err 126 | } 127 | 128 | consumed, err := r.RoutePacket(c, p) 129 | 130 | if err != nil { 131 | return err 132 | } 133 | 134 | if !consumed { 135 | Logger.Warningf("%s packet %s ignored", c, p) 136 | } 137 | } 138 | } 139 | 140 | func (c *Connection) Close() error { 141 | if s, ok := c.stream.(io.Closer); ok { 142 | return s.Close() 143 | } 144 | 145 | return nil 146 | } 147 | 148 | func (c *Connection) String() string { 149 | if c, ok := c.stream.(net.Conn); ok { 150 | return fmt.Sprintf("[pso2/net/connection: %s -> %s]", c.LocalAddr(), c.RemoteAddr()) 151 | } else { 152 | return "[pso2/net/connection]" 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /net/handlers.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "path" 5 | "fmt" 6 | "net" 7 | "os" 8 | "encoding/binary" 9 | "crypto/rsa" 10 | "aaronlindsay.com/go/pkg/pso2/net/packets" 11 | ) 12 | 13 | func HandlerCipher(privateKey *rsa.PrivateKey) PacketHandler { 14 | return handlerCipher{privateKey} 15 | } 16 | 17 | type handlerCipher struct { 18 | privateKey *rsa.PrivateKey 19 | } 20 | 21 | func (h handlerCipher) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 22 | Logger.Debugf("%s cipher packet, enabling encryption", c) 23 | 24 | b, err := packets.ParseCipher(p) 25 | if err != nil { 26 | return false, err 27 | } 28 | 29 | key, err := b.RC4Key(h.privateKey) 30 | if err != nil { 31 | return false, err 32 | } 33 | 34 | err = c.SetCipher(key) 35 | 36 | return true, err 37 | } 38 | 39 | func HandlerIgnore(handler PacketHandler) PacketHandler { 40 | return handlerIgnore{handler} 41 | } 42 | 43 | type handlerIgnore struct { 44 | handler PacketHandler 45 | } 46 | 47 | func (h handlerIgnore) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 48 | _, err := h.handler.HandlePacket(c, p) 49 | return false, err 50 | } 51 | 52 | func HandlerDump(location string) PacketHandler { 53 | return handlerDump{location} 54 | } 55 | 56 | type handlerDump struct { 57 | location string 58 | } 59 | 60 | func (h handlerDump) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 61 | host, portRemote, _ := net.SplitHostPort(c.RemoteAddr().String()) 62 | _, portLocal, _ := net.SplitHostPort(c.LocalAddr().String()) 63 | filename := path.Join(h.location, fmt.Sprintf("%s-%s-%s.dump", host, portLocal, portRemote)) 64 | 65 | f, err := os.OpenFile(filename, os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0666) 66 | 67 | if err != nil { 68 | Logger.Warningf("%s error opening dump file %s. %s", c, filename, err) 69 | return false, nil 70 | } 71 | 72 | end := binary.LittleEndian 73 | binary.Write(f, end, uint32(8 + len(p.Data))) 74 | binary.Write(f, end, p.Type) 75 | binary.Write(f, end, p.Flags) 76 | binary.Write(f, end, p.Data) 77 | f.Close() 78 | 79 | return false, nil 80 | } 81 | -------------------------------------------------------------------------------- /net/log.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import "github.com/juju/loggo" 4 | 5 | var Logger loggo.Logger = loggo.GetLogger("pso2.net") 6 | -------------------------------------------------------------------------------- /net/packets/block.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | const TypeBlock = 0x2c11 8 | 9 | type Block struct { 10 | Unk [7]uint32 11 | NameRaw [0x20]uint16 12 | AddressRaw [4]uint8 13 | Port uint16 14 | Unk2 [0x26]uint8 15 | } 16 | 17 | func (b *Block) Name() string { 18 | return DecodeString(b.NameRaw[:]) 19 | } 20 | 21 | func (b *Block) SetName(v string) { 22 | EncodeString(v, b.NameRaw[:]) 23 | } 24 | 25 | func (b *Block) Address() net.IP { 26 | return net.IP(b.AddressRaw[:]) 27 | } 28 | 29 | func (b *Block) SetAddress(v net.IP) { 30 | copy(b.AddressRaw[:], v.To4()) 31 | } 32 | 33 | func (b *Block) Packet() (*Packet, error) { 34 | return PacketFromBinary(TypeBlock, 0, b) 35 | } 36 | 37 | func ParseBlock(p *Packet) (*Block, error) { 38 | s, err := PacketToBinary(p, &Block{}) 39 | return s.(*Block), err 40 | } 41 | -------------------------------------------------------------------------------- /net/packets/blockresponse.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | const TypeBlockResponse = 0x1311 8 | 9 | type BlockResponse struct { 10 | Unk [0x0c]uint8 11 | AddressRaw [4]uint8 12 | Port uint16 13 | Unk2 [0x0a]uint8 14 | } 15 | 16 | func (b *BlockResponse) Address() net.IP { 17 | return net.IP(b.AddressRaw[:]) 18 | } 19 | 20 | func (b *BlockResponse) SetAddress(v net.IP) { 21 | copy(b.AddressRaw[:], v.To4()) 22 | } 23 | 24 | func (b *BlockResponse) Packet() (*Packet, error) { 25 | return PacketFromBinary(TypeBlockResponse, 0, b) 26 | } 27 | 28 | func ParseBlockResponse(p *Packet) (*BlockResponse, error) { 29 | s, err := PacketToBinary(p, &BlockResponse{}) 30 | return s.(*BlockResponse), err 31 | } 32 | -------------------------------------------------------------------------------- /net/packets/blocks.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | ) 7 | 8 | const TypeBlocks = 0x6511 9 | const TypeBlocks2 = 0x1011 10 | 11 | type Blocks struct { 12 | Count uint32 13 | Entries []Block `length:"Count"` 14 | } 15 | 16 | func (b *Blocks) Packet() (*Packet, error) { 17 | var buffer bytes.Buffer 18 | binary.Write(&buffer, binary.LittleEndian, uint32(len(b.Entries))) 19 | binary.Write(&buffer, binary.LittleEndian, b.Entries) 20 | 21 | return &Packet{TypeBlocks, 0, buffer.Bytes()}, nil 22 | } 23 | 24 | func ParseBlocks(p *Packet) (*Blocks, error) { 25 | s, err := PacketToBinary(p, &Blocks{}) 26 | return s.(*Blocks), err 27 | } 28 | -------------------------------------------------------------------------------- /net/packets/cipher.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "errors" 5 | "crypto/rsa" 6 | "crypto/rand" 7 | ) 8 | 9 | const TypeCipher = 0x0b11 10 | 11 | type Cipher struct { 12 | KeyData [0x80]uint8 13 | Padding [0x84]uint8 14 | } 15 | 16 | func (c *Cipher) Key(key *rsa.PrivateKey) ([]uint8, error) { 17 | data := make([]uint8, len(c.KeyData)) 18 | for i := range data { 19 | data[i] = c.KeyData[len(c.KeyData) - i - 1] 20 | } 21 | 22 | return rsa.DecryptPKCS1v15(nil, key, data) 23 | } 24 | 25 | func (c *Cipher) RC4Key(key *rsa.PrivateKey) ([]uint8, error) { 26 | data, err := c.Key(key) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return CipherRC4Key(data) 32 | } 33 | 34 | func CipherRC4Key(key []uint8) ([]uint8, error) { 35 | if len(key) != 0x20 { 36 | return nil, errors.New("pso2/net/packets/Cipher: invalid decrypted key length") 37 | } 38 | 39 | return key[0x10:], nil 40 | } 41 | 42 | func (c *Cipher) SetKey(v []uint8, key *rsa.PublicKey) error { 43 | data, err := rsa.EncryptPKCS1v15(rand.Reader, key, v) 44 | 45 | if err != nil { 46 | return err 47 | } 48 | 49 | if len(data) != len(c.KeyData) { 50 | return errors.New("pso2/net/packets/Cipher: invalid encrypted key length") 51 | } 52 | 53 | for i := range c.KeyData { 54 | c.KeyData[i] = data[len(c.KeyData) - i - 1] 55 | } 56 | 57 | return nil 58 | } 59 | 60 | func (s *Cipher) Packet() (*Packet, error) { 61 | return PacketFromBinary(TypeCipher, 0, s) 62 | } 63 | 64 | func ParseCipher(p *Packet) (*Cipher, error) { 65 | s, err := PacketToBinary(p, &Cipher{}) 66 | return s.(*Cipher), err 67 | } 68 | 69 | func packetCipher(p *Packet) (interface{}, error) { 70 | return ParseCipher(p) 71 | } 72 | -------------------------------------------------------------------------------- /net/packets/packet.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "fmt" 5 | "bytes" 6 | "github.com/quarnster/util/encoding/binary" 7 | bin "encoding/binary" 8 | "aaronlindsay.com/go/pkg/pso2/util" 9 | ) 10 | 11 | const FlagProcessed = 4 12 | 13 | type Packet struct { 14 | Type uint16 15 | Flags uint16 16 | Data []uint8 17 | } 18 | 19 | func (p *Packet) String() string { 20 | return fmt.Sprintf("[pso2/net/packet: 0x%04x (0x%04x) size: 0x%08x]", p.Type, p.Flags, len(p.Data)) 21 | } 22 | 23 | type PacketData interface { 24 | Packet() (*Packet, error) 25 | } 26 | 27 | func PacketToBinary(p *Packet, i interface{}) (interface{}, error) { 28 | reader := binary.BinaryReader{ Reader: util.Seeker(bytes.NewBuffer(p.Data)), Endianess: binary.LittleEndian } 29 | 30 | err := reader.ReadInterface(i) 31 | 32 | return i, err 33 | } 34 | 35 | func PacketFromBinary(packetType, flags uint16, i interface{}) (*Packet, error) { 36 | data := make([]uint8, 0, bin.Size(i)) 37 | b := bytes.NewBuffer(data) 38 | err := bin.Write(b, bin.LittleEndian, i) 39 | 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return &Packet{packetType, flags, b.Bytes()}, nil 45 | } 46 | -------------------------------------------------------------------------------- /net/packets/room.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | const TypeRoom = 0x1711 8 | const TypeRoomTeam = 0x4f11 9 | 10 | type Room struct { 11 | Unk1 [6]uint32 12 | AddressRaw [4]uint8 13 | Unk2 uint32 14 | Port uint16 15 | Unk3 [6]uint8 16 | } 17 | 18 | func (b *Room) Address() net.IP { 19 | return net.IP(b.AddressRaw[:]) 20 | } 21 | 22 | func (b *Room) SetAddress(v net.IP) { 23 | copy(b.AddressRaw[:], v.To4()) 24 | } 25 | 26 | func (b *Room) Packet() (*Packet, error) { 27 | return PacketFromBinary(TypeRoom, 0, b) 28 | } 29 | 30 | func ParseRoom(p *Packet) (*Room, error) { 31 | s, err := PacketToBinary(p, &Room{}) 32 | return s.(*Room), err 33 | } 34 | 35 | type RoomTeam Room 36 | 37 | func (b *RoomTeam) Packet() (*Packet, error) { 38 | return PacketFromBinary(TypeRoomTeam, 0, b) 39 | } 40 | -------------------------------------------------------------------------------- /net/packets/ship.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | const TypeShip = 0x3d11 8 | const ShipCount = 10 9 | var ShipHostnames [ShipCount]string = [ShipCount]string{ 10 | "gs136.pso2gs.net", 11 | "gs001.pso2gs.net", 12 | "gs016.pso2gs.net", 13 | "gs031.pso2gs.net", 14 | "gs046.pso2gs.net", 15 | "gs061.pso2gs.net", 16 | "gs076.pso2gs.net", 17 | "gs091.pso2gs.net", 18 | "gs106.pso2gs.net", 19 | "gs121.pso2gs.net", 20 | } 21 | 22 | type ShipEntry struct { 23 | Unk1, Number uint32 24 | NameRaw [0x10]uint16 25 | AddressRaw [4]uint8 26 | Zero, Unk2 uint32 27 | } 28 | 29 | type Ship struct { 30 | Entries [ShipCount]ShipEntry 31 | Unk [3]uint32 32 | } 33 | 34 | func (s *ShipEntry) Name() string { 35 | return DecodeString(s.NameRaw[:]) 36 | } 37 | 38 | func (s *ShipEntry) SetName(v string) { 39 | EncodeString(v, s.NameRaw[:]) 40 | } 41 | 42 | func (s *ShipEntry) Address() net.IP { 43 | return net.IP(s.AddressRaw[:]) 44 | } 45 | 46 | func (s *ShipEntry) SetAddress(v net.IP) { 47 | copy(s.AddressRaw[:], v.To4()) 48 | } 49 | 50 | func (s *Ship) Packet() (*Packet, error) { 51 | return PacketFromBinary(TypeShip, FlagProcessed, s) 52 | } 53 | 54 | func ParseShip(p *Packet) (*Ship, error) { 55 | s, err := PacketToBinary(p, &Ship{}) 56 | return s.(*Ship), err 57 | } 58 | -------------------------------------------------------------------------------- /net/packets/string.go: -------------------------------------------------------------------------------- 1 | package packets 2 | 3 | import ( 4 | "bytes" 5 | "unicode/utf16" 6 | "encoding/binary" 7 | ) 8 | 9 | func EncodeVariableString(v string, xor, sub int) []uint8 { 10 | data := utf16.Encode([]rune(v)) 11 | 12 | var odata bytes.Buffer 13 | end := binary.LittleEndian 14 | binary.Write(&odata, end, uint32(xor) ^ (uint32(len(data) + 1) + uint32(sub))) 15 | binary.Write(&odata, end, data) 16 | binary.Write(&odata, end, uint16(0)) 17 | return odata.Bytes() 18 | } 19 | 20 | func DecodeString(v []uint16) string { 21 | return string(utf16.Decode(v)) 22 | } 23 | 24 | func EncodeString(v string, buffer []uint16) { 25 | raw := utf16.Encode([]rune(v)) 26 | copy(buffer, raw) 27 | for i := len(raw); i < len(buffer); i++ { 28 | buffer[i] = 0 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /net/proxy.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "net" 5 | "fmt" 6 | "sync" 7 | "crypto/rsa" 8 | "aaronlindsay.com/go/pkg/pso2/net/packets" 9 | ) 10 | 11 | type Proxy struct { 12 | ServerEndpoint, ClientEndpoint string 13 | 14 | connections map[*Connection]*Connection 15 | connectionsLock sync.Mutex 16 | } 17 | 18 | func NewProxy(serverEndpoint, clientEndpoint string) *Proxy { 19 | return &Proxy{serverEndpoint, clientEndpoint, make(map[*Connection]*Connection), sync.Mutex{}} 20 | } 21 | 22 | func (p *Proxy) Listen() (net.Listener, error) { 23 | return net.Listen("tcp4", p.ServerEndpoint) 24 | } 25 | 26 | func (p *Proxy) Start(l net.Listener, serverRoute *PacketRoute, clientRoute *PacketRoute) error { 27 | for { 28 | conn, err := l.Accept() 29 | 30 | if err != nil { 31 | Logger.Errorf("%s %s listener error. %s", p, l, err) 32 | return err 33 | } 34 | 35 | go func() { 36 | Logger.Infof("%s new connection from %s", p, conn.RemoteAddr()) 37 | c := NewConnection(conn) 38 | client, err := p.Connect(c) 39 | if err != nil { 40 | Logger.Errorf("%s %s connection failed. %s", p, c, err) 41 | c.Close() 42 | } else { 43 | p.Proxy(c, serverRoute, client, clientRoute) 44 | c.Close() 45 | client.Close() 46 | } 47 | }() 48 | } 49 | } 50 | 51 | func (p *Proxy) Connect(c *Connection) (*Connection, error) { 52 | clientConn, err := net.Dial("tcp4", p.ClientEndpoint) 53 | 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | return NewConnection(clientConn), nil 59 | } 60 | 61 | func (p *Proxy) Proxy(server *Connection, serverRoute *PacketRoute, client *Connection, clientRoute *PacketRoute) error { 62 | Logger.Infof("%s Proxying connection %s", p, server) 63 | 64 | ch := make(chan error) 65 | 66 | k := func(c *Connection, r *PacketRoute) { 67 | err := c.RoutePackets(r) 68 | ch <- err 69 | } 70 | 71 | p.connectionsLock.Lock() 72 | p.connections[server] = client 73 | p.connections[client] = server 74 | p.connectionsLock.Unlock() 75 | 76 | go k(server, serverRoute) 77 | go k(client, clientRoute) 78 | 79 | var err error 80 | for i := 0; i < 2; i++ { 81 | e := <-ch 82 | if err == nil { 83 | err = e 84 | } 85 | 86 | server.Close() 87 | client.Close() 88 | } 89 | 90 | p.connectionsLock.Lock() 91 | delete(p.connections, server) 92 | delete(p.connections, client) 93 | p.connectionsLock.Unlock() 94 | 95 | Logger.Infof("%s Proxy %s closed. %s", p, server, err) 96 | 97 | return err 98 | } 99 | 100 | func (p *Proxy) Destination(c *Connection) (d *Connection) { 101 | p.connectionsLock.Lock() 102 | d = p.connections[c] 103 | p.connectionsLock.Unlock() 104 | return 105 | } 106 | 107 | func (p *Proxy) String() string { 108 | return fmt.Sprintf("[pso2/net/proxy: %s -> %s]", p.ServerEndpoint, p.ClientEndpoint) 109 | } 110 | 111 | type ProxyEndpointListener interface { 112 | EndpointAnnouncement(ip net.IP, port uint16) 113 | } 114 | 115 | func ProxyHandlerShip(p *Proxy, l ProxyEndpointListener, ip net.IP) PacketHandler { 116 | return proxyHandlerShip{p, l, ip} 117 | } 118 | 119 | type proxyHandlerShip struct { 120 | proxy *Proxy 121 | listener ProxyEndpointListener 122 | addr net.IP 123 | } 124 | 125 | func (h proxyHandlerShip) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 126 | Logger.Debugf("%s %s ship packet, rewriting addresses", h.proxy, c) 127 | 128 | s, err := packets.ParseShip(p) 129 | if err != nil { 130 | return false, err 131 | } 132 | 133 | for i := range s.Entries { 134 | e := &s.Entries[i] 135 | h.listener.EndpointAnnouncement(e.Address(), 12000 + (uint16(e.Number) % 10000)) 136 | e.SetAddress(h.addr) 137 | } 138 | 139 | p, err = s.Packet() 140 | 141 | if err != nil { 142 | return false, err 143 | } 144 | 145 | return true, h.proxy.Destination(c).WritePacket(p) 146 | } 147 | 148 | func ProxyHandlerBlocks(p *Proxy, l ProxyEndpointListener, ip net.IP) PacketHandler { 149 | return proxyHandlerBlocks{p, l, ip} 150 | } 151 | 152 | type proxyHandlerBlocks struct { 153 | proxy *Proxy 154 | listener ProxyEndpointListener 155 | addr net.IP 156 | } 157 | 158 | func (h proxyHandlerBlocks) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 159 | Logger.Debugf("%s %s block list packet, rewriting addresses", h.proxy, c) 160 | 161 | b, err := packets.ParseBlocks(p) 162 | if err != nil { 163 | return false, err 164 | } 165 | 166 | for i := range b.Entries { 167 | e := &b.Entries[i] 168 | h.listener.EndpointAnnouncement(e.Address(), e.Port) 169 | e.SetAddress(h.addr) 170 | } 171 | 172 | packetType := p.Type 173 | p, err = b.Packet() 174 | 175 | if err != nil { 176 | return false, err 177 | } 178 | 179 | p.Type = packetType 180 | 181 | return true, h.proxy.Destination(c).WritePacket(p) 182 | } 183 | 184 | func ProxyHandlerBlockResponse(p *Proxy, l ProxyEndpointListener, ip net.IP) PacketHandler { 185 | return proxyHandlerBlockResponse{p, l, ip} 186 | } 187 | 188 | type proxyHandlerBlockResponse struct { 189 | proxy *Proxy 190 | listener ProxyEndpointListener 191 | addr net.IP 192 | } 193 | 194 | func (h proxyHandlerBlockResponse) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 195 | Logger.Debugf("%s %s block response packet, rewriting address", h.proxy, c) 196 | 197 | b, err := packets.ParseBlockResponse(p) 198 | if err != nil { 199 | return false, err 200 | } 201 | 202 | h.listener.EndpointAnnouncement(b.Address(), b.Port) 203 | b.SetAddress(h.addr) 204 | 205 | p, err = b.Packet() 206 | 207 | if err != nil { 208 | return false, err 209 | } 210 | 211 | return true, h.proxy.Destination(c).WritePacket(p) 212 | } 213 | 214 | func ProxyHandlerBlock(p *Proxy, l ProxyEndpointListener, ip net.IP) PacketHandler { 215 | return proxyHandlerBlock{p, l, ip} 216 | } 217 | 218 | type proxyHandlerBlock struct { 219 | proxy *Proxy 220 | listener ProxyEndpointListener 221 | addr net.IP 222 | } 223 | 224 | func (h proxyHandlerBlock) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 225 | Logger.Debugf("%s %s block packet, rewriting address", h.proxy, c) 226 | 227 | b, err := packets.ParseBlock(p) 228 | if err != nil { 229 | return false, err 230 | } 231 | 232 | h.listener.EndpointAnnouncement(b.Address(), b.Port) 233 | b.SetAddress(h.addr) 234 | 235 | p, err = b.Packet() 236 | 237 | if err != nil { 238 | return false, err 239 | } 240 | 241 | return true, h.proxy.Destination(c).WritePacket(p) 242 | } 243 | 244 | func ProxyHandlerRoom(p *Proxy, l ProxyEndpointListener, ip net.IP) PacketHandler { 245 | return proxyHandlerRoom{p, l, ip} 246 | } 247 | 248 | type proxyHandlerRoom struct { 249 | proxy *Proxy 250 | listener ProxyEndpointListener 251 | addr net.IP 252 | } 253 | 254 | func (h proxyHandlerRoom) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 255 | Logger.Debugf("%s %s room packet, rewriting address", h.proxy, c) 256 | 257 | r, err := packets.ParseRoom(p) 258 | if err != nil { 259 | return false, err 260 | } 261 | 262 | h.listener.EndpointAnnouncement(r.Address(), r.Port) 263 | r.SetAddress(h.addr) 264 | 265 | packetType := p.Type 266 | p, err = r.Packet() 267 | 268 | if err != nil { 269 | return false, err 270 | } 271 | 272 | p.Type = packetType 273 | 274 | return true, h.proxy.Destination(c).WritePacket(p) 275 | } 276 | 277 | func ProxyHandlerCipher(p *Proxy, privateKey *rsa.PrivateKey, publicKey *rsa.PublicKey) PacketHandler { 278 | return proxyHandlerCipher{p, privateKey, publicKey} 279 | } 280 | 281 | type proxyHandlerCipher struct { 282 | proxy *Proxy 283 | privateKey *rsa.PrivateKey 284 | publicKey *rsa.PublicKey 285 | } 286 | 287 | func (h proxyHandlerCipher) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 288 | Logger.Debugf("%s %s cipher packet, re-encrypting", h.proxy, c) 289 | 290 | b, err := packets.ParseCipher(p) 291 | if err != nil { 292 | return false, err 293 | } 294 | 295 | key, err := b.Key(h.privateKey) 296 | if err != nil { 297 | return false, err 298 | } 299 | 300 | rc4key, err := packets.CipherRC4Key(key) 301 | if err != nil { 302 | return false, err 303 | } 304 | 305 | b.SetKey(key, h.publicKey) 306 | 307 | p, err = b.Packet() 308 | if err != nil { 309 | return false, err 310 | } 311 | 312 | dest := h.proxy.Destination(c) 313 | err = dest.WritePacket(p) 314 | if err == nil { 315 | err = dest.SetCipher(rc4key) 316 | } 317 | 318 | return true, err 319 | } 320 | 321 | func ProxyHandlerFallback(p *Proxy) PacketHandler { 322 | return proxyHandlerFallback{p} 323 | } 324 | 325 | type proxyHandlerFallback struct { 326 | proxy *Proxy 327 | } 328 | 329 | func (h proxyHandlerFallback) HandlePacket(c *Connection, p *packets.Packet) (bool, error) { 330 | Logger.Tracef("%s %s unknown packet %s, forwarding", h.proxy, c, p) 331 | 332 | return true, h.proxy.Destination(c).WritePacket(p) 333 | } 334 | -------------------------------------------------------------------------------- /net/routing.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "sort" 5 | "aaronlindsay.com/go/pkg/pso2/net/packets" 6 | ) 7 | 8 | const ( 9 | RoutePriorityLow int = -100 10 | RoutePriorityNormal = 0 11 | RoutePriorityHigh = 100 12 | ) 13 | 14 | type PacketHandler interface { 15 | HandlePacket(c *Connection, p *packets.Packet) (bool, error) 16 | } 17 | 18 | type packetHandlerItem struct { 19 | handler PacketHandler 20 | priority int 21 | } 22 | 23 | type packetHandlerMaskItem struct { 24 | packetHandlerItem 25 | mask uint16 26 | } 27 | 28 | type PacketRoute struct { 29 | items map[uint16][]packetHandlerItem 30 | masks []packetHandlerMaskItem 31 | } 32 | 33 | func (r *PacketRoute) Route(t uint16, prio int, h PacketHandler) { 34 | if r.items == nil { 35 | r.items = make(map[uint16][]packetHandlerItem) 36 | } 37 | 38 | r.items[t] = append(r.items[t], packetHandlerItem{h, prio}) 39 | } 40 | 41 | func (r *PacketRoute) RouteMask(t uint16, prio int, h PacketHandler) { 42 | r.masks = append(r.masks, packetHandlerMaskItem{packetHandlerItem{h, prio}, t}) 43 | } 44 | 45 | type packetHandlerList []*packetHandlerItem 46 | func (p packetHandlerList) Len() int { 47 | return len(p) 48 | } 49 | 50 | func (p packetHandlerList) Less(i, j int) bool { 51 | return p[i].priority > p[j].priority 52 | } 53 | 54 | func (p packetHandlerList) Swap(i, j int) { 55 | p[i], p[j] = p[j], p[i] 56 | } 57 | 58 | func (r *PacketRoute) RoutePacket(c *Connection, p *packets.Packet) (consumed bool, err error) { 59 | items := make(packetHandlerList, 0, 4) 60 | 61 | for i, h := range r.masks { 62 | if h.mask & p.Type != 0 { 63 | items = append(items, &r.masks[i].packetHandlerItem) 64 | } 65 | } 66 | 67 | if l, ok := r.items[p.Type]; ok { 68 | for i := range l { 69 | items = append(items, &l[i]) 70 | } 71 | } 72 | 73 | sort.Sort(items) 74 | 75 | for _, i := range items { 76 | consumed, err = i.handler.HandlePacket(c, p) 77 | 78 | if consumed || err != nil { 79 | return 80 | } 81 | } 82 | 83 | return 84 | } 85 | -------------------------------------------------------------------------------- /pso2-download.md: -------------------------------------------------------------------------------- 1 | ## pso2-download 2 | 3 | [**Download pso2-download.exe**](pso2-download.exe) - [Screenshot!](pso2-download-screen.png) 4 | 5 | pso2-download is an updater, launcher, and language patcher for Phantasy Star Online 2. It automatically performs quick updates and keeps patches instantly up to date as soon as the game is updated. Patches are downloaded in a very bandwidth-conscious way such that the entire game can be translated with about 6MB. 6 | 7 | Set it up once, forget about it, and just use it to launch pso2 from now on. The game and any patches will automatically be kept up to date for you. To start using it, do the following... 8 | 9 | 1. Create a shortcut to the exe (hold alt and drag pso2-download.exe to your desktop, for example) 10 | 11 | 2. Open up the shortcut's properties 12 | 13 | 3. In the Shortcut tab, add any flags you want to use to the end of the Target line. The following flags are recommended for a full english patch: 14 | 15 | -l -d -i -t eng,story-eng -b 16 | 17 | You may omit `-i` to disable the item translation, and the `-t eng,story-eng` part to disable the english patch. Get rid of `-l` if you only want to use the update and patching functionality. 18 | 4. After all flags, add the path to your pso2_bin folder. Like this example Target: 19 | 20 | "C:\pso2-download.exe" -l -d -i -t eng,story-eng -b -pubkey publickey.blob "C:\Program Files (x86)\SEGA\PHANTASYSTARONLINE2\pso2_bin" 21 | 22 | 5. In the Compatibility tab, check "Run this program as an administrator" (optional, but required if you want it to actually launch the game. Skip if you only use this as a downloader/patcher). 23 | 24 | 6. Launch the shortcut. It will do any downloading/updating/patching only when necessary, then start the game for you. 25 | 26 | 27 | **Warning** upon first run the launcher will need to do a quick file check. If it finds anything modified (such as english patches), the files will be redownloaded and then repatched. You may want to uninstall your patches from a backup beforehand to avoid any wasted bandwidth. 28 | 29 | 30 | ### Other Usage Examples 31 | 32 | - Revert your pso2 installation to an original, unpatched state by hash checking all files and downloading any that have been modified/corrupt/etc. 33 | 34 | "C:\pso2-download.exe" -h -a -d "C:\Program Files (x86)\SEGA\PHANTASYSTARONLINE2\pso2_bin" 35 | 36 | Note, you may want to copy the files from `pso2_bin/download/backup` beforehand (if it exists) in order to lessen the load of files that need redownloading. 37 | 38 | - Remove any files wasting space in your PSO2 folder 39 | 40 | "C:\pso2-download.exe" -u -g "C:\Program Files (x86)\SEGA\PHANTASYSTARONLINE2\pso2_bin" 41 | 42 | 43 | ### Flags 44 | 45 | There are a whole lot of flags that pso2-download.exe accepts. 46 | 47 | - `-l` Launch the game after performing all other operations 48 | - `-d` Download any files that have changed since the last update 49 | - `-i` Use the english item translation patch 50 | - `-t` Apply the specified english translations by name (currently only eng and story-eng exist) 51 | - `-b` Back up any files before patching them. This places files under `pso2_bin/download/backup`, which can be copied back into `data/win32` to restore the game without a huge download 52 | - `-pubkey path.blob` Inject the specified public key into PSO2. Used for [PSO2Proxy](http://pso2proxy.cyberkitsune.net) 53 | - `-a` Consider all files unupdated. Useful in conjunction with -c and -h 54 | - `-c` Check files to determine whether any have been changed 55 | - `-h` Hash all files when checking them. A more time-intensive but thorough version of the -c flag 56 | - `-g` Clean up any unused garbage files left over from previous updates/installs 57 | - `-u` Force a redownload of the file list without checking for a new version 58 | - `-dumppubkey path.blob` dumps the Sega public key to a file 59 | -------------------------------------------------------------------------------- /text/tagfile.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | // NOTE: This file is all wrong, by the way. Only TagRead() works, the rest can be thrown out 4 | 5 | import ( 6 | "io" 7 | "aaronlindsay.com/go/pkg/pso2/util" 8 | "github.com/quarnster/util/encoding/binary" 9 | bin "encoding/binary" 10 | ) 11 | 12 | type TagFileEntry struct { 13 | Tag string `length:"4"` 14 | Size uint32 15 | 16 | Data io.ReadSeeker `if:"0"` 17 | } 18 | 19 | type TagFile struct { 20 | Entries []TagFileEntry 21 | 22 | reader io.ReadSeeker 23 | } 24 | 25 | func NewTagFile(reader io.ReadSeeker) (*TagFile, error) { 26 | f := &TagFile{ nil, reader } 27 | return f, f.parse() 28 | } 29 | 30 | func (f *TagFile) parse() error { 31 | var err error 32 | reader := binary.BinaryReader{ Reader: f.reader, Endianess: binary.LittleEndian }; 33 | 34 | offset := int64(0) 35 | 36 | var entry TagFileEntry 37 | for err = reader.ReadInterface(&entry); err == nil; err = reader.ReadInterface(&entry) { 38 | offset += 8 39 | entry.Data = io.NewSectionReader(util.ReaderAt(f.reader), offset, int64(entry.Size)) 40 | 41 | f.Entries = append(f.Entries, entry) 42 | 43 | _, err = f.reader.Seek(int64(entry.Size), 1) 44 | offset += int64(entry.Size) 45 | } 46 | 47 | if err == io.EOF { 48 | return nil 49 | } 50 | 51 | return err 52 | } 53 | 54 | func TagRead(r io.ReadSeeker) (TagFileEntry, error) { 55 | reader := binary.BinaryReader{ Reader: r, Endianess: binary.LittleEndian }; 56 | 57 | var err error 58 | var entry TagFileEntry 59 | if err = reader.ReadInterface(&entry); err == nil { 60 | offset, err := r.Seek(0, 1) 61 | entry.Data = io.NewSectionReader(util.ReaderAt(r), offset, int64(entry.Size)) 62 | 63 | return entry, err 64 | } 65 | 66 | return TagFileEntry{}, err 67 | } 68 | 69 | func (f *TagFile) Write(writer io.Writer) (err error) { 70 | var n int64 71 | end := bin.LittleEndian 72 | 73 | for _, entry := range f.Entries { 74 | size := (entry.Size + 7) / 8 * 8 75 | 76 | bin.Write(writer, end, entry.Tag) 77 | bin.Write(writer, end, size) 78 | 79 | n, err = io.CopyN(writer, entry.Data, int64(entry.Size)) 80 | 81 | if err == nil || err == io.EOF { 82 | if n < int64(size) { 83 | err = bin.Write(writer, end, make([]uint8, int64(size) - n)) 84 | } 85 | } 86 | 87 | if err != nil { 88 | return 89 | } 90 | } 91 | 92 | return 93 | } 94 | -------------------------------------------------------------------------------- /text/textfile.go: -------------------------------------------------------------------------------- 1 | package text 2 | 3 | import ( 4 | "io" 5 | "errors" 6 | "aaronlindsay.com/go/pkg/pso2/util" 7 | "github.com/quarnster/util/encoding/binary" 8 | "unicode/utf8" 9 | bin "encoding/binary" 10 | ) 11 | 12 | const textBufferThreshold = 0x80000 13 | const textBufferDataThreshold = 0x800000 14 | 15 | type TextPair struct { 16 | Identifier, String string 17 | 18 | identifierIndex, stringIndex int 19 | } 20 | 21 | type TextFile struct { 22 | Entries []TextEntry 23 | 24 | Pairs []TextPair 25 | } 26 | 27 | type TextEntry struct { 28 | Value []uint32 29 | Text string 30 | TextStatus int 31 | } 32 | 33 | const ( 34 | TextEntryNone = iota 35 | TextEntryString 36 | TextEntryIdentifier 37 | ) 38 | 39 | func NewTextFile(reader io.ReadSeeker) (*TextFile, error) { 40 | f := &TextFile{nil, nil} 41 | return f, f.parse(reader) 42 | } 43 | 44 | func (t *TextFile) parse(r io.ReadSeeker) (err error) { 45 | nifl, err := TagRead(r) 46 | 47 | if err != nil { 48 | return err 49 | } 50 | 51 | if nifl.Tag != "NIFL" { 52 | return errors.New("NIFL tag expected") 53 | } 54 | 55 | reader := binary.BinaryReader{ Reader: nifl.Data, Endianess: binary.LittleEndian } 56 | 57 | type niflHeaderType struct { 58 | Unk, OffsetREL0, SizeREL0, OffsetNOF0, SizeNOF0 uint32 59 | } 60 | 61 | var niflHeader niflHeaderType 62 | if err = reader.ReadInterface(&niflHeader); err != nil { 63 | return err 64 | } 65 | 66 | if niflHeader.Unk != 1 { 67 | return errors.New("NIFL header magic != 1") 68 | } 69 | 70 | r.Seek(int64(niflHeader.OffsetREL0), 0) 71 | 72 | rel0, err := TagRead(r) 73 | if rel0.Tag != "REL0" { 74 | return errors.New("REL0 tag expected") 75 | } 76 | 77 | var rel0data io.ReadSeeker 78 | var rel0strings io.ReadSeeker 79 | reader = binary.BinaryReader{ Reader: rel0.Data, Endianess: binary.LittleEndian } 80 | rel0size, err := reader.Uint32() 81 | rel0data = io.NewSectionReader(util.ReaderAt(rel0.Data), 8, int64(rel0size) - 8) 82 | rel0strings = io.NewSectionReader(util.ReaderAt(rel0.Data), int64(rel0size), int64(rel0.Size - rel0size)) 83 | 84 | if rel0size < textBufferDataThreshold { 85 | rel0data, err = util.MemReader(rel0data) 86 | if err != nil { 87 | return err 88 | } 89 | } 90 | 91 | if rel0.Size - rel0size < textBufferThreshold { 92 | rel0strings, err = util.MemReader(rel0strings) 93 | if err != nil { 94 | return err 95 | } 96 | } 97 | 98 | r.Seek(int64(niflHeader.OffsetNOF0), 0) 99 | 100 | nof0, err := TagRead(r) 101 | if nof0.Tag != "NOF0" { 102 | return errors.New("NOF0 tag expected") 103 | } 104 | 105 | nof0reader := binary.BinaryReader{ Reader: nof0.Data, Endianess: binary.LittleEndian } 106 | count, err := nof0reader.Uint32() 107 | offsets := make([]uint32, int(count) + 1) 108 | i := 0 109 | for offset, _ := nof0reader.Uint32(); i < int(count); i++ { 110 | end, _ := nof0reader.Uint32() 111 | 112 | offsets[i] = end - offset 113 | 114 | offset = end 115 | 116 | if offsets[i] % 4 != 0 { 117 | return errors.New("nof0 entry not a multiple of 32 bits") 118 | } 119 | } 120 | offsets[i] = 8 121 | 122 | t.Entries = make([]TextEntry, len(offsets)) 123 | 124 | rel0reader := binary.BinaryReader{ Reader: rel0data, Endianess: binary.LittleEndian } 125 | 126 | pairMode := false 127 | var pair *string 128 | var pairi int 129 | for i, offset := range offsets { 130 | entry := &t.Entries[i] 131 | 132 | entry.Value = make([]uint32, offset / 4) 133 | for i := 0; i < int(offset / 4); i++ { 134 | entry.Value[i], err = rel0reader.Uint32() 135 | } 136 | 137 | if entry.Value[0] == 0xffffffff { 138 | pairMode = true 139 | } else if entry.Value[0] == 0x14 { 140 | pairMode = false 141 | pair = nil 142 | } 143 | 144 | if len(entry.Value) == 1 && entry.Value[0] != 0xffffffff { 145 | rel0strings.Seek(int64(entry.Value[0] - rel0size - 8), 0) 146 | charSize := 1 147 | if pair != nil { 148 | charSize = 2 149 | } 150 | entry.Text, _ = readString(charSize, rel0strings) 151 | 152 | if pair != nil { 153 | entry.TextStatus = TextEntryString 154 | t.Pairs = append(t.Pairs, TextPair{*pair, entry.Text, pairi, i}) 155 | pair = nil 156 | } else { 157 | entry.TextStatus = TextEntryIdentifier 158 | if pairMode { 159 | pair = &entry.Text 160 | pairi = i 161 | } 162 | } 163 | } 164 | } 165 | 166 | _, err = r.Seek(int64((nof0.Size + 8 + 0x0f) / 0x10 * 0x10) - 8, 1) 167 | 168 | nend, err := TagRead(r) 169 | if nend.Tag != "NEND" { 170 | return errors.New("NEND tag expected") 171 | } 172 | 173 | return err 174 | } 175 | 176 | func readString(charSize int, reader io.Reader) (string, error) { 177 | stringValue := make([]rune, 0) 178 | for len(stringValue) == 0 || stringValue[len(stringValue) - 1] != 0 { 179 | p := make([]uint8, charSize) 180 | _, err := reader.Read(p) 181 | 182 | if err != nil { 183 | return string(stringValue), err 184 | } 185 | 186 | if charSize == 1 { 187 | stringValue = append(stringValue, rune(p[0])) 188 | } else { 189 | stringValue = append(stringValue, rune((uint16(p[1]) << 8) | uint16(p[0]))) 190 | } 191 | } 192 | 193 | return string(stringValue[:len(stringValue) - 1]), nil 194 | } 195 | 196 | func (t *TextFile) Write(writer io.Writer) error { 197 | end := bin.LittleEndian 198 | 199 | // TODO: This reuse detection is pretty bad... 200 | stringReuse := make(map[string]TextEntry) 201 | 202 | entrySize := uint32(0) 203 | stringSize := uint32(0) 204 | for _, entry := range t.Entries { 205 | entrySize += uint32(len(entry.Value) * 4) 206 | 207 | if entry.TextStatus != TextEntryNone { 208 | if reuse, ok := stringReuse[entry.Text]; ok && reuse.TextStatus == entry.TextStatus { 209 | entry.Value[0] = reuse.Value[0] 210 | } else { 211 | entry.Value[0] = stringSize 212 | 213 | charSize := 1 214 | strlen := len(entry.Text) 215 | if entry.TextStatus == TextEntryString { 216 | charSize = 2 217 | strlen = utf8.RuneCountInString(entry.Text) 218 | } 219 | 220 | stringSize += uint32(charSize * (strlen + 1)) 221 | stringSize = (stringSize + 3) / 4 * 4 222 | 223 | stringReuse[entry.Text] = entry 224 | } 225 | } 226 | } 227 | 228 | rel0size := entrySize + stringSize + 0x10 229 | rel0sizeRounded := (rel0size + 0x0f) / 0x10 * 0x10 230 | nof0size := uint32(len(t.Entries) + 1) * 4 + 8 231 | nof0sizeRounded := (nof0size + 0x0f) / 0x10 * 0x10 232 | 233 | io.WriteString(writer, "NIFL") 234 | bin.Write(writer, end, uint32(0x18)) 235 | bin.Write(writer, end, uint32(0x01)) 236 | bin.Write(writer, end, uint32(0x20)) 237 | bin.Write(writer, end, rel0sizeRounded) 238 | bin.Write(writer, end, rel0sizeRounded + 0x20) 239 | bin.Write(writer, end, nof0sizeRounded) 240 | bin.Write(writer, end, uint32(0)) 241 | 242 | io.WriteString(writer, "REL0") 243 | bin.Write(writer, end, rel0size - 8) 244 | bin.Write(writer, end, entrySize + 8) 245 | bin.Write(writer, end, uint32(0)) 246 | 247 | for _, entry := range t.Entries { 248 | if entry.TextStatus != TextEntryNone { 249 | entry.Value[0] += entrySize + 0x10 250 | } 251 | 252 | bin.Write(writer, end, entry.Value) 253 | } 254 | 255 | stringReuse = make(map[string]TextEntry) 256 | 257 | for _, entry := range t.Entries { 258 | if reuse, ok := stringReuse[entry.Text]; ok && reuse.TextStatus == entry.TextStatus { 259 | continue 260 | } 261 | 262 | strlen := len(entry.Text) + 1 263 | switch entry.TextStatus { 264 | case TextEntryIdentifier: 265 | io.WriteString(writer, entry.Text) 266 | bin.Write(writer, end, uint8(0)) 267 | case TextEntryString: 268 | for _, r := range entry.Text { 269 | bin.Write(writer, end, uint16(r)) 270 | } 271 | bin.Write(writer, end, uint16(0)) 272 | strlen = (utf8.RuneCountInString(entry.Text) + 1) * 2 273 | default: 274 | continue 275 | } 276 | 277 | padding := 4 - (strlen % 4) 278 | if padding < 4 { 279 | writer.Write(make([]uint8, padding)) 280 | } 281 | 282 | stringReuse[entry.Text] = entry 283 | } 284 | 285 | writer.Write(make([]uint8, rel0sizeRounded - rel0size)) 286 | 287 | io.WriteString(writer, "NOF0") 288 | bin.Write(writer, end, nof0size - 8) 289 | bin.Write(writer, end, uint32(len(t.Entries) - 1)) 290 | 291 | offset := uint32(0x10) 292 | for _, entry := range t.Entries { 293 | bin.Write(writer, end, offset) 294 | offset += uint32(len(entry.Value) * 4) 295 | } 296 | 297 | writer.Write(make([]uint8, nof0sizeRounded - nof0size)) 298 | 299 | io.WriteString(writer, "NEND") 300 | bin.Write(writer, end, uint32(0x08)) 301 | bin.Write(writer, end, uint32(0)) 302 | bin.Write(writer, end, uint32(0)) 303 | 304 | return nil 305 | } 306 | 307 | func (t *TextFile) PairIdentifier(p *TextPair) *TextEntry { 308 | return &t.Entries[p.identifierIndex] 309 | } 310 | 311 | func (t *TextFile) PairString(p *TextPair) *TextEntry { 312 | return &t.Entries[p.stringIndex] 313 | } 314 | -------------------------------------------------------------------------------- /trans/cmd/trans.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "time" 7 | "sync" 8 | "bufio" 9 | "errors" 10 | "io/ioutil" 11 | "github.com/cheggaaa/pb" 12 | "aaronlindsay.com/go/pkg/pso2/ice" 13 | "aaronlindsay.com/go/pkg/pso2/text" 14 | "aaronlindsay.com/go/pkg/pso2/util" 15 | "aaronlindsay.com/go/pkg/pso2/trans" 16 | ) 17 | 18 | func StripDatabase(dbpath, flagStrip string) (err error) { 19 | err = util.CopyFile(dbpath, flagStrip) 20 | if err != nil { 21 | return 22 | } 23 | 24 | db, err := trans.NewDatabase(flagStrip) 25 | if err != nil { 26 | return 27 | } 28 | 29 | err = db.Strip() 30 | 31 | db.Close() 32 | 33 | return 34 | } 35 | 36 | func PatchFiles(db *trans.Database, pso2dir, translationName, backupPath, outputPath string, parallel int) (errs []error) { 37 | translation, err := db.QueryTranslation(translationName) 38 | if err == nil && translation == nil { 39 | err = errors.New("translation not found") 40 | } 41 | if err != nil { 42 | return []error{err} 43 | } 44 | 45 | archives, err := db.QueryArchivesTranslation(translation) 46 | if err != nil { 47 | return []error{err} 48 | } 49 | 50 | pbar := pb.New(len(archives)) 51 | pbar.SetRefreshRate(time.Second / 10) 52 | pbar.Start() 53 | 54 | queue := make(chan *trans.Archive) 55 | done := make(chan bool) 56 | 57 | errlock := sync.Mutex{} 58 | 59 | complain := func(err error) bool { 60 | if err != nil { 61 | errlock.Lock() 62 | errs = append(errs, err) 63 | errlock.Unlock() 64 | return true 65 | } 66 | 67 | return false 68 | } 69 | 70 | for i := 0; i < parallel; i++ { 71 | go func() { 72 | for { 73 | a, ok := <-queue 74 | if !ok { 75 | break 76 | } 77 | 78 | aname := path.Join(pso2dir, a.Name.String()) 79 | af, err := os.OpenFile(aname, os.O_RDONLY, 0); 80 | if complain(err) { 81 | continue 82 | } 83 | 84 | archive, err := ice.NewArchive(util.BufReader(af)) 85 | if complain(err) { 86 | continue 87 | } 88 | 89 | files, err := db.QueryFiles(a) 90 | if complain(err) { 91 | continue 92 | } 93 | 94 | fileDirty := false 95 | 96 | var textfiles []*os.File 97 | for _, f := range files { 98 | tstrings, err := db.QueryTranslationStringsFile(translation, &f) 99 | if complain(err) || len(tstrings) == 0 { 100 | continue 101 | } 102 | 103 | strings := make([]*trans.String, len(tstrings)) 104 | for i, ts := range tstrings { 105 | strings[i], err = db.QueryStringTranslation(&ts) 106 | complain(err) 107 | } 108 | 109 | file := archive.FindFile(-1, f.Name) 110 | if file == nil { 111 | if complain(errors.New(f.Name + ": file not found")) { 112 | continue 113 | } 114 | } 115 | textfile, err := text.NewTextFile(file.Data) 116 | 117 | collisions := make(map[string]int) 118 | 119 | for _, p := range textfile.Pairs { 120 | collision := collisions[p.Identifier] 121 | collisions[p.Identifier] = collision + 1 122 | 123 | var ts *trans.TranslationString 124 | for i, s := range strings { 125 | if s.Identifier == p.Identifier && s.Collision == collision { 126 | ts = &tstrings[i] 127 | break 128 | } 129 | } 130 | if ts == nil { 131 | continue 132 | } 133 | 134 | if p.String != ts.Translation { 135 | entry := textfile.PairString(&p) 136 | entry.Text = ts.Translation 137 | fileDirty = true 138 | } 139 | } 140 | 141 | tf, err := ioutil.TempFile("", "") 142 | if complain(err) { 143 | continue 144 | } 145 | 146 | writer := bufio.NewWriter(tf) 147 | err = textfile.Write(writer) 148 | writer.Flush() 149 | if complain(err) { 150 | tf.Close() 151 | os.Remove(tf.Name()) 152 | continue 153 | } 154 | pos, _ := tf.Seek(0, 1) 155 | tf.Seek(0, 0) 156 | 157 | archive.ReplaceFile(file, tf, uint32(pos)) 158 | textfiles = append(textfiles, tf) 159 | } 160 | 161 | if fileDirty { 162 | ofile, err := ioutil.TempFile("", "") 163 | 164 | aname := path.Join(outputPath, a.Name.String()) 165 | 166 | if !complain(err) { 167 | backupPath := backupPath 168 | if archive.IsModified() { 169 | backupPath = "" 170 | } 171 | 172 | writer := bufio.NewWriter(ofile) 173 | err = archive.Write(writer) 174 | writer.Flush() 175 | ofile.Close() 176 | 177 | if backupPath != "" { 178 | opath := path.Join(backupPath, path.Base(aname)) 179 | err = os.Rename(aname, opath) 180 | if err != nil { 181 | err = util.CopyFile(aname, opath) 182 | } 183 | } 184 | 185 | if complain(err) { 186 | os.Remove(ofile.Name()) 187 | } else { 188 | err = os.Rename(ofile.Name(), aname) 189 | if err != nil { 190 | err = util.CopyFile(ofile.Name(), aname) 191 | os.Remove(ofile.Name()) 192 | } 193 | complain(err) 194 | } 195 | } 196 | } 197 | 198 | for _, tf := range textfiles { 199 | tf.Close() 200 | os.Remove(tf.Name()) 201 | } 202 | 203 | af.Close() 204 | 205 | pbar.Increment() 206 | } 207 | 208 | done <-true 209 | }() 210 | } 211 | 212 | for i := range archives { 213 | queue <-&archives[i] 214 | } 215 | close(queue) 216 | 217 | for i := 0; i < parallel; i++ { 218 | <-done 219 | } 220 | 221 | pbar.Finish() 222 | 223 | return 224 | } 225 | -------------------------------------------------------------------------------- /trans/db.go: -------------------------------------------------------------------------------- 1 | package trans 2 | 3 | import ( 4 | "database/sql" 5 | _ "github.com/mattn/go-sqlite3" 6 | ) 7 | 8 | type Database struct { 9 | db *sql.DB 10 | } 11 | 12 | const sqlCreateTables = ` 13 | CREATE TABLE IF NOT EXISTS archives ( 14 | archiveid INTEGER PRIMARY KEY NOT NULL, 15 | name BLOB UNIQUE NOT NULL 16 | ); 17 | 18 | CREATE TABLE IF NOT EXISTS files ( 19 | fileid INTEGER PRIMARY KEY NOT NULL, 20 | archiveid INTEGER REFERENCES archives(archiveid) NOT NULL, 21 | name TEXT NOT NULL 22 | ); 23 | 24 | CREATE TABLE IF NOT EXISTS strings ( 25 | stringid INTEGER PRIMARY KEY NOT NULL, 26 | fileid INTEGER REFERENCES files(fileid) NOT NULL, 27 | version INTEGER NOT NULL, 28 | collision INTEGER NOT NULL, 29 | identifier TEXT NOT NULL, 30 | value TEXT NOT NULL 31 | ); 32 | CREATE UNIQUE INDEX IF NOT EXISTS strings_index ON strings(fileid, identifier, collision); 33 | 34 | CREATE TABLE IF NOT EXISTS translations ( 35 | translationid INTEGER PRIMARY KEY NOT NULL, 36 | name TEXT NOT NULL 37 | ); 38 | 39 | CREATE TABLE IF NOT EXISTS translationstrings ( 40 | translationid INTEGER REFERENCES translations(translationid) NOT NULL, 41 | stringid INTEGER REFERENCES strings(stringid) NOT NULL, 42 | translation TEXT NOT NULL 43 | ); 44 | CREATE UNIQUE INDEX IF NOT EXISTS translationstrings_index ON translationstrings(translationid, stringid); 45 | ` 46 | 47 | 48 | func NewDatabase(path string) (*Database, error){ 49 | db, err := sql.Open("sqlite3", path) 50 | 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | _, err = db.Exec(sqlCreateTables) 56 | 57 | return &Database{db}, err 58 | } 59 | 60 | func (d *Database) Close() { 61 | d.db.Close() 62 | } 63 | 64 | func (d *Database) Begin() { 65 | d.db.Exec("BEGIN") 66 | } 67 | 68 | func (d *Database) End() { 69 | d.db.Exec("END") 70 | } 71 | 72 | func (d *Database) query(query string, args ...interface{}) (rows *sql.Rows, err error) { 73 | stmt, err := d.db.Prepare(query) 74 | if err != nil { 75 | return 76 | } 77 | 78 | rows, err = stmt.Query(args...) 79 | 80 | stmt.Close() 81 | 82 | return 83 | } 84 | 85 | func (d *Database) exec(query string, args ...interface{}) (result sql.Result, err error) { 86 | stmt, err := d.db.Prepare(query) 87 | if err != nil { 88 | return 89 | } 90 | 91 | result, err = stmt.Exec(args...) 92 | 93 | stmt.Close() 94 | 95 | return 96 | } 97 | 98 | func (d *Database) InsertArchive(name *ArchiveName) (a *Archive, err error) { 99 | result, err := d.exec("INSERT INTO archives (name) VALUES (?)", name[:]) 100 | if err != nil { 101 | return 102 | } 103 | 104 | id, err := result.LastInsertId() 105 | a = &Archive{id, d, *name} 106 | 107 | return 108 | } 109 | 110 | const sqlQueryArchive = "SELECT archiveid, name FROM archives " 111 | func (d *Database) queryArchive(rows *sql.Rows) (a *Archive, err error) { 112 | a = &Archive{} 113 | a.DB = d 114 | var name []uint8 115 | err = rows.Scan(&a.id, &name) 116 | if err != nil { 117 | a = nil 118 | } else { 119 | copy(a.Name[:], name) 120 | } 121 | 122 | return 123 | } 124 | 125 | func (d *Database) queryArchives(query string, args ...interface{}) (archives []Archive, err error) { 126 | rows, err := d.query(query, args...) 127 | if err != nil { 128 | return 129 | } 130 | 131 | for rows.Next() { 132 | var a *Archive 133 | a, err = d.queryArchive(rows) 134 | if err != nil { 135 | break 136 | } 137 | 138 | archives = append(archives, *a) 139 | } 140 | 141 | rows.Close() 142 | return 143 | } 144 | 145 | func (d *Database) QueryArchive(name *ArchiveName) (a *Archive, err error) { 146 | rows, err := d.query(sqlQueryArchive + "WHERE name = ?", name[:]) 147 | if err != nil { 148 | return nil, err 149 | } 150 | 151 | if rows.Next() { 152 | a, err = d.queryArchive(rows) 153 | } 154 | 155 | rows.Close() 156 | return 157 | } 158 | 159 | func (d *Database) QueryArchives() (archives []Archive, err error) { 160 | return d.queryArchives(sqlQueryArchive) 161 | } 162 | 163 | func (d *Database) QueryArchivesTranslation(t *Translation) (archives []Archive, err error) { 164 | return d.queryArchives(` 165 | SELECT a.archiveid, a.name 166 | FROM translationstrings AS ts 167 | JOIN strings AS s ON s.stringid = ts.stringid 168 | JOIN files AS f ON f.fileid = s.fileid 169 | JOIN archives AS a ON a.archiveid = f.archiveid 170 | WHERE ts.translationid = ? 171 | GROUP BY a.archiveid`, t.id) 172 | } 173 | 174 | func (d *Database) InsertFile(a *Archive, name string) (f *File, err error) { 175 | result, err := d.exec("INSERT INTO files (archiveid, name) VALUES (?, ?)", a.id, name) 176 | if err != nil { 177 | return 178 | } 179 | 180 | id, err := result.LastInsertId() 181 | f = &File{id, a.id, d, name} 182 | 183 | return 184 | } 185 | 186 | const sqlQueryFile = "SELECT fileid, archiveid, name FROM files " 187 | func (d *Database) queryFile(rows *sql.Rows) (f *File, err error) { 188 | f = &File{} 189 | f.DB = d 190 | err = rows.Scan(&f.id, &f.archiveid, &f.Name) 191 | if err != nil { 192 | f = nil 193 | } 194 | 195 | return 196 | } 197 | 198 | func (d *Database) QueryFile(a *Archive, name string) (f *File, err error) { 199 | rows, err := d.query(sqlQueryFile + "WHERE archiveid = ? AND name = ?", a.id, name) 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | if rows.Next() { 205 | f, err = d.queryFile(rows) 206 | } 207 | 208 | rows.Close() 209 | return 210 | } 211 | 212 | func (d *Database) QueryFiles(a *Archive) (files []File, err error) { 213 | rows, err := d.query(sqlQueryFile + "WHERE archiveid = ?", a.id) 214 | if err != nil { 215 | return 216 | } 217 | 218 | for rows.Next() { 219 | var f *File 220 | f, err = d.queryFile(rows) 221 | if err != nil { 222 | break 223 | } 224 | 225 | files = append(files, *f) 226 | } 227 | 228 | rows.Close() 229 | return 230 | } 231 | 232 | func (d *Database) InsertString(f *File, version, collision int, identifier, value string) (s *String, err error) { 233 | result, err := d.exec("INSERT INTO strings (fileid, version, collision, identifier, value) VALUES (?, ?, ?, ?, ?)", f.id, version, collision, identifier, value) 234 | if err != nil { 235 | return 236 | } 237 | 238 | id, err := result.LastInsertId() 239 | s = &String{id, f.id, d, version, collision, identifier, value} 240 | 241 | return 242 | } 243 | 244 | func (d *Database) UpdateString(f *String, version int, value string) (s *String, err error) { 245 | _, err = d.exec("UPDATE strings SET version = ?, value = ? WHERE stringid = ?", version, value, f.id) 246 | if err != nil { 247 | return 248 | } 249 | 250 | s = &String{f.id, f.fileid, d, version, f.Collision, f.Identifier, value} 251 | 252 | return 253 | } 254 | 255 | const sqlQueryString = "SELECT stringid, fileid, version, collision, identifier, value FROM strings " 256 | func (d *Database) queryString(rows *sql.Rows) (s *String, err error) { 257 | s = &String{} 258 | s.DB = d 259 | err = rows.Scan(&s.id, &s.fileid, &s.Version, &s.Collision, &s.Identifier, &s.Value) 260 | if err != nil { 261 | s = nil 262 | } 263 | 264 | return 265 | } 266 | 267 | func (d *Database) QueryString(f *File, collision int, id string) (s *String, err error) { 268 | rows, err := d.query(sqlQueryString + "WHERE fileid = ? AND collision = ? AND identifier = ?", f.id, collision, id) 269 | if err != nil { 270 | return nil, err 271 | } 272 | 273 | if rows.Next() { 274 | s, err = d.queryString(rows) 275 | } 276 | 277 | rows.Close() 278 | return 279 | } 280 | 281 | func (d *Database) QueryStringTranslation(t *TranslationString) (s *String, err error) { 282 | rows, err := d.query(sqlQueryString + "WHERE stringid = ?", t.stringid) 283 | if err != nil { 284 | return nil, err 285 | } 286 | 287 | if rows.Next() { 288 | s, err = d.queryString(rows) 289 | } 290 | 291 | rows.Close() 292 | return 293 | } 294 | 295 | func (d *Database) QueryStrings() (strings []String, err error) { 296 | rows, err := d.query(sqlQueryString) 297 | if err != nil { 298 | return 299 | } 300 | 301 | for rows.Next() { 302 | var s *String 303 | s, err = d.queryString(rows) 304 | if err != nil { 305 | break 306 | } 307 | 308 | strings = append(strings, *s) 309 | } 310 | 311 | rows.Close() 312 | return 313 | } 314 | 315 | func (d *Database) InsertTranslation(name string) (t *Translation, err error) { 316 | result, err := d.exec("INSERT INTO translations (name) VALUES (?)", name) 317 | if err != nil { 318 | return 319 | } 320 | 321 | id, err := result.LastInsertId() 322 | t = &Translation{id, d, name} 323 | 324 | return 325 | } 326 | 327 | const sqlQueryTranslation = "SELECT translationid, name FROM translations " 328 | func (d *Database) queryTranslation(rows *sql.Rows) (t *Translation, err error) { 329 | t = &Translation{} 330 | t.DB = d 331 | err = rows.Scan(&t.id, &t.Name) 332 | if err != nil { 333 | t = nil 334 | } 335 | 336 | return 337 | } 338 | 339 | func (d *Database) QueryTranslation(name string) (t *Translation, err error) { 340 | rows, err := d.query(sqlQueryTranslation + "WHERE name = ?", name) 341 | if err != nil { 342 | return nil, err 343 | } 344 | 345 | if rows.Next() { 346 | t, err = d.queryTranslation(rows) 347 | } 348 | 349 | rows.Close() 350 | return 351 | } 352 | 353 | func (d *Database) QueryTranslations() (translations []Translation, err error) { 354 | rows, err := d.query(sqlQueryTranslation) 355 | if err != nil { 356 | return 357 | } 358 | 359 | for rows.Next() { 360 | var t *Translation 361 | t, err = d.queryTranslation(rows) 362 | if err != nil { 363 | break 364 | } 365 | 366 | translations = append(translations, *t) 367 | } 368 | 369 | rows.Close() 370 | return 371 | } 372 | 373 | func (d *Database) InsertTranslationString(t *Translation, f *String, translation string) (s *TranslationString, err error) { 374 | _, err = d.exec("INSERT INTO translationstrings (translationid, stringid, translation) VALUES (?, ?, ?)", t.id, f.id, translation) 375 | if err != nil { 376 | return 377 | } 378 | 379 | s = &TranslationString{f.id, t.id, d, translation} 380 | 381 | return 382 | } 383 | 384 | func (d *Database) UpdateTranslationString(f *TranslationString, value string) (s *TranslationString, err error) { 385 | _, err = d.exec("UPDATE translationstrings SET translation = ? WHERE translationid = ? AND stringid = ?", value, f.translationid, f.stringid) 386 | if err != nil { 387 | return 388 | } 389 | 390 | s = &TranslationString{f.translationid, f.stringid, d, value} 391 | 392 | return 393 | } 394 | 395 | const sqlQueryTranslationString = "SELECT translationid, stringid, translation FROM translationstrings " 396 | func (d *Database) queryTranslationString(rows *sql.Rows) (s *TranslationString, err error) { 397 | s = &TranslationString{} 398 | s.DB = d 399 | err = rows.Scan(&s.translationid, &s.stringid, &s.Translation) 400 | if err != nil { 401 | s = nil 402 | } 403 | 404 | return 405 | } 406 | 407 | func (d *Database) queryTranslationStrings(query string, args ...interface{}) (strings []TranslationString, err error) { 408 | rows, err := d.query(query, args...) 409 | if err != nil { 410 | return 411 | } 412 | 413 | for rows.Next() { 414 | var s *TranslationString 415 | s, err = d.queryTranslationString(rows) 416 | if err != nil { 417 | break 418 | } 419 | 420 | strings = append(strings, *s) 421 | } 422 | 423 | rows.Close() 424 | return 425 | } 426 | 427 | func (d *Database) QueryTranslationString(t *Translation, f *String) (s *TranslationString, err error) { 428 | rows, err := d.query(sqlQueryTranslationString + "WHERE translationid = ? AND stringid = ?", t.id, f.id) 429 | if err != nil { 430 | return nil, err 431 | } 432 | 433 | if rows.Next() { 434 | s, err = d.queryTranslationString(rows) 435 | } 436 | 437 | rows.Close() 438 | return 439 | } 440 | 441 | func (d *Database) QueryTranslationStrings(t *Translation) (strings []TranslationString, err error) { 442 | return d.queryTranslationStrings(sqlQueryTranslationString + "WHERE translationid = ?", t) 443 | } 444 | 445 | func (d *Database) QueryTranslationStringsFile(t *Translation, f *File) (strings []TranslationString, err error) { 446 | return d.queryTranslationStrings(` 447 | SELECT ts.translationid, ts.stringid, ts.translation 448 | FROM translationstrings AS ts 449 | JOIN strings AS s ON s.stringid = ts.stringid 450 | WHERE ts.translationid = ? AND s.fileid = ?`, t.id, f.id) 451 | } 452 | 453 | func (d *Database) Strip() (err error) { 454 | _, err = d.db.Exec(` 455 | UPDATE strings SET value = '', version = 0; 456 | DELETE FROM strings WHERE NOT stringid IN (SELECT stringid FROM translationstrings); 457 | DELETE FROM files WHERE NOT fileid IN (SELECT fileid FROM strings); 458 | DELETE FROM archives WHERE NOT archiveid IN (SELECT archiveid FROM files); 459 | VACUUM; 460 | ANALYZE; 461 | `) 462 | return 463 | } 464 | -------------------------------------------------------------------------------- /trans/trans.go: -------------------------------------------------------------------------------- 1 | package trans 2 | 3 | import ( 4 | "fmt" 5 | "errors" 6 | ) 7 | 8 | type ArchiveName [0x10]uint8 9 | 10 | type Archive struct { 11 | id int64 12 | DB *Database 13 | Name ArchiveName 14 | } 15 | 16 | type File struct { 17 | id int64 18 | archiveid int64 19 | DB *Database 20 | Name string 21 | } 22 | 23 | type String struct { 24 | id int64 25 | fileid int64 26 | DB *Database 27 | Version int 28 | Collision int 29 | Identifier, Value string 30 | } 31 | 32 | type Translation struct { 33 | id int64 34 | DB *Database 35 | Name string 36 | } 37 | 38 | type TranslationString struct { 39 | stringid int64 40 | translationid int64 41 | DB *Database 42 | Translation string 43 | } 44 | 45 | func (a *ArchiveName) String() string { 46 | return fmt.Sprintf("%x", a[:]) 47 | } 48 | 49 | func ArchiveNameFromString(value string) (*ArchiveName, error) { 50 | var name []uint8 51 | n, err := fmt.Sscanf(value, "%x", &name) 52 | if err != nil { 53 | return nil, err 54 | } else if n != 1 { 55 | return nil, errors.New("invalid archive name format") 56 | } 57 | 58 | var aname ArchiveName 59 | if len(name) != len(aname) { 60 | return nil, errors.New("invalid archive name length") 61 | } 62 | 63 | copy(aname[:], name) 64 | return &aname, nil 65 | } 66 | -------------------------------------------------------------------------------- /util/bufreader.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io" 5 | "bufio" 6 | ) 7 | 8 | type bufReader struct { 9 | reader io.ReadSeeker 10 | breader *bufio.Reader 11 | position int64 12 | } 13 | 14 | func (b *bufReader) Read(p []uint8) (n int, err error) { 15 | n, err = b.breader.Read(p) 16 | 17 | b.position += int64(n) 18 | 19 | return 20 | } 21 | 22 | func (b *bufReader) Seek(offset int64, whence int) (n int64, err error) { 23 | relative := int64(-1) 24 | if whence == 1 { 25 | relative = offset 26 | } else if whence == 0 { 27 | relative = offset - b.position 28 | } 29 | 30 | if relative == 0 { 31 | n = b.position 32 | } else if relative >= 0 && relative < int64(b.breader.Buffered()) { 33 | var buffer [0x100]uint8 34 | for relative > 0 && err == nil { 35 | diff := relative 36 | if diff > int64(len(buffer)) { 37 | diff = int64(len(buffer)) 38 | } 39 | 40 | var r int 41 | r, err = b.breader.Read(buffer[:diff]) 42 | b.position += int64(r) 43 | relative -= int64(r) 44 | } 45 | 46 | n = b.position 47 | } else { 48 | if whence == 1 { 49 | n, err = b.reader.Seek(b.position + offset, 0) 50 | b.position = n 51 | } else { 52 | n, err = b.reader.Seek(offset, whence) 53 | b.position = n 54 | } 55 | 56 | b.breader.Reset(b.reader) 57 | } 58 | 59 | return 60 | } 61 | 62 | func BufReader(reader io.ReadSeeker) io.ReadSeeker { 63 | return &bufReader{reader, bufio.NewReader(reader), 0} 64 | } 65 | 66 | func BufReaderSize(reader io.ReadSeeker, size int) io.ReadSeeker { 67 | return &bufReader{reader, bufio.NewReaderSize(reader, size), 0} 68 | } 69 | -------------------------------------------------------------------------------- /util/closeguard.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type closeGuardWrapper struct { 8 | io.Writer 9 | } 10 | 11 | func (c closeGuardWrapper) Close() error { 12 | return nil 13 | } 14 | 15 | func CloseGuard(writer io.Writer) io.Writer { 16 | return closeGuardWrapper{writer} 17 | } 18 | -------------------------------------------------------------------------------- /util/copyfile.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | func CopyFile(source, dest string) (err error) { 9 | df, err := os.Create(dest) 10 | if err != nil { 11 | return 12 | } 13 | 14 | sf, err := os.Open(source) 15 | if err != nil { 16 | df.Close() 17 | return 18 | } 19 | 20 | _, err = io.Copy(df, sf) 21 | 22 | sf.Close() 23 | df.Close() 24 | 25 | return 26 | } 27 | -------------------------------------------------------------------------------- /util/memreader.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io" 5 | "io/ioutil" 6 | "bytes" 7 | ) 8 | 9 | func MemReader(reader io.Reader) (io.ReadSeeker, error) { 10 | buffer, err := ioutil.ReadAll(reader) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | return bytes.NewReader(buffer), nil 16 | } 17 | -------------------------------------------------------------------------------- /util/pipe.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "io" 4 | 5 | type PipeWriterType interface { 6 | Write(writer io.Writer) error 7 | } 8 | 9 | func PipeReader(writer PipeWriterType) *io.PipeReader { 10 | r, w := io.Pipe() 11 | go func() { 12 | w.CloseWithError(writer.Write(w)) 13 | }() 14 | return r 15 | } 16 | -------------------------------------------------------------------------------- /util/readerat.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io" 5 | "errors" 6 | ) 7 | 8 | type readerAtWrapper struct { 9 | io.ReadSeeker 10 | } 11 | 12 | func (r readerAtWrapper) ReadAt(p []byte, off int64) (int, error) { 13 | n, err := r.Seek(off, 0) 14 | if err == nil && n != off { 15 | err = errors.New("unable to seek to requested offset") 16 | } 17 | 18 | if err != nil { 19 | return 0, err 20 | } 21 | 22 | return r.Read(p) 23 | } 24 | 25 | func ReaderAt(reader io.Reader) io.ReaderAt { 26 | switch r := reader.(type) { 27 | case io.ReaderAt: 28 | return r 29 | case io.ReadSeeker: 30 | return readerAtWrapper{r} 31 | default: 32 | return readerAtWrapper{Seeker(reader)} 33 | } 34 | 35 | panic("readerAt") 36 | } 37 | -------------------------------------------------------------------------------- /util/readn.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "io" 4 | 5 | func ReadN(reader io.Reader, p []uint8) (n int, err error) { 6 | for len(p) > 0 && err == nil { 7 | var r int 8 | r, err = reader.Read(p) 9 | 10 | n += r 11 | p = p[r:] 12 | } 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /util/readwriter.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "io" 4 | 5 | type readWriteWrapper struct { 6 | io.Reader 7 | io.Writer 8 | } 9 | 10 | func (r readWriteWrapper) Close() (err error) { 11 | if c, ok := r.Reader.(io.Closer); ok { 12 | err = c.Close() 13 | } 14 | 15 | if c, ok := r.Writer.(io.Closer); ok { 16 | e := c.Close() 17 | 18 | if e != nil { 19 | err = e 20 | } 21 | } 22 | 23 | return 24 | } 25 | 26 | func ReadWriter(reader io.Reader, writer io.Writer) io.ReadWriteCloser { 27 | return readWriteWrapper{reader, writer} 28 | } 29 | -------------------------------------------------------------------------------- /util/seeker.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "io" 5 | "errors" 6 | ) 7 | 8 | type seekerWrapper struct { 9 | io.Reader 10 | position int64 11 | } 12 | 13 | func (s seekerWrapper) Seek(offset int64, whence int) (int64, error) { 14 | // Determine offset... 15 | switch whence { 16 | case 1: 17 | offset += s.position 18 | case 2: 19 | return s.position, errors.New("unsupported seek") 20 | } 21 | 22 | if offset < s.position { 23 | return s.position, errors.New("unsupported seek") 24 | } 25 | 26 | // Read forward until we reach our destination 27 | var err error 28 | var buffer [0x400]uint8 29 | for offset > s.position && err == nil { 30 | diff := offset - s.position 31 | 32 | if diff > int64(len(buffer)) { 33 | diff = int64(len(buffer)) 34 | } 35 | 36 | var read int 37 | read, err = s.Read(buffer[:diff]) 38 | s.position += int64(read) 39 | } 40 | 41 | return s.position, err 42 | } 43 | 44 | func Seeker(reader io.Reader) io.ReadSeeker { 45 | switch r := reader.(type) { 46 | case io.ReadSeeker: 47 | return r 48 | default: 49 | return seekerWrapper{reader, 0} 50 | } 51 | } 52 | --------------------------------------------------------------------------------