import { _decorator, Asset, assetManager, BufferAsset, Component, Node, resources, TextAsset, v2, v3, Vec2, } from "cc"; import { ReadStream } from "../Tool/ReadStream"; import { Ani_Frame, ScriptAni, ScriptFile } from "../GlobalInterface/GlobalInterface"; import { GlobalTool } from "../Tool/GlobalTool"; const { ccclass, property } = _decorator; class ScriptTree { //PVF文件头数据 Index_Header_Data: Uint8Array; //PVF原始数据 Script_Data_Buffer: Uint8Array; //文件头读取流 Index_Header_Read_Object: ReadStream; //树Map TreeMap: Map = new Map(); constructor(gIndex_Header_Data, gScript_Data_Buffer) { this.Index_Header_Data = gIndex_Header_Data; this.Script_Data_Buffer = gScript_Data_Buffer; this.Index_Header_Read_Object = new ReadStream(this.Index_Header_Data); } ReadTree(currPos, startPos, Index) { this.Index_Header_Read_Object.Seekg(currPos); const FileNumber = this.Index_Header_Read_Object.GetInt(); const FilePathLength = this.Index_Header_Read_Object.GetInt(); const FilePath = this.Index_Header_Read_Object.GetBufferByLength(FilePathLength); const FileLength = this.Index_Header_Read_Object.GetInt(); const Cre32 = this.Index_Header_Read_Object.GetInt(); //数据偏移 const RelativeOffset = this.Index_Header_Read_Object.GetInt(); if (FileLength > 0) { //数据长度 const RealFileLength = (FileLength + 3) & 4294967292; //文件路径 const RealFilePath = GlobalTool.uint8ArrayToString(FilePath).replace("\\", "/"); //建立索引Map this.TreeMap.set(RealFilePath, { Cre32: Cre32, StartPos: startPos, Offset: RelativeOffset, Length: RealFileLength }); } return FilePathLength + 20; } //原数据 StringtableBaseBuf; //CRE key StringtableCre32; //读取Bin文件 StringTable: Map = new Map(); //当前索引位置 StringtableCurrentIndex: number = 0; //当前读取索引位置 StringtableReadCurrentIndex: number = 0; //本类用的特殊解密 分段解密 CrcDecodeSpecial(): boolean { const num: number = 2175242257; // 2175242257L in hexadecimal const dataView = new DataView( this.StringtableBaseBuf.buffer, this.StringtableBaseBuf.byteOffset, this.StringtableBaseBuf.byteLength, ); //开始时间 const StartTime = new Date().getTime(); //死循环读取树文件数据 但是每一次最多只读15毫秒 为了保证UI线程刷新 while (true) { // Ensure we don't read beyond the end of the data //如果当前索引位置加上4大于数组长度 说明读取完毕 返回true if (this.StringtableCurrentIndex >= this.StringtableBaseBuf.length) return true; //当前时间减去开始时间 const NowTime = new Date().getTime(); //如果本轮循环已经读取了15毫秒了 就先返回刷新ui if (NowTime - StartTime >= 15) return false; // Read a 32-bit integer (little endian) from the buffer let anInt = dataView.getUint32(this.StringtableCurrentIndex, true); // Perform the XOR operations let val = anInt ^ num ^ this.StringtableCre32; // Rotate right 6 bits let jiemi = (val >>> 6) | (val << (32 - 6)); // Write the result back into the buffer dataView.setUint32(this.StringtableCurrentIndex, jiemi, true); this.StringtableCurrentIndex = this.StringtableCurrentIndex + 4; } } InitStringBin() { //如果这个是空就读取一下 if (this.StringtableBaseBuf == undefined) { const FileObject = this.TreeMap.get("stringtable.bin"); if (FileObject == undefined) return true; this.StringtableBaseBuf = this.Script_Data_Buffer.slice( FileObject.StartPos + FileObject.Offset, FileObject.StartPos + FileObject.Offset + FileObject.Length ); this.StringtableCre32 = FileObject.Cre32; } const Flag = this.CrcDecodeSpecial(); if (Flag) { let Ro = new ReadStream(this.StringtableBaseBuf); const Count = Ro.GetInt(); //开始时间 const StartTime = new Date().getTime(); while (true) { //当前时间减去开始时间 const NowTime = new Date().getTime(); //如果本轮循环已经读取了15毫秒了 就先返回刷新ui if (NowTime - StartTime >= 15) return false; if (this.StringtableReadCurrentIndex < Count) { Ro.Seekg(this.StringtableReadCurrentIndex * 4 + 4); const StartPos = Ro.GetInt(); const EndPos = Ro.GetInt(); const Len = EndPos - StartPos; Ro.Seekg(StartPos + 4); const Str = Ro.GetString(Len); this.StringTable.set(this.StringtableReadCurrentIndex, Str); this.StringtableReadCurrentIndex++; } else { return true; } } } return false; } //读取字符串索引 Load_StringTable: Map = new Map(); InitLoad_String() { const FileObject = this.TreeMap.get("n_string.lst"); if (FileObject == undefined) return; let RealBuf = this.Script_Data_Buffer.slice( FileObject.StartPos + FileObject.Offset, FileObject.StartPos + FileObject.Offset + FileObject.Length ); GlobalTool.CrcDecode(RealBuf, FileObject.Cre32); let Ro = new ReadStream(RealBuf); const Count = Ro.GetUShort(); if (Count != 53424) return; let i = 2; while (i < Ro.len()) { if (Ro.len() - i >= 10) { Ro.Seekg(i + 6); const Key = this.StringTable.get(Ro.GetInt()); if (Key) { const File = this.TreeMap.get(Key.toLocaleLowerCase()); let FileBuf = this.Script_Data_Buffer.slice( File.StartPos + File.Offset, File.StartPos + File.Offset + File.Length ); GlobalTool.CrcDecode(FileBuf, File.Cre32); let Str = GlobalTool.uint8ArrayToString(FileBuf, 'big5'); let StrArr = Str.split('\r\n'); StrArr.forEach((strobj, index) => { if (strobj.indexOf('>')) { let strobjarr = strobj.split('>', 2); this.Load_StringTable.set(strobjarr[0], strobjarr[1]); } }); } } else break; i += 10; } } //Pvf文件数据 ScriptFileData = new Map(); GetFileData(Path: string) { //检查路径是否存在 const FileObject = this.TreeMap.get(Path); if (FileObject == undefined) return false; //如果读过直接返回数据 const FileData = this.ScriptFileData.get(Path); if (FileData != undefined) return FileData; //获取文件数据字节数组 let RealBuf = this.Script_Data_Buffer.slice( FileObject.StartPos + FileObject.Offset, FileObject.StartPos + FileObject.Offset + FileObject.Length ); GlobalTool.CrcDecode(RealBuf, FileObject.Cre32); //Ani的处理逻辑 if (Path.indexOf(".ani") != -1) { // try { let Buf = this.Decompile_ani(RealBuf); this.ScriptFileData.set(Path, Buf); return Buf; // } catch (error) { // console.log(Path); // } } else { try { let Buf = this.Decompile_script(RealBuf); this.ScriptFileData.set(Path, Buf); return Buf; } catch (error) { // console.log(RealFilePath); } } } Decompile_script(RealBuf: any) { const Ro = new ReadStream(RealBuf); let out: string = ""; if (Ro.len() >= 7) { //以5为单步从第二位开始遍历字节 let i = 2; while (i < Ro.len()) { //到最后了就不处理了防止内存越界 if (Ro.len() - i >= 5) { Ro.Seekg(i); //内容指示位 let currentByte = Number(Ro.GetBufferByLength(1)); //内容指示位 let after = Ro.GetInt(); switch (currentByte) { case 10: { Ro.Seekg(i - 4); const Before = Ro.GetInt(); let Buf = this.StringTable.get(after); if (!Buf) { Buf = ""; } else { Buf = "<" + Before + "::" + Buf + "`" + this.Load_StringTable.get(Buf) + "`>"; } Buf = Buf + "\r\n"; out += Buf; break; } case 2: { out += after + '\t'; break; } case 4: { const buffer = new ArrayBuffer(4); const view = new DataView(buffer); view.setInt32(0, after, true); const Buf = view.getFloat32(0); out += after + '\t'; break; } case 6: case 8: case 7: case 5: { let Buf = this.StringTable.get(after); if (!Buf) Buf = ""; if (currentByte == 5) { Buf = "\r\n" + Buf + "\r\n"; } else if (currentByte == 7) { Buf = "`" + Buf + "`\r\n"; } else if (currentByte == 6 || currentByte == 8) { Buf = "{{" + currentByte + "=`" + Buf + "`}}\r\n"; } out += Buf; break; } default: out += ""; break; } } i += 5; } } return out; } //解包数据 Unpack_Chr(pvfData, curr: number, after: number, before: number): string { switch (curr) { case 2: return after.toString(); //整数型 case 4: // return new Float32Array([after]).buffer.slice(0, 4).readFloatLE().toString(); case 5: case 6: case 7: case 8: if (pvfData.str_bin_map.has(after)) { return pvfData.str_bin_map.get(after); } else { return ""; } case 10: if (!pvfData.str_bin_map.has(after)) { return ""; } let tempstr = pvfData.str_bin_map.get(after); // return `<${before}::\`${tempstr}\`${_getNString(pvfData, before, tempstr)}.toString()}>`; default: return ""; } } //读取Ani Decompile_ani(RealBuf: any): any { let AniObject: ScriptAni = { Img_List: new Array(), Flag: new Map(), Frame: new Array(), }; const Ro = new ReadStream(RealBuf); //总帧数 const Frame_Max = Ro.GetUShort(); //总共调用了多少个Img const Img_Count = Ro.GetUShort(); //Img的路径读取 存入数组 for (let index = 0; index < Img_Count; index++) { const Buf = Ro.GetInt(); //有可能Img有空路径 AniObject.Img_List.push(Ro.GetString(Buf)); } //Ani头部标签数量 const Ani_H_Item_Count = Ro.GetUShort(); //处理标签 for (let index = 0; index < Ani_H_Item_Count; index++) { //标签类型 const Type = Ro.GetUShort(); switch (Type) { case 0: case 1: { const Key = this.Get_Ani_Flag(Type); const Value = Number(Ro.GetBufferByLength(1)); AniObject.Flag.set(Key, Value); break; } case 3: case 28: { const Key = this.Get_Ani_Flag(Type); const Value = Ro.GetUShort(); AniObject.Flag.set(Key, Value); break; } case 18: //此处无解析 暂时先保证运行 残影功能暂时用不上 Ro.GetBufferByLength(1); Ro.GetInt(); Ro.GetInt(); Ro.GetInt(); Ro.Get256(); Ro.Get256(); Ro.Get256(); Ro.Get256(); Ro.GetUShort(); break; default: break; } } //读取每一个Img for (let index = 0; index < Frame_Max; index++) { //帧结构体对象 let FrameObject: Ani_Frame = { Box: new Map>(), Img_Path: null, Img_Index: null, Pos: null, Flag: new Map(), Delay: null, }; //碰撞框项目数量 const Ani_Box_Item_Count = Ro.GetUShort(); for (let _i = 0; _i < Ani_Box_Item_Count; _i++) { const Box_Type = Ro.GetUShort(); let D_Box_b = []; for (let _k = 0; _k < 6; _k++) { D_Box_b.push(Ro.GetInt()); } //0是攻击框 1是受击框 FrameObject.Box.set(15 - Box_Type, D_Box_b); } //调用的第几个Img const Index_Buf = Ro.GetShort(); //如果等于-1说明是img路径为空 if (Index_Buf >= 0) { FrameObject.Img_Path = AniObject.Img_List[Index_Buf]; //Img中的PNG下标 FrameObject.Img_Index = Ro.GetUShort(); } else { FrameObject.Img_Path = ""; FrameObject.Img_Index = 0; } //坐标 FrameObject.Pos = v3(Ro.GetInt(), -Ro.GetInt(), 0); //Img中的项目数量 const Img_Flag_Count = Ro.GetUShort(); for (let _o = 0; _o < Img_Flag_Count; _o++) { const Img_Flag_Type = Ro.GetUShort(); let Key; let Value; switch (Img_Flag_Type) { case 0: case 1: case 10: Key = this.Get_Ani_Flag(Img_Flag_Type); Value = Number(Ro.GetBufferByLength(1)); FrameObject.Flag.set(Key, Value); break; case 3: Key = "COORD"; Value = Ro.GetUShort(); FrameObject.Flag.set(Key, Value); break; case 17: Key = "PRELOAD"; Value = 1; FrameObject.Flag.set(Key, Value); break; case 7: Key = "IMAGE_RATE"; Value = { x: Ro.GetFloat(), y: Ro.GetFloat() }; FrameObject.Flag.set(Key, Value); break; case 8: Key = "IMAGE_ROTATE"; Value = Ro.GetFloat(); FrameObject.Flag.set(Key, Value); break; case 9: Key = "RGBA"; Value = [ Ro.Get256(), Ro.Get256(), Ro.Get256(), Ro.Get256(), ]; FrameObject.Flag.set(Key, Value); break; case 11: const Effect_Type = Ro.GetUShort(); Key = "GRAPHIC_EFFECT_" + this.Get_Ani_Effect_Type(Effect_Type); switch (Effect_Type) { case 5: Value = [Ro.Get256(), Ro.Get256(), Ro.Get256()]; break; case 6: Value = [Ro.GetShort(), Ro.GetShort()]; break; } FrameObject.Flag.set(Key, Value); break; case 12: Value = Ro.GetInt(); FrameObject.Delay = Value; break; case 13: Key = "DAMAGE_TYPE"; Value = this.Get_Ani_Damage_Type(Ro.GetUShort()); FrameObject.Flag.set(Key, Value); break; case 16: const SoundTempSize = Ro.GetInt(); Key = "PLAY_SOUND"; Value = Ro.GetString(SoundTempSize); FrameObject.Flag.set(Key, Value); break; case 23: Key = "SET_FLAG"; Value = Ro.GetInt(); FrameObject.Flag.set(Key, Value); break; case 24: Key = "FLIP_TYPE"; Value = this.Get_Ani_Flip_Type(Ro.GetUShort()); FrameObject.Flag.set(Key, Value); break; case 25: Key = "LOOP_START"; FrameObject.Flag.set(Key, 1); break; case 26: Key = "LOOP_END"; Value = Ro.GetInt(); FrameObject.Flag.set(Key, Value); break; case 27: Key = "CLIP"; Value = [ Ro.GetShort(), Ro.GetShort(), Ro.GetShort(), Ro.GetShort(), ]; FrameObject.Flag.set(Key, Value); break; default: break; } } //每一帧都是一个结构体 存入数组中 AniObject.Frame.push(FrameObject); } return AniObject; } Get_Ani_Flip_Type(data: number): string { switch (data) { case 1: return "HORIZON"; case 2: return "VERTICAL"; case 3: return "ALL"; default: return ""; } } Get_Ani_Effect_Type(data: number): string { switch (data) { case 0: return "NONE"; case 1: return "DODGE"; case 2: return "LINEARDODGE"; case 3: return "DARK"; case 4: return "XOR"; case 5: return "MONOCHROME"; case 6: return "SPACEDISTORT"; default: return ""; } } Get_Ani_Damage_Type(data: number): any { switch (data) { case 0: return "NORMAL"; case 1: return "SUPERARMOR"; case 2: return "UNBREAKABLE"; default: return ""; } } Get_Ani_Flag(data: number): string { switch (data) { case 0: return "LOOP"; case 1: return "SHADOW"; case 3: return "COORD"; case 7: return "IMAGE_RATE"; case 8: return "IMAGE_ROTATE"; case 9: return "RGBA"; case 10: return "INTERPOLATION"; case 11: return "GRAPHIC_EFFECT"; case 12: return "DELAY"; case 13: return "DAMAGE_TYPE"; case 14: return "DAMAGE_BOX"; case 15: return "ATTACK_BOX"; case 16: return "PLAY_SOUND"; case 17: return "PRELOAD"; case 18: return "SPECTRUM"; case 23: return "SET_FLAG"; case 24: return "FLIP_TYPE"; case 25: return "LOOP_START"; case 26: return "LOOP_END"; case 27: return "CLIP"; case 28: return "OPERATION"; default: return ""; } } } class ScriptData { //Pvf数据 ScriptDataBuffer; //读取UUID的长度 UUID_LENGTH; //UUID 读 1 - 36位 构造 UTF8 string UUID; //版本号 Version; // 文件路径数据的大小 AlignedIndexHeaderSize; // 解密密钥 IndexHeaderCrc; // 文件数量 IndexSize; // 存放文件路径数据 Index_Header_Data; //Pvf解密出的数据 ScriptDataObject: ScriptTree = null; constructor( public buffer: Uint8Array, public uuidLength: number, public uuid: string, public version: number, public alignedIndexHeaderSize: number, public indexHeaderCrc: number, public indexSize: number, public indexHeaderData: Uint8Array ) { //Pvf数据 this.ScriptDataBuffer = buffer; //读取UUID的长度 this.UUID_LENGTH = uuidLength; //UUID 读 1 - 36位 构造 UTF8 string this.UUID = uuid; //版本号 this.Version = version; // 文件路径数据的大小 this.AlignedIndexHeaderSize = alignedIndexHeaderSize; // 解密密钥 this.IndexHeaderCrc = indexHeaderCrc; // 文件数量 this.IndexSize = indexSize; // 存放文件路径数据 this.Index_Header_Data = indexHeaderData; } //初始化路径树 把所有文件路径建立起来还没有读 InitPathTree() { //先进行Crc解密 GlobalTool.CrcDecode(this.Index_Header_Data, this.IndexHeaderCrc); //构造PVF数据类 this.ScriptDataObject = new ScriptTree( //文件数据 this.Index_Header_Data, //整个Pvf的原始数据 this.ScriptDataBuffer ); } //当期读取树数据的Pos CurrentTreePos = 0; //当前读取文件下标 CurrentIndex = 0; ReadPathTree() { //开始时间 const StartTime = new Date().getTime(); //死循环读取树文件数据 但是每一次最多只读15毫秒 为了保证UI线程刷新 while (true) { //当前时间减去开始时间 const NowTime = new Date().getTime(); if ((NowTime - StartTime) >= 15) break; //当前读取的下标小于总数量 if (this.CurrentIndex < this.IndexSize) { const Length = this.ScriptDataObject.ReadTree( this.CurrentTreePos, this.AlignedIndexHeaderSize + 56, this.CurrentIndex ); this.CurrentTreePos += Length; this.CurrentIndex++; } //如果不小于 说明读完了 else { //读取bin文件 bin Map const Flag = this.ScriptDataObject.InitStringBin(); if (Flag) { //读取字符串索引 this.ScriptDataObject.InitLoad_String(); return true; } return false; } } return false; } GetDataByPath(Path: string) { return this.ScriptDataObject.TreeMap.get(Path); } } @ccclass("GameScript") export class GameScript extends Component { //全局单例类 private static instance: GameScript; private constructor() { super(); } public static getInstance(): GameScript { if (!GameScript.instance) { GameScript.instance = new GameScript(); } return GameScript.instance; } //Pvf对象 PvfData: ScriptData; //加载完成后的回调函数 LoadCallBack: Function; //初始化Pvf Init(Func: Function) { //储存回调 this.LoadCallBack = Func; //读取Pvf this.ReadPvf(); } //读取原始数据 ReadPvf() { resources.load("Script/Rindro_Script", BufferAsset, (err, content) => { //转化原始数据为Uint8数组 const Buf = new Uint8Array(content.buffer()); //构造一个 读取流对象 const ScriptObj = new ReadStream(Buf); //读取UUID的长度 const UUID_LENGTH = ScriptObj.GetInt(); //UUID 读 1 - 36位 构造 UTF8 string const UUID = ScriptObj.GetString(UUID_LENGTH); //版本号 const Version = ScriptObj.GetInt(); // 文件路径数据的大小 const AlignedIndexHeaderSize = ScriptObj.GetInt(); // 解密密钥 const IndexHeaderCrc = ScriptObj.GetInt(); // 文件数量 const IndexSize = ScriptObj.GetInt(); // 存放文件路径数据 const Index_Header_Data = ScriptObj.GetBufferByLength(AlignedIndexHeaderSize); this.PvfData = new ScriptData( Buf, UUID_LENGTH, UUID, Version, AlignedIndexHeaderSize, IndexHeaderCrc, IndexSize, Index_Header_Data ); //建立文件索引树 this.PvfData.InitPathTree(); //可以开始读取PVF数据 this.StartInitFlag = true; }); } //可以开始读取PVF数据 StartInitFlag = false; //是否已经读取完成 InitFlag = false; Update() { if (!this.InitFlag && this.StartInitFlag) { const Flag = this.PvfData.ReadPathTree(); if (Flag) { this.LoadCallBack(); this.InitFlag = true; } } } GetDataByPath(Path: string) { const Ret = this.PvfData.ScriptDataObject.GetFileData(Path); if (Ret) return Ret; else console.error("索引路径不存在: " + Path); } }