# 使用命令行

# 简述

ES 内置了一门简单的语言, 可以比较高自由度地精细地操控每一个功能.

调用方式为

./ExtractorSharp.exe -m <命令>

WARNING

  • 使用本功能需要对任意 C 系编程语言Windows 命令行 有一些了解, 并且以下文档默认读者了解这些.
  • 本功能尚不完善, 可能会有未知 bug.
  • 本功能可能会快速迭代, 无法保证文档实时更新, 但尽量保证向后兼容.
  • 相关代码在ExtractorSharp/Core/CommandParser.cs, 一切以此为准

最近更新: 2020/05/03

# 语言快速入门

此语言非常简略, 大部分时候形如:

1,2,3|toArray|toInt|asVar|a;
1|toInt|addOne|addOne|addOne|asVar|b;
|useVar|b|addOne;

@{
    replaceImage;
    ...;
    ...;
}

代码从左到右从上到下执行, 以|为分界符, |前的通常是操作数, 其后是命令, 代码行以分号结尾.

# 命令

这对应于 GO 的接口, 允许链式调用.

1|toInt|addOne|addOne|addOne;
|useVar|b|addOne;

可视为

"1".toInt().addOne().addOne().addOne();
b.addOne();

*所有的内置命令见下表. *

聪明的你一定发现1对应成了"1", 请看数据类型.

# 数据类型

语言基础数据类型仅有:

  • 字符串, 写出来的默认就是字符串, 如asdsdfgsd, 字符串, 123, 987.123. 只有字符串能够直接表示.
  • 整数, 整数只能通过命令构造, 123|toInt => "123".toInt().
  • 数组, 允许任意类型数组, 但是原生的构造方法只有1,2,3|toArray => "1,2,3".split(','), 暂时不支持直接索引数组.

但是也支持

  • 原生 C# 对象, 一般是调用 API 的返回值

# 变量使用

构造变量使用值|asVar|名, 如1|asVar|onevar one = 1.
使用变量使用|useVar|名, 如|useVar|one$one.

变量总是全局的, 没有作用域, 变量可以重复构造, 即

1|asVar|one;
one|addOne|asVar|one;
# one -> 2

WARNING

代码目前没有注释功能.

变量使用过程允许使用 C# 变量的字段/属性/方法, 如

1|toInt|asVar|one; # var one = int.Parse("1");
|useVar|one.ToString(); # $one.ToString();

sprite_character_challenge2nd_gunner_launcher.NPK|LoadFile|asVar|albums;
# var albums =  LoadFile("sprite_character_challenge2nd_gunner_launcher.NPK");
|useVar|albums.Length; # $albums.Length;

WARNING

useVar 暂时不支持传参, 可以a.ToString(), 但是不能b.GetValue(0).

# 流程控制

暂时没有判断, 只有一个forEach.

# forEach:

sprite_character_challenge2nd_gunner_launcher.NPK|LoadFile|asVar|albums;
|useVar|albums|forEach|album{
    |useVar|album.Name|message;
}

相当于

var albums = LoadFile("sprite_character_challenge2nd_gunner_launcher.NPK");
albums.forEach(album => {
    message(album.Name);
})
# 或者
foreach (var album in albums){
    message(album.Name);
}

# 调用 ES Action

ES 将各个动作抽象为 Action, 这也是本项目得以快速使用的重要原因.

# Action

Action 继承自ICommand, 包含

string Name { get; }

/// <summary>
///     可否撤销
/// </summary>
/// <returns></returns>
bool CanUndo { get; }

/// <summary>
///     是否对文件有实质影响
/// </summary>
/// <returns></returns>
bool IsChanged { get; }

/// <summary>
///     执行
/// </summary>
void Do(params object[] args);

/// <summary>
///     撤销
/// </summary>
void Undo();

/// <summary>
///     重做
/// </summary>
void Redo();

使用 Action 的过程即拼凑 args 数组, 然后调用 Do. 所有的 Action 可以在 Github 看到, 欲知传参方式则直接跳转对应文件查看 Do 的处理过程即可, 更详细的 API 文档待完善.

这里推荐下函数断点来查看参数.

TODO: 在界面一键导出一组动作

# 语法

调用 Action 的语法为:

@{
    saveImage;
    |useVar|album;
    1|toInt;
    1|toArray|toInt;
    Z:\output;
    "";
    0|toInt;
    0|toInt;
    false|toBool;
    |asNull;
    true|toBool;
}

saveImage是 ES 的一个动作, 用于导出album的所有贴图, 其余即按顺序填充函数参数.

一个文件对应许多album, albums即打开页面后左侧的那块区域中的.img, 见此处

