language change
This commit is contained in:
12
language-change/Makefile
Normal file
12
language-change/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
APP_NAME = language-change.exe
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
go build -o $(APP_NAME) ./
|
||||
|
||||
run: build
|
||||
./$(APP_NAME)
|
||||
|
||||
clean:
|
||||
rm -f $(APP_NAME)
|
||||
30
language-change/asset-meta/bytehash16.go
Normal file
30
language-change/asset-meta/bytehash16.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package assetMeta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ByteHash16 []byte
|
||||
|
||||
func ByteHash16FromBytes(r io.ReadSeeker) (ByteHash16, error) {
|
||||
fullHash := make([]byte, 16)
|
||||
buf := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for j := 0; j < 4; j++ {
|
||||
fullHash[i*4+j] = buf[3-j]
|
||||
}
|
||||
}
|
||||
return ByteHash16(fullHash), nil
|
||||
}
|
||||
|
||||
func (b ByteHash16) String() string {
|
||||
s := ""
|
||||
for _, v := range b {
|
||||
s += fmt.Sprintf("%02x", v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
28
language-change/asset-meta/dataEntry.go
Normal file
28
language-change/asset-meta/dataEntry.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package assetMeta
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
type DataEntry struct {
|
||||
NameHash int32
|
||||
Size uint32
|
||||
Offset uint32
|
||||
}
|
||||
|
||||
func DataEntryFromBytes(r io.Reader) (*DataEntry, error) {
|
||||
var d DataEntry
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &d.NameHash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(r, binary.BigEndian, &d.Size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(r, binary.BigEndian, &d.Offset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
98
language-change/asset-meta/designIndex.go
Normal file
98
language-change/asset-meta/designIndex.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package assetMeta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
|
||||
type DesignIndex struct {
|
||||
UnkI64 int64
|
||||
FileCount int32
|
||||
DesignDataCount int32
|
||||
FileList []FileEntry
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (d *DesignIndex) FindDataAndFileByTarget(target int32) (DataEntry, FileEntry, error) {
|
||||
for _, file := range d.FileList {
|
||||
for _, entry := range file.DataEntries {
|
||||
if entry.NameHash == target {
|
||||
return entry, file, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return DataEntry{}, FileEntry{}, errors.New("not found")
|
||||
}
|
||||
|
||||
|
||||
func DesignIndexFromBytes(assetFolder string, indexHash string) (*DesignIndex, error) {
|
||||
path := filepath.Join(assetFolder, fmt.Sprintf("DesignV_%s.bytes", indexHash))
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := bytes.NewReader(data)
|
||||
|
||||
var d DesignIndex
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &d.UnkI64); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(r, binary.BigEndian, &d.FileCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(r, binary.BigEndian, &d.DesignDataCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.FileList = make([]FileEntry, 0, d.FileCount)
|
||||
for i := int32(0); i < d.FileCount; i++ {
|
||||
entry, err := FileEntryFromBytes(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.FileList = append(d.FileList, *entry)
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
|
||||
func GetIndexHash(assetFolder string) (string, error) {
|
||||
path := filepath.Join(assetFolder, "M_DesignV.bytes")
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Seek(0x1C, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hash := make([]byte, 0x10)
|
||||
index := 0
|
||||
for i := 0; i < 4; i++ {
|
||||
chunk := make([]byte, 4)
|
||||
_, err := f.Read(chunk)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for bytePos := 3; bytePos >= 0; bytePos-- {
|
||||
hash[index] = chunk[bytePos]
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
return hex.EncodeToString(hash), nil
|
||||
}
|
||||
63
language-change/asset-meta/fileEntry.go
Normal file
63
language-change/asset-meta/fileEntry.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package assetMeta
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type FileEntry struct {
|
||||
NameHash int32
|
||||
FileByteName string
|
||||
Size int64
|
||||
DataCount int32
|
||||
DataEntries []DataEntry
|
||||
Unk uint8
|
||||
}
|
||||
|
||||
func FileEntryFromBytes(r io.Reader) (*FileEntry, error) {
|
||||
var f FileEntry
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &f.NameHash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := make([]byte, 16)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.FileByteName = toHex(buf)
|
||||
|
||||
if err := binary.Read(r, binary.BigEndian, &f.Size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := binary.Read(r, binary.BigEndian, &f.DataCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f.DataEntries = make([]DataEntry, 0, f.DataCount)
|
||||
for i := int32(0); i < f.DataCount; i++ {
|
||||
entry, err := DataEntryFromBytes(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.DataEntries = append(f.DataEntries, *entry)
|
||||
}
|
||||
|
||||
// read 1 byte
|
||||
b := make([]byte, 1)
|
||||
if _, err := r.Read(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Unk = b[0]
|
||||
|
||||
return &f, nil
|
||||
}
|
||||
|
||||
func toHex(buf []byte) string {
|
||||
s := ""
|
||||
for _, b := range buf {
|
||||
s += fmt.Sprintf("%02x", b)
|
||||
}
|
||||
return s
|
||||
}
|
||||
32
language-change/asset-meta/miniAsset.go
Normal file
32
language-change/asset-meta/miniAsset.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package assetMeta
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
type MiniAsset struct {
|
||||
RevisionID uint32
|
||||
DesignIndexHash ByteHash16
|
||||
}
|
||||
|
||||
func MiniAssetFromBytes(r io.ReadSeeker) (*MiniAsset, error) {
|
||||
if _, err := r.Seek(6*4, io.SeekCurrent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var revID uint32
|
||||
if err := binary.Read(r, binary.LittleEndian, &revID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash, err := ByteHash16FromBytes(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MiniAsset{
|
||||
RevisionID: revID,
|
||||
DesignIndexHash: hash,
|
||||
}, nil
|
||||
}
|
||||
114
language-change/excel-language/patch.go
Normal file
114
language-change/excel-language/patch.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package excelLanguage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
assetMeta "language-change/asset-meta"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type ExcelLanguage struct {
|
||||
AssetFolder string
|
||||
ExcelDataEntry *assetMeta.DataEntry
|
||||
ExcelFileEntry *assetMeta.FileEntry
|
||||
}
|
||||
|
||||
func NewExcelLanguage(assetFolder string, dataEntry *assetMeta.DataEntry, fileEntry *assetMeta.FileEntry) *ExcelLanguage {
|
||||
return &ExcelLanguage{
|
||||
AssetFolder: assetFolder,
|
||||
ExcelDataEntry: dataEntry,
|
||||
ExcelFileEntry: fileEntry,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ExcelLanguage) Unmarshal(rows []LanguageRow) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
buf.WriteByte(0)
|
||||
if err := writeI8Varint(buf, int8(len(rows))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, row := range rows {
|
||||
rowData, err := row.Unmarshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.Write(rowData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (a *ExcelLanguage) Parse() ([]LanguageRow, error) {
|
||||
excelPath := filepath.Join(a.AssetFolder, a.ExcelFileEntry.FileByteName+".bytes")
|
||||
|
||||
f, err := os.Open(excelPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Seek(int64(a.ExcelDataEntry.Offset), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buffer := make([]byte, a.ExcelDataEntry.Size)
|
||||
if _, err := io.ReadFull(f, buffer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(buffer)
|
||||
|
||||
_, _ = reader.ReadByte() // skip first byte
|
||||
count, err := readI8Varint(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([]LanguageRow, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
bitmask, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
row := LanguageRow{}
|
||||
|
||||
if bitmask&(1<<0) != 0 {
|
||||
s, err := readString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row.Area = &s
|
||||
}
|
||||
if bitmask&(1<<1) != 0 {
|
||||
t, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row.Type = &t
|
||||
}
|
||||
if bitmask&(1<<2) != 0 {
|
||||
arr, err := readStringArray(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row.LanguageList = arr
|
||||
}
|
||||
if bitmask&(1<<3) != 0 {
|
||||
s, err := readString(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
row.DefaultLanguage = &s
|
||||
}
|
||||
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
81
language-change/excel-language/row.go
Normal file
81
language-change/excel-language/row.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package excelLanguage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type LanguageRow struct {
|
||||
Area *string
|
||||
Type *uint8
|
||||
LanguageList []string
|
||||
DefaultLanguage *string
|
||||
}
|
||||
|
||||
func (r *LanguageRow) Unmarshal() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
var bitmask uint8
|
||||
if r.Area != nil {
|
||||
bitmask |= 1 << 0
|
||||
}
|
||||
if r.Type != nil {
|
||||
bitmask |= 1 << 1
|
||||
}
|
||||
if len(r.LanguageList) > 0 {
|
||||
bitmask |= 1 << 2
|
||||
}
|
||||
if r.DefaultLanguage != nil {
|
||||
bitmask |= 1 << 3
|
||||
}
|
||||
|
||||
if err := buf.WriteByte(bitmask); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.Area != nil {
|
||||
if err := writeString(buf, *r.Area); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if r.Type != nil {
|
||||
if err := buf.WriteByte(*r.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(r.LanguageList) > 0 {
|
||||
if err := writeStringArray(buf, r.LanguageList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if r.DefaultLanguage != nil {
|
||||
if err := writeString(buf, *r.DefaultLanguage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func writeString(buf *bytes.Buffer, s string) error {
|
||||
if len(s) > 255 {
|
||||
return errors.New("string too long")
|
||||
}
|
||||
if err := buf.WriteByte(uint8(len(s))); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := buf.Write([]byte(s))
|
||||
return err
|
||||
}
|
||||
|
||||
func writeStringArray(buf *bytes.Buffer, arr []string) error {
|
||||
if err := writeI8Varint(buf, int8(len(arr))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range arr {
|
||||
if err := writeString(buf, s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
53
language-change/excel-language/utils.go
Normal file
53
language-change/excel-language/utils.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package excelLanguage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
func writeI8Varint(buf *bytes.Buffer, v int8) error {
|
||||
uv := uint64((uint32(v) << 1) ^ uint32(v>>7)) // zigzag encode
|
||||
b := make([]byte, binary.MaxVarintLen64)
|
||||
n := binary.PutUvarint(b, uv)
|
||||
_, err := buf.Write(b[:n])
|
||||
return err
|
||||
}
|
||||
|
||||
func readI8Varint(r *bytes.Reader) (int, error) {
|
||||
uv, err := binary.ReadUvarint(r)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// zigzag decode
|
||||
v := int((uv >> 1) ^ uint64((int64(uv&1)<<63)>>63))
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func readString(r *bytes.Reader) (string, error) {
|
||||
l, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := make([]byte, l)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func readStringArray(r *bytes.Reader) ([]string, error) {
|
||||
length, err := readI8Varint(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arr := make([]string, 0, length)
|
||||
for i := 0; i < length; i++ {
|
||||
s, err := readString(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arr = append(arr, s)
|
||||
}
|
||||
return arr, nil
|
||||
}
|
||||
3
language-change/go.mod
Normal file
3
language-change/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module language-change
|
||||
|
||||
go 1.25.0
|
||||
226
language-change/main.go
Normal file
226
language-change/main.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
assetMeta "language-change/asset-meta"
|
||||
excelLanguage "language-change/excel-language"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func isValidLang(lang string) bool {
|
||||
valid := []string{"en", "jp", "cn", "kr"}
|
||||
return slices.Contains(valid, lang)
|
||||
}
|
||||
|
||||
func GetLanguage(assetPath string) (string, string, error) {
|
||||
typeVersionGame := "cn"
|
||||
|
||||
indexHash, err := assetMeta.GetIndexHash(assetPath)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
DesignIndex, err := assetMeta.DesignIndexFromBytes(assetPath, indexHash)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
dataEntry, fileEntry, err := DesignIndex.FindDataAndFileByTarget(-515329346)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
allowedLanguage := excelLanguage.NewExcelLanguage(assetPath, &dataEntry, &fileEntry)
|
||||
languageRows, err := allowedLanguage.Parse()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
currentTextLang := ""
|
||||
currentVoiceLang := ""
|
||||
|
||||
pairs := []struct {
|
||||
area string
|
||||
typ *uint8
|
||||
}{
|
||||
{"os", nil},
|
||||
{"cn", func() *uint8 { v := uint8(1); return &v }()},
|
||||
{"os", func() *uint8 { v := uint8(1); return &v }()},
|
||||
{"cn", nil},
|
||||
}
|
||||
|
||||
for _, p := range pairs {
|
||||
var found *excelLanguage.LanguageRow
|
||||
for i := range languageRows {
|
||||
if languageRows[i].Area != nil && *languageRows[i].Area == p.area {
|
||||
if (languageRows[i].Type == nil && p.typ == nil) ||
|
||||
(languageRows[i].Type != nil && p.typ != nil && *languageRows[i].Type == *p.typ) {
|
||||
found = &languageRows[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if found == nil {
|
||||
continue
|
||||
}
|
||||
if found.DefaultLanguage != nil && found.Area != nil && *found.Area == typeVersionGame && found.Type == nil {
|
||||
currentTextLang = *found.DefaultLanguage
|
||||
}
|
||||
if found.DefaultLanguage != nil && found.Area != nil && *found.Area == typeVersionGame && found.Type != nil {
|
||||
currentVoiceLang = *found.DefaultLanguage
|
||||
}
|
||||
}
|
||||
|
||||
if currentTextLang == "" || currentVoiceLang == "" || !isValidLang(currentTextLang) || !isValidLang(currentVoiceLang) {
|
||||
return "", "", errors.New("not found language")
|
||||
}
|
||||
|
||||
return currentTextLang, currentVoiceLang, nil
|
||||
}
|
||||
|
||||
func SetLanguage(assetPath string, text, voice string) error {
|
||||
indexHash, err := assetMeta.GetIndexHash(assetPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
DesignIndex, err := assetMeta.DesignIndexFromBytes(assetPath, indexHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataEntry, fileEntry, err := GetAssetData(DesignIndex, "AllowedLanguage")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allowedLanguage := excelLanguage.NewExcelLanguage(assetPath, dataEntry, fileEntry)
|
||||
languageRows, err := allowedLanguage.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pairs := []struct {
|
||||
area string
|
||||
typ *uint8
|
||||
lang string
|
||||
}{
|
||||
{"os", nil, text},
|
||||
{"cn", func() *uint8 { v := uint8(1); return &v }(), voice},
|
||||
{"os", func() *uint8 { v := uint8(1); return &v }(), voice},
|
||||
{"cn", nil, text},
|
||||
}
|
||||
|
||||
for _, p := range pairs {
|
||||
var found *excelLanguage.LanguageRow
|
||||
for i := range languageRows {
|
||||
if languageRows[i].Area != nil && *languageRows[i].Area == p.area {
|
||||
if (languageRows[i].Type == nil && p.typ == nil) ||
|
||||
(languageRows[i].Type != nil && p.typ != nil && *languageRows[i].Type == *p.typ) {
|
||||
found = &languageRows[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if found == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
found.DefaultLanguage = &p.lang
|
||||
found.LanguageList = []string{p.lang}
|
||||
}
|
||||
|
||||
data, err := allowedLanguage.Unmarshal(languageRows)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filePath := filepath.Join(assetPath, fileEntry.FileByteName+".bytes")
|
||||
|
||||
f, err := os.OpenFile(filePath, os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Seek(int64(dataEntry.Offset), 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := f.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(data) < int(dataEntry.Size) {
|
||||
remaining := int(dataEntry.Size) - len(data)
|
||||
zeros := bytes.Repeat([]byte{0}, remaining)
|
||||
if _, err := f.Write(zeros); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("Enter source DesignData folder path: ")
|
||||
source, _ := reader.ReadString('\n')
|
||||
source = strings.TrimSpace(source)
|
||||
if source == "" {
|
||||
fmt.Fprintln(os.Stderr, "no source folder provided")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
info, err := os.Stat(source)
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Fprintln(os.Stderr, "source folder does not exist")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error accessing path:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
fmt.Fprintln(os.Stderr, "path is not a directory")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
currentTextLang, currentVoiceLang, err := GetLanguage(source)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error getting language:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Current language:", currentTextLang, currentVoiceLang)
|
||||
|
||||
fmt.Println("Allow languages: en, jp, cn, kr")
|
||||
|
||||
fmt.Print("Enter text language: ")
|
||||
textLang, _ := reader.ReadString('\n')
|
||||
textLang = strings.TrimSpace(textLang)
|
||||
if textLang == "" {
|
||||
fmt.Fprintln(os.Stderr, "no text language provided")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Print("Enter voice language: ")
|
||||
voiceLang, _ := reader.ReadString('\n')
|
||||
voiceLang = strings.TrimSpace(voiceLang)
|
||||
if voiceLang == "" {
|
||||
fmt.Fprintln(os.Stderr, "no voice language provided")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := SetLanguage(source, textLang, voiceLang); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error setting language:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Language set successfully.")
|
||||
|
||||
}
|
||||
39
language-change/ulils.go
Normal file
39
language-change/ulils.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
assetMeta "language-change/asset-meta"
|
||||
)
|
||||
|
||||
func Get32BitHashConst(s string) int32 {
|
||||
var hash1 int32 = 5381
|
||||
var hash2 int32 = hash1
|
||||
|
||||
bytes := []byte(s)
|
||||
length := len(bytes)
|
||||
|
||||
for i := 0; i < length; i += 2 {
|
||||
hash1 = ((hash1 << 5) + hash1) ^ int32(bytes[i])
|
||||
if i+1 < length {
|
||||
hash2 = ((hash2 << 5) + hash2) ^ int32(bytes[i+1])
|
||||
}
|
||||
}
|
||||
|
||||
return int32(uint32(hash1) + uint32(hash2)*1566083941)
|
||||
}
|
||||
|
||||
func GetAssetData(assets *assetMeta.DesignIndex, name string) (*assetMeta.DataEntry, *assetMeta.FileEntry, error) {
|
||||
dataEntry, fileEntry, err := assets.FindDataAndFileByTarget(Get32BitHashConst("BakedConfig/ExcelOutput/" + name + ".bytes"))
|
||||
if err == nil {
|
||||
return &dataEntry, &fileEntry, nil
|
||||
}
|
||||
dataEntry, fileEntry, err = assets.FindDataAndFileByTarget(Get32BitHashConst("BakedConfig/ExcelOutputGameCore/" + name + ".bytes"))
|
||||
if err == nil {
|
||||
return &dataEntry, &fileEntry, nil
|
||||
}
|
||||
dataEntry, fileEntry, err = assets.FindDataAndFileByTarget(Get32BitHashConst("BakedConfig/ExcelOutputAdventureGame/" + name + ".bytes"))
|
||||
if err == nil {
|
||||
return &dataEntry, &fileEntry, nil
|
||||
}
|
||||
return nil, nil, errors.New("not found")
|
||||
}
|
||||
Reference in New Issue
Block a user