lua序列化二進制腳本
1. lua 二進制的移位運算
由於 Lua 腳本語言本身不支持對數字的二進制操作(例如 與,或,非 等操作),MUSHclient 為此提供了一套專門用於二進制操作的函數,它們都定義在一個「bit」表中,使用時只要requre 「bit」即可。
例如:
bit.bor - 按位「或」運算
此函數需要一個或多個無符號整數作為參數,返回所有參數執行按位「或」運算後的結果。
例如:
bit.mod - 取模函數 (得到整除後的余數)
此函數需要兩個整數作為參數。函數結果是兩個數整除之後的余數。
例如:
bit.neg - 按位「非」運算
此函數需要一個無符號整數作為參數,返回這個參數執行按位「非」運算後的結果。
例如:
bit.shl - 按位左移
此函數需要兩個無符號整數作為參數。第一個參數是被移位的數,第二個參數是向左移動的位數。
例如:
bit.shr - 按位右移
此函數需要兩個無符號整數作為參數。第一個參數是被移位的數,第二個參數是向右移動的位數。
例如:
bit.tostring (n, base) - 把一個數字轉換為字元串
把一個數字根據指定的基數轉換為字元串(大寫形式)base 產生是可選的,默認為 10,其有效范圍為 2 - 36。數字的小數部分會被忽略. 被轉換的數字可以是負數,轉換後會保留 "-" 符號。
例如:
bit.tonumber (s, base) - 把字元串轉換為一個數字
把一個字元串形式的數字轉換為相應的數字。和標准 Lua tonumber 函數不一樣的是此函數可以處理高達 52 bit 的數字(Lua 默認的函數只能處理 32 bit 的數字)。
例如:
base 參數是可選的,指明了轉換的基數,默認為 10。base 的有效范圍是 2 - 36。此函數不支持小數,也不支持指數表示形式的整數(例如 10.24e15)。如果一定要使用這樣的數,你只有使用 Lua 自帶的 tonumber 函數了。
由於計算機對浮點數的限制,字元串能轉換的數字不能超過 52 bit,例如:十六進制 FFFFFFFFFFFFF (十進制 4503599627370495)。
字元串開始的空白字元會自動被忽略,實際數字前面可以有一個 + 或者 - 號。
bit.xor - 按位「異或」運算
此函數需要一個或多個無符號整數作為參數,返回所有參數執行按位「異或」運算後的結果。
例如:
2. lua 怎麼反編譯
1.Lua的虛擬機指令,5.2 的有40條
Lua的指令使用一個32bit的unsigned integer表示。所有指令的定義都在lopcodes.h文件中(可以從Lua 官方網站下
載),使用一個enum OpCode代表指令類型。在lua5.2中,總共有40種指令(id從0到39)。根據指令參數的不同,可以將所有指令分為4
類:
typedef enum {
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
OP_MOVE,/* A B R(A) := R(B) */
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
OP_LOADKX,/* A R(A) := Kst(extra arg) */
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
OP_GETTABUP,/* A B C R(A) := UpValue[B][RK(C)] */
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
OP_SETTABUP,/* A B C UpValue[A][RK(B)] := RK(C) */
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
OP_UNM,/* A B R(A) := -R(B) */
OP_NOT,/* A B R(A) := not R(B) */
OP_LEN,/* A B R(A) := length of R(B) */
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A) + 1 */
OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
OP_FORLOOP,/* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
OP_TFORCALL,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
OP_TFORLOOP,/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
OP_VARARG,/* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
} OpCode;
**********************************************************
虛擬機指令(2) MOVE & LOAD
OP_MOVE A B
OP_MOVE用來將寄存器B中的值拷貝到寄存器A中,由於Lua是基於寄存器虛擬機,大部分的指令都是直接對寄存器進行操作,而不需要對數據進行壓棧和彈棧。OP_MOVE 指令的作用 是將一個Local變數復制給另一個local變數.
例子:
local a = 10;
local b = a;
編譯出來的結果
1 [1] LOAD 0 1;1代表的是常量表的項,這里代表的是10
2 [2] MOVE 1 0
所代表的二進制為
B A OP_Code
Load 0 1 = 100000000 000000000 00000000 000001 = 0x80000001 ,
也就是說, 0x80000001 的二進制所代表的指令為 Load 0 1,這里B中的最高位為1,表示的B為常量表的序號,而不是寄存器
MOVE 1 0 = 000000000 000000000 00000001 000000 = 0x40
*****************華麗分割線***********************************************
1.lua 的二進制格式,官方的luac.exe 編譯出來的格式
原始的lua 腳本為
local a = 10
local b = a
print(b)
下面介紹格式文件,介紹每個欄位的意思.當然啦,這種格式是官方的,各個游戲公司可能會做一些改動,但是萬變不離其宗。個個欄位已經用顏色標明了
在lua 的源文件中,前面四個位元組 1b 4c 75 61 也就是 \033Lua , 標識的是lua文件的特有的標示符數據格式,代表是lua
#define LUA_SIGNATURE "\033Lua" 033時八進制 = 0x1b ,很多那些反編譯工具判斷這四個位元組的值,來判斷是否能反編譯,很多公司都會偷偷的去掉或者用其他的值來替換,以迷惑菜鳥。呵呵
52 第五個位元組,表示的是,當前lua 的目標版本,這里指的是5.2 版本。
感覺編輯的好痛苦,我還是直接貼我的比較圖算了,看起來比較舒服
函數的頭描述
linedefined = 00 00 00 00 ;函數定義開始處的行號
linedefined = 00 00 00 00 ; 函數定義結束處的行號 ;頂級函數開始和結束行號都是為00
numparams = 00 ;固定參數的數目 number of fixed parameters
is_vararg = 01 ;可變參數標識符
• 1=VARARG_HASARG
• 2=VARARG_ISVARARG
• 4=VARARG_NEEDSARG
maxstacksize = 03 ;調用函數所需要的堆棧空間指令段
sizecode = 06 00 00 00 ; 函數中 指令的數目,緩存區的大小 = sizecode * sizeof(Instruction),每四個位元組為一條指令
code = 02 00 00 00 41 00 00 00 87 40 40 00 c1 00 80 00 a0 40 00 01 1e 00 80 00
常量列表 保存著函數中引用的常量的列表 (常量池)
Constant.sizek = 02 00 00 00 ;常量列表的大小 ,緩存區的大小 = Constant.sizek * sizeof(TValue) = 2 * 8 = 16,每項為8個位元組,
TValue * = 03 00 00 .
00 00 00 00 24 40 04 06 00 00 00 70 72 69 6e 74 [email protected]
Constant list 數據結構 保存著函數中引用的常量的列表 (常量池)
Integer 常量列表的大小 (sizek)
[
1 byte 常量類型 (value in parentheses): • 0=LUA_TNIL, 1=LUA_TBOOLEAN,• 3=LUA_TNUMBER, 4=LUA_TSTRING
Const 常量本身: 如果常量類型是0這個域不存在;如果類型是1,這個是0或1;如果類型是3這個域是 Number;如果類型是4 這個域是String。
]
這里的String 是包含"0"為結束的字元串
為什麼上傳圖片以後,圖片都變小了,而且不清晰呢?
***********************給大家發一點福利,矯正虛擬機指令的函數**************************************
//矯正虛擬機指令
DWORD Rectify(DWORD Source);
{
DWORD Instruction = Source;
BYTE Source_OpCode = Instruction & 0x3F;
switch(Source_OpCode)
{
case OP_MOVE:
Source_OpCode = Target_OpCode;
break;
...
}
Instruction = ((Instruction & 0xFFFFFFC0) | Source_OpCode);
return Instruction
}
3. 怎麼使用lua腳本
LUA腳本語言基本使用方法是本文要將介紹的內容,主要是來學習Lua腳本語言的使用方法,具體內容來看本文詳解。
先要把下邊這些語句加入到VC中的頭文件,一般是加到StdAfx.h中
extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" #pragma comment(lib, "lualib.lib") #pragma comment(lib, "lua.lib") }
然後一般來說都使用一個全局的LUA庫定義的這個東西
lua_State* g_Lua;
並且在工程處初始化時也給LUA初始化
g_Lua = lua_open(); **加這句 /* load Lua base libraries */ 網上一些教材中這么寫的 lua_baselibopen(g_Lua); 不過我這簡單的例子中不用到這些也行 lua_tablibopen(g_Lua); lua_iolibopen(g_Lua); lua_strlibopen(g_Lua); lua_mathlibopen(g_Lua);
緊接著聲明介面函數,注冊上函數
lua_register(g_Lua, "Message", myMessage);
好,初始化部分完了,看看介面函數的寫法。
函數必須這樣的格式來寫
static int Func(lua_State *L) { 靜態型函數,而且必須帶參數為lua_State結構指針 返回的值是代表返回的數據個數,比如return 2;就可以代表 返回兩個整數啊,浮點數什麼的,象LUA的腳本編寫就可以這樣 i, j = Func() , 這樣就表示可以從Func介面函數中得到兩個返回值了 return 0; }
執行腳本語句可以讀文件,也可以直接讀函數名
注冊了
lua_register(g_Lua, "Message", myMessage); static int myMessage(lua_State *L) { OutputDebugString("OK"); return 0; } lua_dofile(g_Lua, strCurPath); //讀文件,必須給出完整的文件路徑名稱 lua_dostring(g_Lua, "Message()"); //直接讀函數
文件中只要寫上
Message()
就可以了。
4. 使用lua-cmsgpack序列化和反序列化lua對象
lua-cmsgpack是一個開源的MessagePack實現方式、純C的庫,沒有任何其它依賴,編譯後可以直接被lua調用,目前主要支持Lua 5.1/5.2/5.3 版本。
1、什麼是MessagePack?
官方的解釋是:
跟JSON及其類似,但是比JSON更快並且佔用空間更小,舉個官方給出的例子,直接截官方圖:
翻譯官方的解釋:
MessagePack是一種高效的二進制序列化格式, 它允許在多種語言(如JSON)之間交換數據,但它越來越小, 小整數被編碼為單個位元組,典型的短字元串除了字元串本身之外還需要一個額外的位元組。
目前市面上流行的開發語言MessagePack幾乎支持,官方的地址為: http://msgpack.org/Lua MessagePack也提供了一套開源庫,地址在: https://github.com/fperrad/lua-MessagePack/ 。
但是,作者使用的是lua-cmsgpack,至於哪個比較優異,作者還沒有去比較,主要是先發現了lua-cmsgpack,後面看了下README文件,使用方法應該是差不多的,大家可以拿來參考。
lua-cmsgpack包括官方提供的lua-MessagePack都需要自行編譯,因為可能平台太多,所以官方沒有為每一個平台提供編譯好的版本。lua-cmsgpack的github地址為: https://github.com/antirez/lua-cmsgpack
git clone下來之後需要安裝cmake工具,mac平台直接在項目目錄:
即可,當然需要預先安裝lua,並且是5.1版本以上的。
主要說下CentOS平台下cmake可能會出現的問題,如果cmake的過程出現以下錯誤:
出現以上錯誤的話,需要自行安裝lua的一些依賴庫,一般:
就可以了,如果還不行,再試試下面的命令:
編譯完成之後會生成cmsgpack.so文件,使用的時候直接require進去即可
運行效果:
cmsgpack.pack()可以把多個lua對象序列化成一個二進制msgpack,執行反序化的時候會返回對應數量的lua對象,非常的方便。
有趣的是redis也支持MessagePack,因此結合lua和lua-cmsgpack可以產生不錯的化學反應,下面是一個簡單的例子(結合OpenResty):
測試返回結果:
在某些場合還是有不錯應用場景的。
5. lua序列化與反序列化
主要用於存儲對象狀態為另一種通用格式,比如存儲為二進制、xml、json等等,把對象轉換成這種格式就叫序列化,而反序列化通常是從這種格式轉換回來。
使用序列化主要是因為跨和對象存儲的需求,因為網路上只允許字元串或者二進制格式,而文件需要使用二進制流格式,如果想把一個內存中的對象存儲下來就必須使用序列化轉換為xml(字元串)、json(字元串)或二進制(流)
6. lua腳本怎麼編譯成二進制
luac的參數提供完整路徑,或者在lua文件所在目錄打開命令行
另外請確認已正確設置環境變數(如在cmd下直接luac可以運行,即提示no input files give即可)
比如lua文件在E:\f1\f2\f3\a.lua的話
cmd ->
luac e:\f1\f2\f3\a.lua
或者
在資源管理器中瀏覽到f2,然後shift+右鍵f3,在此打開命令行 (僅win7,xp貌似沒這選項),然後luac a.lua
又或者
cmd ->
e: --打開E盤
cd f1\f2\f3 --cd命令進入f3
luac a.lua