# 示例

本示例演示了导出一个 NPK 文件的贴图后, 将贴图内容置换为随机色块后再另存为新的 NPK 文件的流程.

$cmd_export = @"
D:\WeGameApps\地下城与勇士\ImagePacks2\sprite_character_challenge2nd_gunner_launcher.NPK|LoadFile|asVar|albums;
useVar|albums|forEach|album|{
	@{
		saveImage; 
		|useVar|album;
		1|toInt;
		1|toArray|toInt;
		Z:\output;
		"";
		0|toInt;
		0|toInt;
		false|toBool;
		|asNull;
		true|toBool;
	}
}
0|exit;
"@

$cmd_replace_and_save = @"
D:\WeGameApps\地下城与勇士\ImagePacks2\sprite_character_challenge2nd_gunner_launcher.NPK|LoadFile|asVar|albums;
useVar|albums|forEach|album|{
	@{
		replaceImage; 
		0|toInt;
		false|toBool;
		2|toInt;
		Z:\output\|Concat|useVar|album.Name;
		|useVar|album;
		|asNull;
	}
}

@{
	saveImg; 
	|useVar|albums;
	Z:\output\|Concat|sprite_character_challenge2nd_gunner_launcher.NPK;
	2|toInt;
}
0|exit;

"@

C:\Users\Administrator\Desktop\ExtractorSharp\ExtractorSharp\bin\Release\ExtractorSharp.exe -m  $cmd_export | Wait-Process
python .\img_to_black.py 
C:\Users\Administrator\Desktop\ExtractorSharp\ExtractorSharp\bin\Release\ExtractorSharp.exe -m  $cmd_replace_and_save | Wait-Process
# img_to_black.py
import cv2
import os
import numpy as np

for path in os.listdir("./"):
    if os.path.isdir(path):
        for filename in os.listdir(f"./{path}"):
            filepath = f"./{path}/{filename}"
            print(filepath)
            img = cv2.imread(filepath)
            img[:] = np.random.randint(0, 255, (1,3))
            cv2.imwrite(filepath, img)

# 内置命令

TIP

更多内置命令的示例参见ExtractorSharp.UnitTest/Command/UnitTest1.cs

WARNING

以下示例都没有添加分号[;]作为结尾

# asNull

获得一个null

例:

1|asNull # null
|asNull # null

# addOne

将前值喜加一

例:

1|toInt|addOne # 2
1|toInt|addOne|addOne # 3

# toBool

将前值转换为逻辑型. 判断逻辑为arg.CurrentArg as string != "false" 所以除了"false", 其余全是true

例:

1|toBool # true
0|toBool # true
1|asNull|toBool # true

false|asNull # false

# toArray

将数据转换到数组, 要求数据以逗号分隔.

例:

1,2,3|toArray # [1, 2, 3]

# toInt

字符串会转换为整数, 字符串数组会转换为整数数组

# LoadFile

导入一个文件, 由于此动作并未在 Action 中定义, 因此我将其加入了语言内置命令

例:

sprite_character_challenge2nd_gunner_launcher.NPK|LoadFile; # 返回 Album[]

# exit

以某个状态码退出

例:

0|exit # Environment.Exit(0);
1|toInt|exit # Environment.Exit(1);

# message

弹出一个消息框用于提示.

例:

1|message # MessageBox.Show("1");

# asVar

构造变量, 详细见上部变量使用

# useVar

使用变量, 详细见上部变量使用

# asEnvVar

设置环境变量, 用法同asVar

# useEnvVar

使用环境变量, 用法同useVar

例:

Assert.AreEqual(null, commandParser.InvokeToken("|useEnvVar|testEnv;"));
Assert.AreEqual("qaerfqaw", commandParser.InvokeToken("qaerfqaw|asEnvVar|testEnv;"));
Assert.AreEqual("qaerfqaw", commandParser.InvokeToken("|useEnvVar|testEnv;"));
Assert.AreEqual("qaerfqaw", Environment.GetEnvironmentVariable("testEnv"));

# forEach

循环, 详细见上部流程控制

# @

调用 Action, 详细见上部调用 ES Action

# Concat

连接字符串或者数组.

例:

6|Concat|1 # "61"
1|asVar|a
|useVar|a|Concat|2 # "62"

1,2,3|toArray|asVar|a
|useVar|a|toInt|Concat|4|toInt|addOne|addOne # [1, 2, 3, 6]

WARNING

Concat 在连接向量和标量的时候, 只允许左侧向量, 右侧标量, 因此

4|toInt|addOne|addOne|Concat|useVar|a|toInt

不等于[6, 1, 2, 3]