Skip to content

Commit

Permalink
v2.0.1 更新Usage使用手册和Plugin插件手册
Browse files Browse the repository at this point in the history
- 允许不使用OPCODE进行反编译
  • Loading branch information
wetor committed Oct 7, 2023
1 parent 78f488c commit 179a624
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 19 deletions.
91 changes: 91 additions & 0 deletions Plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
## 使用Plugin支持任意游戏的脚本反编译与导入

插件为非标准的Python,语法类似Python3.4,缺少大量的内置库和一些特性,基本使用没有问题

### 编写规则
可参考 [LOOPERS.py](data/LOOPERS.py)[SP.py](data/SP.py)

#### 解析操作函数
在脚本种定义与OPCODE同名的函数(大小写一致),即可在反编译、导入时使用脚本中的函数去解析对应的操作参数

#### Init函数
在脚本中定义的`Init`函数会在最开始执行一次,一般用来调用`set_config`,也可以进行其他初始化操作

#### opcode_dict全局变量
在没有载入OPCODE或者存在未知OPCODE时,反编译会将操作名输出为`0x22` `0x3A`等十六进制标记,此时可以通过`opcode_dict`来手动映射十六进制标记和操作函数名,如下:
```python
opcode_dict = {
'0x22': 'MESSAGE',
'0x25': 'SELECT'
}
```
此时对于未知的`0x22`操作,将会使用插件中的`MESSAGE`函数进行解析

### core包内置常量
```python
core.Charset_UTF8 = "UTF-8"
core.Charset_Unicode = "UTF-16LE"
core.Charset_SJIS = "Shift_JIS"
```
三种字符集的特点如下:
#### Charset_UTF8
1~3byte一字,固定的结尾0x00
通常英文字符串使用此编码,目前如LOOPERS等新游戏也作为表达式(expr)和PAK包文件名编码也是UTF8

#### Charset_Unicode
2byte(一个uint16)一字,固定的结尾0x0000
目前绝大多数的中文、日文文本均是此编码

#### Charset_SJIS
1~2byte一字,固定的结尾0x00
老游戏的表达式和PAK包编码,一般为单日语游戏使用

### core包内置函数

#### set_config
`set_config(expr_charset, text_charset, default_export=True)`
设置默认值
设置`expr_charset`后可通过`core.expr`获取对应设置的值,通常与PAK的文件名编码一致
设置`text_charset`后可通过`core.text`获取对应设置的值,也会成功`read_str``read_len_str`的默认字符集值
`default_export`即为解析插件未定义OPCODE时,是否将自动解析的uint16参数导出

#### read
`read(export=False) -> list(int)`
以uint16的形式读取**所有**参数,若剩余一位则将以uint8形式读取

#### read_uint8
`read_uint8(export=False) -> int`
以uint8的形式读取一个参数

#### read_uint16
`read_uint16(export=False) -> int`
以uint16的形式读取一个参数

#### read_uint32
`read_uint32(export=False) -> int`
以uint32的形式读取一个参数

#### read_str
`read_str(charset=textCharset, export=True) -> str`
按指定编码读取一个字符串
注意:如确定字符串前面存在一个`uint16`为字符串长度,则需要使用`read_len_str`进行读取,否则长度错乱会导致导入出错

#### read_len_str
`read_len_str(charset=textCharset, export=True) -> str`
按指定编码读取一个包含长度的字符串,导入时会自动计算新的长度

#### read_jump (重要)
`read_jump(file='', export=True) -> int`
以uint32的形式读取一个跳转位置参数,导入时将会自动重构跳转位置
跨文件的跳转需要传入`file`参数,一般为前一个参数。跨文件跳转标记为`global233`
跨文件的`file`参数,需要使用read_str或read_len_str读取,字符集为expr,通常与PAK文件名编码一致
文件内跳转的标记为`label66`

#### end (重要)
`end()`
将已经进行的读取提交,必须调用,否则将无法正常导出导入

#### can_read
`can_read() -> bool`
判断接下来是否可以进行读取

33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Important
This project only accepts **bug issues** and **pull requests**, and does not provide assistance in use
此项目仅接受现有功能的BUG反馈和Pull requests,不提供使用上的帮助

使用方法:[Usage](Usage.md)
此项目仅接受现有功能的BUG反馈和Pull requests,不提供使用上的帮助

# Luck System

LucaSystem ~~engine galgame **Emulator**~~
LucaSystem 引擎解析工具

## 使用方法:[Usage](Usage.md)
## 插件手册:[Plugin](Plugin.md)

## LucaSystem解析完成进度

### Luca Pak 封包文件
Expand Down Expand Up @@ -50,7 +50,10 @@ LucaSystem 引擎解析工具

- 导出完成
- 导入完成
- 简单的模拟执行
- ~~简单的模拟执行~~
- 支持插件扩展(gpython)
- 非标准的Python,语法类似Python3.4,缺少大量的内置库和一些特性,基本使用没有问题
- 插件手册 [Plugin](Plugin.md)

#### 笔记

Expand Down Expand Up @@ -82,13 +85,13 @@ LucaSystem 引擎解析工具
- 导出完成

## 目前支持的游戏

1. LB_EN:《Little Busters! English Edition》 Steam *优先
2. SP:《Summer Pockets》 Nintendo Switch
1. 《LOOPERS》 Steam
2. LB_EN:《Little Busters! English Edition》 Steam
3. SP:《Summer Pockets》 Nintendo Switch

## 目前支持的指令

- MESSAGE (LB_EN、SP)
- MESSAGE (LB_EN、SP、LOOPERS)
- SELECT (LB_EN、SP)
- IMAGELOAD (LB_EN、SP)

Expand All @@ -103,17 +106,17 @@ LucaSystem 引擎解析工具
- GOTO
- JUMP
- FARCALL
- MOVE
- GOSUB

其余指令的数据均为未处理或者解析

## 更新日志

### 未来
### 2023.10.7
- 支持LOOPERS导入和导出(已测试)
- 支持Plugin扩展以支持任意游戏
- 内置SummerPockets(未测试)和LOOPERS默认Plugin插件和OPCODE
- 移除模拟器相关代码

- 完善的log输出
- 支持更多LucaSystem引擎的游戏资源解析
- 通用化与易理解的控制台交互接口

### 6.26
- 完全重构cmd使用方式
Expand Down
14 changes: 14 additions & 0 deletions Usage.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## 使用此工具进行翻译工作时,请注意

- 最好需要提取OPCODE,从可执行文件中获取。如无法提取,需要进行以下工作:
- 反编译脚本,得到全为uint16的脚本文件,找出其中可能包含字符串的操作(一般特别长,并且存在连续的、较大的值)
- 使用Plugin中的`opcode_dict`功能,映射为插件函数并尝试进行解析,解析出字符串并进行翻译
- 此时的字符串不能进行超长,且需要使用**全角空格**(半角字符串则使用半角空格)进行填充为原始长度,否则会导致导入后的脚本无法正常使用
- 如想进行任意字符串的修改,需要识别出[base](data/base)下插件文件中的跳转操作,如`IFN` `FARCALL` `JUMP`等操作,并使用`read_jump`准确的解析出跳转的类型、跳转值等
- 如已经有完整的OPCODE:
- 反编译脚本,得到全为uint16的脚本文件,找出其中可能包含字符串的操作(一般特别长,并且存在连续的、较大的值)
- 使用Plugin进行尝试解析
- 此时的字符串不能进行超长,且需要使用**全角空格**(半角字符串则使用半角空格)进行填充为原始长度,否则会导致导入后的脚本无法正常使用
- 如想进行任意字符串的修改,需要解析[base](data/base)下插件文件中的跳转操作,如`IFN` `FARCALL` `JUMP`,并使用`read_jump`准确的解析出跳转的类型、跳转值等
- 导入和导出必须使用同一份相同的原SCRIPT.PAK、OPCODE和插件,对插件的任何修改都需要重新进行反编译,才可以导入
- 建议:编写额外的工具,从反编译后的脚本中提取需要翻译的文本,并在翻译完成过使用工具替换到反编译后的脚本中,然后再导入,防止游戏数值被意外的修改
## 使用help能获取详细指令信息

## Example
Expand Down
2 changes: 1 addition & 1 deletion cmd/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ func init() {
scriptCmd.PersistentFlags().StringVarP(&ScriptOpcode, "opcode", "O", "", "游戏的OPCODE文件")
scriptCmd.PersistentFlags().StringVarP(&ScriptPlugin, "plugin", "p", "", "游戏OPCODE解析插件")

scriptCmd.MarkFlagsRequiredTogether("opcode", "plugin")
scriptCmd.MarkPersistentFlagRequired("source")
}
4 changes: 3 additions & 1 deletion game/game.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ func NewGame(opt *GameOptions) *Game {
PluginFile: opt.PluginFile,
}),
}
game.VM.LoadOpcode(opt.OpcodeFile)
if len(opt.OpcodeFile) > 0 {
game.VM.LoadOpcode(opt.OpcodeFile)
}
return game
}

Expand Down
4 changes: 2 additions & 2 deletions game/operator/plugin_opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ func init() {
py.MustNewMethod("read_uint8", pluginContext.ReadUInt8, 0, `read_uint8(export=False) -> int`),
py.MustNewMethod("read_uint16", pluginContext.ReadUInt16, 0, `read_uint16(export=False) -> int`),
py.MustNewMethod("read_uint32", pluginContext.ReadUInt32, 0, `read_uint32(export=False) -> int`),
py.MustNewMethod("read_jump", pluginContext.ReadJump, 0, `read_jump(file, export=True) -> int`),
py.MustNewMethod("read_jump", pluginContext.ReadJump, 0, `read_jump(file='', export=True) -> int`),
py.MustNewMethod("read_str", pluginContext.ReadString, 0, `read_str(charset=textCharset, export=True) -> str`),
py.MustNewMethod("read_len_str", pluginContext.ReadLenString, 0, `read_len_str(charset=textCharset, export=True) -> str`),
py.MustNewMethod("end", pluginContext.End, 0, `end()`),
py.MustNewMethod("can_read", pluginContext.CanRead, 0, `can_read()`),
py.MustNewMethod("can_read", pluginContext.CanRead, 0, `can_read() -> bool`),
py.MustNewMethod("set_config", pluginContext.SetConfig, 0, `set_config(expr_charset, text_charset, default_export=True)`),
}

Expand Down

0 comments on commit 179a624

Please sign in to comment.