[文件分析]Terraria文件结构详解
一、Terraria文件结构树
说明:
- {}内为变量,其他固定的字符为一般常量,即在一般情况下不会变动
- 本树为dir的结果,树样本是Terraria 1.3.4+tmodloader+mods
-
根目录
C:\Users\{UserName}\Documents\My Games\Terraria\Captures #截图文件夹 C:\Users\{UserName}\Documents\My Games\Terraria\config.json #配置文件 C:\Users\{UserName}\Documents\My Games\Terraria\favorites.json #标记的“收藏”配置 C:\Users\{UserName}\Documents\My Games\Terraria\input profiles.json #控制配置 C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader #Tmodloader文件夹 C:\Users\{UserName}\Documents\My Games\Terraria\Players #存档-玩家 C:\Users\{UserName}\Documents\My Games\Terraria\TEditLog.txt #Tedit软件的日志 C:\Users\{UserName}\Documents\My Games\Terraria\Worlds #存档-世界
-
截图文件
C:\Users\{UserName}\Documents\My Games\Terraria\Captures\Capture {Date}.png
截图文件以 Capture {Date}.png 命名
-
Tmodloader的文件,该文件夹下替换了原terraria的所有内容
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\config.json
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\favorites.json
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\input profiles.json
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Logs #日志
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Mod Reader #由部分特殊mod创建的文件夹,不予解析。(样本中由RecipeBrowser创建)
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Mod Sources #未编译的mod文件夹,你可以把cs项目放在这里等待编译
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Mods #已编译的mod文件夹
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Players
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\servers.dat #服务器数据文件
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Worlds
-
Mods文件夹
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Mods\Cache #部分mod创建的临时数据文件夹
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Mods\enabled.json #mod开关配置
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Mods\{mod}.tmod #mod文件
-
Tmodloader下的玩家数据文件夹
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Players\{PlayerName}.plr #玩家的人物文件
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Players\{PlayerName}.plr.bak #玩家人物文件的备份
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Players\{PlayerName}.tplr #tmod加载后的玩家文件,一般来说含有一些原游戏没有的内容
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Players\{PlayerName}.tplr.bak
-
Tmodloader下的世界数据文件夹
C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Worlds\{WorldName}.twld #tmod加载后的世界文件 C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Worlds\{WorldName}.twld.bak C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Worlds\{WorldName}.wld C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Worlds\{WorldName}.wld.{Date&TimeStamp}.TEdit #Tedit编辑的世界文件的存档 C:\Users\{UserName}\Documents\My Games\Terraria\ModLoader\Worlds\{WorldName}.wld.bak
-
原游戏下的玩家数据文件夹
C:\Users\{UserName}\Documents\My Games\Terraria\Players\{PlayerName}.plr
C:\Users\{UserName}\Documents\My Games\Terraria\Players\{PlayerName}.plr.bak
-
原游戏下的世界数据文件夹
C:\Users\{UserName}\Documents\My Games\Terraria\Worlds\{WorldName}.wld C:\Users\{UserName}\Documents\My Games\Terraria\Worlds\{WorldName}.wld.{Date&TimeStamp}.TEdit
C:\Users\{UserName}\Documents\My Games\Terraria\Worlds\{WorldName}.wld.bak
二、部分文件内容解析
1.config.json
Terraria游戏运行的必需文件,其内容表示了游戏项目的配置
示例文件如下
复制时请删除//后的内容,因为JSON不包含注释句语法
{
"SmartCursorToggle": {bool}, //智能鼠标开关
"MapEnabled": {bool}, //小地图开关
"InvasionBarMode": {int}, //入侵事件(如海盗入侵)时显示右下角的进度条
"AutoSave": {bool}, //自动保存
"AutoPause": {bool}, //自动暂停
"Language": "en-US", //语言
"PlacementPreview": {bool}, //放置前预览
"GoreVisualsAllowed": {bool}, //血腥视觉的开关
"VolumeSound": {double}, //音效音量,范围是0~1.0
"VolumeAmbient": {double}, //环境音量
"VolumeMusic": {double}, //音乐音量
"UseExperimentalFeatures": {bool}, //实验玩法
"Fullscreen": {bool}, //全屏
"WindowMaximized": {bool}, //默认启动最大化窗口
"WindowBorderless": {bool}, //无边框窗口
"DisplayWidth": {int}, //显示宽(指游戏界面宽度)
"DisplayHeight": {int}, //显示高
"GraphicsQuality": {int}, //游戏画质
"BackgroundEnabled": {bool}, //背景开关
"FrameSkipMode": {int}, //跳帧(用于适配低帧率显示器)
"LightingMode": {int}, //光照模式
"LightingThreads": {int},
"Parallax": {float}, //视差
"ShowItemText": {bool}, //物品文字
"LastLaunchedVersion": {int}, //上一次打开的版本
"ClientUUID": {string@UUID}, //客户端UUID
"UseSmartCursorForCommonBlocks": {bool}, //智能鼠标配置
"UseSmartAxeAfterSmartPickaxe": {bool}, //智能斧子砍树
"UseSmartWallReplacement": {bool}, //智能墙壁替换
"DisableLeftShiftTrashCan": {bool}, //禁用左Shift删除
"HighlightNewItems": {bool}, //高亮新物品
"HidePasswords": {bool}, //掩码
"ReverseUpDownForArmorSetBonuses": {bool}, //?
"MouseShowBuildingGrid": {bool}, //鼠标周围出现建筑网格
"AllowUnfocusedInputOnGamepad": {bool},
"LockOnPriority": {int}, //锁定优先级
"InvisibleCursorForGamepad": {bool}, //使用GamePad游玩时隐藏鼠标
"GamepadDisableInstructionsDisplay": {bool}, //使用GamePad游玩时不展示说明文本
"SettingsUnlock_WorldEvil": {bool}, //允许设定世界邪恶类型,一般来说玩家通过第一次Hardmode之后该选项才会打开
"SettingsEnabled_MinersWobble": {bool},
"UseStormEffect": {bool}, //暴风雪效果
"UseHeatDistortion": {bool}, //热流效果
"WaveQuality": {int}, //水波的质量
"Support4K": {bool}, //支持4K显示
"MouseColor": {
"R": {int},
"G": {int},
"B": {int}
}, //鼠标颜色
"MouseBorderColor": {
"R": {int},
"G": {int},
"B": {int},
"A": {int} //透明度
}, //鼠标边框颜色
"QuickLaunch": {bool}, //快速启动
"Zoom": {float}, //视角缩放
"UIScale": {float}, //界面(UI)缩放
//Modloader独有配置
"ModBrowserPassphrase": "",
"SteamID64": "",
"DownloadModsFromServers": {bool},
"OnlyDownloadSignedModsFromServers": {bool},
"DontRemindModBrowserUpdateReload": {bool},
"DontRemindModBrowserDownloadEnable": {bool},
"MusicStreamMode": {int},
"AlwaysLogExceptions": {bool},
"RemoveForcedMinimumZoom": {bool},
"AllowGreaterResolutions": {bool},
"RunningAchievementEnabled": {bool},
"CloudSavingDefault": {bool}, //默认存储到云端
"FilterLimit": {int},
"FilterPriorityThreshold": {string}
}
2.favorites.json
Terraria游戏运行的必需文件,其内容表示了游戏中收藏的世界/人物
{
"World": {
//可列举多个
{WorldName}: {bool} //世界收藏选项,true时为收藏
},
"Player": {
//可列举多个
{PlayerName}: {bool} //人物收藏
}
}
3.input profiles.json
Terraria游戏运行的必需文件,其内容表示了游戏控制方法
{
"Selected Profile": "Custom",
"Redigit's Pick":
{
"Last Launched Version": 194,
"Settings":
{
"Edittable": true,
"Gamepad - HotbarRadialHoldTime": 16,
"Gamepad - LeftThumbstickDeadzoneX": 0.25,
"Gamepad - LeftThumbstickDeadzoneY": 0.4,
"Gamepad - RightThumbstickDeadzoneX": 0.0,
"Gamepad - RightThumbstickDeadzoneY": 0.0,
"Gamepad - LeftThumbstickInvertX": false,
"Gamepad - LeftThumbstickInvertY": false,
"Gamepad - RightThumbstickInvertX": false,
"Gamepad - RightThumbstickInvertY": false,
"Gamepad - TriggersDeadzone": 0.3,
"Gamepad - InterfaceDeadzoneX": 0.2,
"Gamepad - InventoryMoveCD": 6
},
"Mouse And Keyboard":
{
"MouseLeft": ["Mouse1"],
"MouseRight": ["Mouse2"],
"Up": ["W"],
"Down": ["S"],
"Left": ["A"],
"Right": ["D"],
"Jump": ["Space"],
"Inventory": ["Escape"],
"Grapple": ["E"],
"SmartSelect": ["LeftShift"],
"SmartCursor": ["LeftControl"],
"QuickMount": ["R"],
"QuickHeal": ["H"],
"QuickMana": ["J"],
"QuickBuff": ["B"],
"MapZoomIn": ["Add"],
"MapZoomOut": ["Subtract"],
"MapAlphaUp": ["PageUp"],
"MapAlphaDown": ["PageDown"],
"MapFull": ["M"],
"MapStyle": ["Tab"],
"Hotbar1": ["D1"],
"Hotbar2": ["D2"],
"Hotbar3": ["D3"],
"Hotbar4": ["D4"],
"Hotbar5": ["D5"],
"Hotbar6": ["D6"],
"Hotbar7": ["D7"],
"Hotbar8": ["D8"],
"Hotbar9": ["D9"],
"Hotbar10": ["D0"],
"ViewZoomIn": ["OemPlus"],
"ViewZoomOut": ["OemMinus"]
},
"Gamepad":
{
"MouseLeft": ["RightTrigger"],
"MouseRight": ["B"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Jump": ["LeftTrigger"],
"Inventory": ["Y"],
"Grapple": ["B"],
"SmartSelect": ["LeftStick"],
"SmartCursor": ["RightStick"],
"QuickMount": ["A"],
"MapFull": ["Start"],
"MapStyle": ["Back"],
"HotbarMinus": ["LeftShoulder"],
"HotbarPlus": ["RightShoulder"],
"DpadSnap1": ["DPadUp"],
"DpadSnap2": ["DPadRight"],
"DpadSnap3": ["DPadDown"],
"DpadSnap4": ["DPadLeft"],
"LockOn": ["X"]
},
"Mouse And Keyboard UI":
{
"MouseLeft": ["Mouse1", "Space"],
"MouseRight": ["Mouse2"],
"Up": ["W", "Up"],
"Down": ["S", "Down"],
"Left": ["A", "Left"],
"Right": ["D", "Right"],
"Inventory": ["Escape"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
},
"Gamepad UI":
{
"MouseLeft": ["A"],
"MouseRight": ["LeftShoulder"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Inventory": ["B", "Y"],
"Grapple": ["X"],
"SmartSelect": ["Back"],
"SmartCursor": ["RightShoulder"],
"QuickMount": ["RightStick"],
"MapFull": ["Start"],
"HotbarMinus": ["LeftTrigger"],
"HotbarPlus": ["RightTrigger"],
"DpadSnap1": ["DPadUp"],
"DpadSnap2": ["DPadRight"],
"DpadSnap3": ["DPadDown"],
"DpadSnap4": ["DPadLeft"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
}
},
"Yoraiz0r's Pick":
{
"Last Launched Version": 194,
"Settings":
{
"Edittable": true,
"Gamepad - HotbarRadialHoldTime": 16,
"Gamepad - LeftThumbstickDeadzoneX": 0.25,
"Gamepad - LeftThumbstickDeadzoneY": 0.4,
"Gamepad - RightThumbstickDeadzoneX": 0.0,
"Gamepad - RightThumbstickDeadzoneY": 0.0,
"Gamepad - LeftThumbstickInvertX": false,
"Gamepad - LeftThumbstickInvertY": false,
"Gamepad - RightThumbstickInvertX": false,
"Gamepad - RightThumbstickInvertY": false,
"Gamepad - TriggersDeadzone": 0.3,
"Gamepad - InterfaceDeadzoneX": 0.2,
"Gamepad - InventoryMoveCD": 6
},
"Mouse And Keyboard":
{
"MouseLeft": ["Mouse1"],
"MouseRight": ["Mouse2"],
"Up": ["W"],
"Down": ["S"],
"Left": ["A"],
"Right": ["D"],
"Jump": ["Space"],
"Inventory": ["Escape"],
"Grapple": ["E"],
"SmartSelect": ["LeftShift"],
"SmartCursor": ["LeftControl"],
"QuickMount": ["R"],
"QuickHeal": ["H"],
"QuickMana": ["J"],
"QuickBuff": ["B"],
"MapZoomIn": ["Add"],
"MapZoomOut": ["Subtract"],
"MapAlphaUp": ["PageUp"],
"MapAlphaDown": ["PageDown"],
"MapFull": ["M"],
"MapStyle": ["Tab"],
"Hotbar1": ["D1"],
"Hotbar2": ["D2"],
"Hotbar3": ["D3"],
"Hotbar4": ["D4"],
"Hotbar5": ["D5"],
"Hotbar6": ["D6"],
"Hotbar7": ["D7"],
"Hotbar8": ["D8"],
"Hotbar9": ["D9"],
"Hotbar10": ["D0"],
"ViewZoomIn": ["OemPlus"],
"ViewZoomOut": ["OemMinus"]
},
"Gamepad":
{
"MouseLeft": ["RightTrigger"],
"MouseRight": ["B"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Jump": ["LeftTrigger"],
"Inventory": ["Y"],
"Grapple": ["LeftShoulder"],
"SmartSelect": ["LeftStick"],
"SmartCursor": ["RightStick"],
"QuickMount": ["X"],
"QuickHeal": ["A"],
"MapFull": ["Start"],
"MapStyle": ["Back"],
"RadialHotbar": ["RightShoulder"],
"DpadSnap1": ["DPadUp"],
"DpadSnap2": ["DPadRight"],
"DpadSnap3": ["DPadDown"],
"DpadSnap4": ["DPadLeft"]
},
"Mouse And Keyboard UI":
{
"MouseLeft": ["Mouse1", "Space"],
"MouseRight": ["Mouse2"],
"Up": ["W", "Up"],
"Down": ["S", "Down"],
"Left": ["A", "Left"],
"Right": ["D", "Right"],
"Inventory": ["Escape"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
},
"Gamepad UI":
{
"MouseLeft": ["A"],
"MouseRight": ["LeftShoulder"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Inventory": ["Y"],
"Grapple": ["X"],
"SmartSelect": ["Back"],
"SmartCursor": ["RightShoulder"],
"QuickMount": ["RightStick"],
"MapFull": ["Start"],
"HotbarMinus": ["LeftTrigger"],
"HotbarPlus": ["RightTrigger"],
"DpadSnap1": ["DPadUp"],
"DpadSnap2": ["DPadRight"],
"DpadSnap3": ["DPadDown"],
"DpadSnap4": ["DPadLeft"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"],
"LockOn": ["B"]
}
},
"Console (Playstation)":
{
"Last Launched Version": 194,
"Settings":
{
"Edittable": true,
"Gamepad - HotbarRadialHoldTime": 16,
"Gamepad - LeftThumbstickDeadzoneX": 0.25,
"Gamepad - LeftThumbstickDeadzoneY": 0.4,
"Gamepad - RightThumbstickDeadzoneX": 0.0,
"Gamepad - RightThumbstickDeadzoneY": 0.0,
"Gamepad - LeftThumbstickInvertX": false,
"Gamepad - LeftThumbstickInvertY": false,
"Gamepad - RightThumbstickInvertX": false,
"Gamepad - RightThumbstickInvertY": false,
"Gamepad - TriggersDeadzone": 0.3,
"Gamepad - InterfaceDeadzoneX": 0.2,
"Gamepad - InventoryMoveCD": 6
},
"Mouse And Keyboard":
{
"MouseLeft": ["Mouse1"],
"MouseRight": ["Mouse2"],
"Up": ["W"],
"Down": ["S"],
"Left": ["A"],
"Right": ["D"],
"Jump": ["Space"],
"Inventory": ["Escape"],
"Grapple": ["E"],
"SmartSelect": ["LeftShift"],
"SmartCursor": ["LeftControl"],
"QuickMount": ["R"],
"QuickHeal": ["H"],
"QuickMana": ["J"],
"QuickBuff": ["B"],
"MapZoomIn": ["Add"],
"MapZoomOut": ["Subtract"],
"MapAlphaUp": ["PageUp"],
"MapAlphaDown": ["PageDown"],
"MapFull": ["M"],
"MapStyle": ["Tab"],
"Hotbar1": ["D1"],
"Hotbar2": ["D2"],
"Hotbar3": ["D3"],
"Hotbar4": ["D4"],
"Hotbar5": ["D5"],
"Hotbar6": ["D6"],
"Hotbar7": ["D7"],
"Hotbar8": ["D8"],
"Hotbar9": ["D9"],
"Hotbar10": ["D0"],
"ViewZoomIn": ["OemPlus"],
"ViewZoomOut": ["OemMinus"]
},
"Gamepad":
{
"MouseLeft": ["RightShoulder"],
"MouseRight": ["B"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Jump": ["A"],
"Inventory": ["Y"],
"Grapple": ["LeftShoulder"],
"SmartSelect": ["LeftStick"],
"SmartCursor": ["RightStick"],
"QuickMount": ["Back"],
"MapFull": ["Start"],
"HotbarMinus": ["LeftTrigger"],
"HotbarPlus": ["RightTrigger"],
"DpadRadial1": ["DPadUp"],
"DpadRadial2": ["DPadRight"],
"DpadRadial3": ["DPadDown"],
"DpadRadial4": ["DPadLeft"],
"LockOn": ["X"]
},
"Mouse And Keyboard UI":
{
"MouseLeft": ["Mouse1", "Space"],
"MouseRight": ["Mouse2"],
"Up": ["W", "Up"],
"Down": ["S", "Down"],
"Left": ["A", "Left"],
"Right": ["D", "Right"],
"Inventory": ["Escape"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
},
"Gamepad UI":
{
"MouseLeft": ["A"],
"MouseRight": ["LeftShoulder"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Inventory": ["B", "Y"],
"Grapple": ["X"],
"SmartSelect": ["Back"],
"SmartCursor": ["RightShoulder"],
"QuickMount": ["RightStick"],
"MapFull": ["Start"],
"HotbarMinus": ["LeftTrigger"],
"HotbarPlus": ["RightTrigger"],
"DpadRadial1": ["DPadUp"],
"DpadRadial2": ["DPadRight"],
"DpadRadial3": ["DPadDown"],
"DpadRadial4": ["DPadLeft"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
}
},
"Console (Xbox)":
{
"Last Launched Version": 194,
"Settings":
{
"Edittable": true,
"Gamepad - HotbarRadialHoldTime": 16,
"Gamepad - LeftThumbstickDeadzoneX": 0.25,
"Gamepad - LeftThumbstickDeadzoneY": 0.4,
"Gamepad - RightThumbstickDeadzoneX": 0.0,
"Gamepad - RightThumbstickDeadzoneY": 0.0,
"Gamepad - LeftThumbstickInvertX": false,
"Gamepad - LeftThumbstickInvertY": false,
"Gamepad - RightThumbstickInvertX": false,
"Gamepad - RightThumbstickInvertY": false,
"Gamepad - TriggersDeadzone": 0.3,
"Gamepad - InterfaceDeadzoneX": 0.2,
"Gamepad - InventoryMoveCD": 6
},
"Mouse And Keyboard":
{
"MouseLeft": ["Mouse1"],
"MouseRight": ["Mouse2"],
"Up": ["W"],
"Down": ["S"],
"Left": ["A"],
"Right": ["D"],
"Jump": ["Space"],
"Inventory": ["Escape"],
"Grapple": ["E"],
"SmartSelect": ["LeftShift"],
"SmartCursor": ["LeftControl"],
"QuickMount": ["R"],
"QuickHeal": ["H"],
"QuickMana": ["J"],
"QuickBuff": ["B"],
"MapZoomIn": ["Add"],
"MapZoomOut": ["Subtract"],
"MapAlphaUp": ["PageUp"],
"MapAlphaDown": ["PageDown"],
"MapFull": ["M"],
"MapStyle": ["Tab"],
"Hotbar1": ["D1"],
"Hotbar2": ["D2"],
"Hotbar3": ["D3"],
"Hotbar4": ["D4"],
"Hotbar5": ["D5"],
"Hotbar6": ["D6"],
"Hotbar7": ["D7"],
"Hotbar8": ["D8"],
"Hotbar9": ["D9"],
"Hotbar10": ["D0"],
"ViewZoomIn": ["OemPlus"],
"ViewZoomOut": ["OemMinus"]
},
"Gamepad":
{
"MouseLeft": ["RightTrigger"],
"MouseRight": ["B"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Jump": ["A"],
"Inventory": ["Y"],
"Grapple": ["LeftTrigger"],
"SmartSelect": ["LeftStick"],
"SmartCursor": ["RightStick"],
"QuickMount": ["Back"],
"MapFull": ["Start"],
"HotbarMinus": ["LeftShoulder"],
"HotbarPlus": ["RightShoulder"],
"DpadRadial1": ["DPadUp"],
"DpadRadial2": ["DPadRight"],
"DpadRadial3": ["DPadDown"],
"DpadRadial4": ["DPadLeft"],
"LockOn": ["X"]
},
"Mouse And Keyboard UI":
{
"MouseLeft": ["Mouse1", "Space"],
"MouseRight": ["Mouse2"],
"Up": ["W", "Up"],
"Down": ["S", "Down"],
"Left": ["A", "Left"],
"Right": ["D", "Right"],
"Inventory": ["Escape"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
},
"Gamepad UI":
{
"MouseLeft": ["A"],
"MouseRight": ["LeftShoulder"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Inventory": ["B", "Y"],
"Grapple": ["X"],
"SmartSelect": ["Back"],
"SmartCursor": ["RightShoulder"],
"QuickMount": ["RightStick"],
"MapFull": ["Start"],
"HotbarMinus": ["LeftTrigger"],
"HotbarPlus": ["RightTrigger"],
"DpadRadial1": ["DPadUp"],
"DpadRadial2": ["DPadRight"],
"DpadRadial3": ["DPadDown"],
"DpadRadial4": ["DPadLeft"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
}
},
"Custom":
{
"Last Launched Version": 194,
"Settings":
{
"Edittable": true,
"Gamepad - HotbarRadialHoldTime": 16,
"Gamepad - LeftThumbstickDeadzoneX": 0.25,
"Gamepad - LeftThumbstickDeadzoneY": 0.4,
"Gamepad - RightThumbstickDeadzoneX": 0.0,
"Gamepad - RightThumbstickDeadzoneY": 0.0,
"Gamepad - LeftThumbstickInvertX": false,
"Gamepad - LeftThumbstickInvertY": false,
"Gamepad - RightThumbstickInvertX": false,
"Gamepad - RightThumbstickInvertY": false,
"Gamepad - TriggersDeadzone": 0.3,
"Gamepad - InterfaceDeadzoneX": 0.2,
"Gamepad - InventoryMoveCD": 6
},
"Mouse And Keyboard":
{
"MouseLeft": ["Mouse1"],
"MouseRight": ["Mouse2"],
"Up": ["W"],
"Down": ["S"],
"Left": ["A"],
"Right": ["D"],
"Jump": ["Space"],
"Inventory": ["Escape"],
"Grapple": ["E"],
"SmartSelect": ["LeftShift"],
"SmartCursor": ["LeftControl"],
"QuickMount": ["R"],
"QuickHeal": ["H"],
"QuickMana": ["J"],
"QuickBuff": ["B"],
"MapZoomIn": ["Add"],
"MapZoomOut": ["Subtract"],
"MapAlphaUp": ["PageUp"],
"MapAlphaDown": ["PageDown"],
"MapFull": ["M"],
"MapStyle": ["Tab"],
"Hotbar1": ["D1"],
"Hotbar2": ["D2"],
"Hotbar3": ["D3"],
"Hotbar4": ["D4"],
"Hotbar5": ["D5"],
"Hotbar6": ["D6"],
"Hotbar7": ["D7"],
"Hotbar8": ["D8"],
"Hotbar9": ["D9"],
"Hotbar10": ["D0"],
"ViewZoomIn": ["OemPlus"],
"ViewZoomOut": ["OemMinus"]
},
"Gamepad":
{
"MouseLeft": ["RightTrigger"],
"MouseRight": ["B"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Jump": ["LeftTrigger"],
"Inventory": ["Y"],
"Grapple": ["B"],
"SmartSelect": ["LeftStick"],
"SmartCursor": ["RightStick"],
"QuickMount": ["A"],
"MapFull": ["Start"],
"MapStyle": ["Back"],
"HotbarMinus": ["LeftShoulder"],
"HotbarPlus": ["RightShoulder"],
"DpadSnap1": ["DPadUp"],
"DpadSnap2": ["DPadRight"],
"DpadSnap3": ["DPadDown"],
"DpadSnap4": ["DPadLeft"],
"LockOn": ["X"]
},
"Mouse And Keyboard UI":
{
"MouseLeft": ["Mouse1", "Space"],
"MouseRight": ["Mouse2"],
"Up": ["W", "Up"],
"Down": ["S", "Down"],
"Left": ["A", "Left"],
"Right": ["D", "Right"],
"Inventory": ["Escape"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
},
"Gamepad UI":
{
"MouseLeft": ["A"],
"MouseRight": ["LeftShoulder"],
"Up": ["LeftThumbstickUp"],
"Down": ["LeftThumbstickDown"],
"Left": ["LeftThumbstickLeft"],
"Right": ["LeftThumbstickRight"],
"Inventory": ["B", "Y"],
"Grapple": ["X"],
"SmartSelect": ["Back"],
"SmartCursor": ["RightShoulder"],
"QuickMount": ["RightStick"],
"MapFull": ["Start"],
"HotbarMinus": ["LeftTrigger"],
"HotbarPlus": ["RightTrigger"],
"DpadSnap1": ["DPadUp"],
"DpadSnap2": ["DPadRight"],
"DpadSnap3": ["DPadDown"],
"DpadSnap4": ["DPadLeft"],
"MenuUp": ["DPadUp"],
"MenuDown": ["DPadDown"],
"MenuLeft": ["DPadLeft"],
"MenuRight": ["DPadRight"]
}
}
}
三、Hex内容下对文件的初级解析
1.wld文件的HEX读取方法
根据TEdit的源码,读取wld文件之前会对其版本进行分类。
1 public static World LoadWorld(string filename)//部分代码-加载世界 2 { 3 var w = new World(); 4 uint curVersion = 0; 5 try 6 { 7 lock (_fileLock)//线程锁定 8 { 9 using (var b = new BinaryReader(File.OpenRead(filename)))//二进制读取流 10 { 11 w.Version = b.ReadUInt32();//首先获取世界的创建版本,例如1.3.4对应194 12 curVersion = w.Version; 13 if (w.Version > 87) //版本分流 14 LoadV2(b, filename, w);//二级读取 15 else 16 LoadV1(b, filename, w);//一级读取 17 } 18 w.LastSave = File.GetLastWriteTimeUtc(filename);//读取时间 19 } 20 } 21 catch (Exception err) 22 { 23 //TEdit的错误内容 24 string msg = 25 "There was an error reading the world file. This is usually caused by a corrupt save file or a world version newer than supported.\r\n\r\n" + 26 $"TEdit v{TEditXna.App.Version.FileVersion}\r\n" + 27 $"TEdit Max World: {CompatibleVersion} Current World: {curVersion}\r\n\r\n" + 28 "Do you wish to force it to load anyway?\r\n\r\n" + 29 "WARNING: This may have unexpected results including corrupt world files and program crashes.\r\n\r\n" + 30 $"The error is :\r\n{err.Message}\r\n\r\n{err}\r\n"; 31 if (MessageBox.Show(msg, "World File Error", MessageBoxButton.YesNo, MessageBoxImage.Error) != 32 MessageBoxResult.Yes) 33 return null; 34 } 35 return w; 36 }
(内容由开源编辑器TEdit提供)
2.源码
1.World.FileV1.cs
1 using System.Collections.Generic; 2 using System.ComponentModel; 3 using TEdit.Utility; 4 using TEditXna.Helper; 5 using TEditXNA.Terraria.Objects; 6 using TEdit.Geometry.Primitives; 7 using Vector2 = TEdit.Geometry.Primitives.Vector2; 8 using System; 9 using System.Collections.ObjectModel; 10 using System.IO; 11 12 namespace TEditXNA.Terraria 13 { 14 public partial class World 15 { 16 17 18 private static void SaveV1(World world, BinaryWriter bw) 19 { 20 bw.Write(CompatibleVersion); 21 bw.Write(world.Title); 22 bw.Write(world.WorldId); 23 bw.Write((int) world.LeftWorld); 24 bw.Write((int) world.RightWorld); 25 bw.Write((int) world.TopWorld); 26 bw.Write((int) world.BottomWorld); 27 bw.Write(world.TilesHigh); 28 bw.Write(world.TilesWide); 29 30 bw.Write((byte) world.MoonType); 31 bw.Write(world.TreeX0); 32 bw.Write(world.TreeX1); 33 bw.Write(world.TreeX2); 34 bw.Write(world.TreeStyle0); 35 bw.Write(world.TreeStyle1); 36 bw.Write(world.TreeStyle2); 37 bw.Write(world.TreeStyle3); 38 bw.Write(world.CaveBackX0); 39 bw.Write(world.CaveBackX1); 40 bw.Write(world.CaveBackX2); 41 bw.Write(world.CaveBackStyle0); 42 bw.Write(world.CaveBackStyle1); 43 bw.Write(world.CaveBackStyle2); 44 bw.Write(world.CaveBackStyle3); 45 bw.Write(world.IceBackStyle); 46 bw.Write(world.JungleBackStyle); 47 bw.Write(world.HellBackStyle); 48 49 bw.Write(world.SpawnX); 50 bw.Write(world.SpawnY); 51 bw.Write(world.GroundLevel); 52 bw.Write(world.RockLevel); 53 bw.Write(world.Time); 54 bw.Write(world.DayTime); 55 bw.Write(world.MoonPhase); 56 bw.Write(world.BloodMoon); 57 bw.Write(world.IsEclipse); 58 bw.Write(world.DungeonX); 59 bw.Write(world.DungeonY); 60 61 bw.Write(world.IsCrimson); 62 63 bw.Write(world.DownedBoss1); 64 bw.Write(world.DownedBoss2); 65 bw.Write(world.DownedBoss3); 66 bw.Write(world.DownedQueenBee); 67 bw.Write(world.DownedMechBoss1); 68 bw.Write(world.DownedMechBoss2); 69 bw.Write(world.DownedMechBoss3); 70 bw.Write(world.DownedMechBossAny); 71 bw.Write(world.DownedPlantBoss); 72 bw.Write(world.DownedGolemBoss); 73 bw.Write(world.SavedGoblin); 74 bw.Write(world.SavedWizard); 75 bw.Write(world.SavedMech); 76 bw.Write(world.DownedGoblins); 77 bw.Write(world.DownedClown); 78 bw.Write(world.DownedFrost); 79 bw.Write(world.DownedPirates); 80 81 bw.Write(world.ShadowOrbSmashed); 82 bw.Write(world.SpawnMeteor); 83 bw.Write((byte) world.ShadowOrbCount); 84 bw.Write(world.AltarCount); 85 bw.Write(world.HardMode); 86 bw.Write(world.InvasionDelay); 87 bw.Write(world.InvasionSize); 88 bw.Write(world.InvasionType); 89 bw.Write(world.InvasionX); 90 91 bw.Write(world.TempRaining); 92 bw.Write(world.TempRainTime); 93 bw.Write(world.TempMaxRain); 94 bw.Write(world.OreTier1); 95 bw.Write(world.OreTier2); 96 bw.Write(world.OreTier3); 97 bw.Write(world.BgTree); 98 bw.Write(world.BgCorruption); 99 bw.Write(world.BgJungle); 100 bw.Write(world.BgSnow); 101 bw.Write(world.BgHallow); 102 bw.Write(world.BgCrimson); 103 bw.Write(world.BgDesert); 104 bw.Write(world.BgOcean); 105 bw.Write((int) world.CloudBgActive); 106 bw.Write(world.NumClouds); 107 bw.Write(world.WindSpeedSet); 108 109 110 for (int x = 0; x < world.TilesWide; ++x) 111 { 112 OnProgressChanged(world, 113 new ProgressChangedEventArgs(x.ProgressPercentage(world.TilesWide), "Saving UndoTiles...")); 114 115 int rle = 0; 116 for (int y = 0; y < world.TilesHigh; y = y + rle + 1) 117 { 118 Tile curTile = world.Tiles[x, y]; 119 WriteTileDataToStreamV1(curTile, bw); 120 121 int rleTemp = 1; 122 while (y + rleTemp < world.TilesHigh && curTile.Equals(world.Tiles[x, (y + rleTemp)])) 123 ++rleTemp; 124 rle = rleTemp - 1; 125 bw.Write((short) rle); 126 } 127 } 128 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Saving Chests...")); 129 WriteChestDataToStreamV1(world.Chests, bw); 130 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Saving Signs...")); 131 WriteSignDataToStreamV1(world.Signs, bw); 132 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Saving NPC Data...")); 133 foreach (NPC curNpc in world.NPCs) 134 { 135 bw.Write(true); 136 bw.Write(curNpc.Name); 137 bw.Write(curNpc.Position.X); 138 bw.Write(curNpc.Position.Y); 139 bw.Write(curNpc.IsHomeless); 140 bw.Write(curNpc.Home.X); 141 bw.Write(curNpc.Home.Y); 142 } 143 bw.Write(false); 144 145 146 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Saving NPC Names...")); 147 148 world.FixNpcs(); 149 150 bw.Write(world.GetNpc(17).Name); 151 bw.Write(world.GetNpc(18).Name); 152 bw.Write(world.GetNpc(19).Name); 153 bw.Write(world.GetNpc(20).Name); 154 bw.Write(world.GetNpc(22).Name); 155 bw.Write(world.GetNpc(54).Name); 156 bw.Write(world.GetNpc(38).Name); 157 bw.Write(world.GetNpc(107).Name); 158 bw.Write(world.GetNpc(108).Name); 159 bw.Write(world.GetNpc(124).Name); 160 bw.Write(world.GetNpc(160).Name); 161 bw.Write(world.GetNpc(178).Name); 162 bw.Write(world.GetNpc(207).Name); 163 bw.Write(world.GetNpc(208).Name); 164 bw.Write(world.GetNpc(209).Name); 165 bw.Write(world.GetNpc(227).Name); 166 bw.Write(world.GetNpc(228).Name); 167 bw.Write(world.GetNpc(229).Name); 168 169 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Saving Validation Data...")); 170 bw.Write(true); 171 bw.Write(world.Title); 172 bw.Write(world.WorldId); 173 } 174 175 public static void WriteSignDataToStreamV1(IList<Sign> signs, BinaryWriter bw) 176 { 177 for (int i = 0; i < 1000; ++i) 178 { 179 if (i >= signs.Count || string.IsNullOrWhiteSpace(signs[i].Text)) 180 { 181 bw.Write(false); 182 } 183 else 184 { 185 Sign curSign = signs[i]; 186 bw.Write(true); 187 bw.Write(curSign.Text); 188 bw.Write(curSign.X); 189 bw.Write(curSign.Y); 190 } 191 } 192 } 193 194 public static void WriteChestDataToStreamV1(IList<Chest> chests, BinaryWriter bw) 195 { 196 for (int i = 0; i < 1000; ++i) 197 { 198 if (i >= chests.Count) 199 { 200 bw.Write(false); 201 } 202 else 203 { 204 Chest curChest = chests[i]; 205 bw.Write(true); 206 bw.Write(curChest.X); 207 bw.Write(curChest.Y); 208 for (int j = 0; j < Chest.MaxItems; ++j) 209 { 210 if (curChest.Items.Count > j) 211 { 212 if (curChest.Items[j].NetId == 0) 213 curChest.Items[j].StackSize = 0; 214 215 bw.Write((short)curChest.Items[j].StackSize); 216 if (curChest.Items[j].StackSize > 0) 217 { 218 bw.Write(curChest.Items[j].NetId); // TODO Verify 219 bw.Write(curChest.Items[j].Prefix); 220 } 221 } 222 else 223 bw.Write((byte)0); 224 } 225 } 226 } 227 } 228 229 public static void WriteTileDataToStreamV1(Tile curTile, BinaryWriter bw) 230 { 231 if (curTile.Type == (int)TileType.IceByRod) 232 curTile.IsActive = false; 233 234 bw.Write(curTile.IsActive); 235 if (curTile.IsActive) 236 { 237 bw.Write(curTile.Type); 238 if (TileProperties[curTile.Type].IsFramed) 239 { 240 bw.Write(curTile.U); 241 bw.Write(curTile.V); 242 } 243 244 if (curTile.TileColor > 0) 245 { 246 bw.Write(true); 247 bw.Write(curTile.TileColor); 248 } 249 else 250 bw.Write(false); 251 } 252 if (curTile.Wall > 0) 253 { 254 bw.Write(true); 255 bw.Write(curTile.Wall); 256 257 if (curTile.WallColor > 0) 258 { 259 bw.Write(true); 260 bw.Write(curTile.WallColor); 261 } 262 else 263 bw.Write(false); 264 } 265 else 266 bw.Write(false); 267 268 if (curTile.LiquidAmount > 0) 269 { 270 bw.Write(true); 271 bw.Write(curTile.LiquidAmount); 272 bw.Write(curTile.LiquidType == LiquidType.Lava); 273 bw.Write(curTile.LiquidType == LiquidType.Honey); 274 } 275 else 276 bw.Write(false); 277 278 bw.Write(curTile.WireRed); 279 bw.Write(curTile.WireGreen); 280 bw.Write(curTile.WireBlue); 281 bw.Write(curTile.BrickStyle != 0); 282 bw.Write((byte)curTile.BrickStyle); 283 bw.Write(curTile.Actuator); 284 bw.Write(curTile.InActive); 285 } 286 287 private static void LoadV1(BinaryReader reader, string filename, World w) 288 { 289 uint version = w.Version; 290 w.Title = reader.ReadString(); 291 w.WorldId = reader.ReadInt32(); 292 293 w.Rand = new Random(w.WorldId); 294 295 w.LeftWorld = reader.ReadInt32(); 296 w.RightWorld = reader.ReadInt32(); 297 w.TopWorld = reader.ReadInt32(); 298 w.BottomWorld = reader.ReadInt32(); 299 w.TilesHigh = reader.ReadInt32(); 300 w.TilesWide = reader.ReadInt32(); 301 302 //if (w.TilesHigh > 10000 || w.TilesWide > 10000 || w.TilesHigh <= 0 || w.TilesWide <= 0) 303 // throw new FileLoadException(string.Format("Invalid File: {0}", filename)); 304 305 306 if (version >= 63) 307 w.MoonType = reader.ReadByte(); 308 else 309 w.MoonType = (byte)w.Rand.Next(MaxMoons); 310 311 312 if (version >= 44) 313 { 314 w.TreeX0 = reader.ReadInt32(); 315 w.TreeX1 = reader.ReadInt32(); 316 w.TreeX2 = reader.ReadInt32(); 317 w.TreeStyle0 = reader.ReadInt32(); 318 w.TreeStyle1 = reader.ReadInt32(); 319 w.TreeStyle2 = reader.ReadInt32(); 320 w.TreeStyle3 = reader.ReadInt32(); 321 } 322 if (version >= 60) 323 { 324 w.CaveBackX0 = reader.ReadInt32(); 325 w.CaveBackX1 = reader.ReadInt32(); 326 w.CaveBackX2 = reader.ReadInt32(); 327 w.CaveBackStyle0 = reader.ReadInt32(); 328 w.CaveBackStyle1 = reader.ReadInt32(); 329 w.CaveBackStyle2 = reader.ReadInt32(); 330 w.CaveBackStyle3 = reader.ReadInt32(); 331 w.IceBackStyle = reader.ReadInt32(); 332 if (version >= 61) 333 { 334 w.JungleBackStyle = reader.ReadInt32(); 335 w.HellBackStyle = reader.ReadInt32(); 336 } 337 } 338 else 339 { 340 w.CaveBackX[0] = w.TilesWide/2; 341 w.CaveBackX[1] = w.TilesWide; 342 w.CaveBackX[2] = w.TilesWide; 343 w.CaveBackStyle0 = 0; 344 w.CaveBackStyle1 = 1; 345 w.CaveBackStyle2 = 2; 346 w.CaveBackStyle3 = 3; 347 w.IceBackStyle = 0; 348 w.JungleBackStyle = 0; 349 w.HellBackStyle = 0; 350 } 351 352 w.SpawnX = reader.ReadInt32(); 353 w.SpawnY = reader.ReadInt32(); 354 w.GroundLevel = (int) reader.ReadDouble(); 355 w.RockLevel = (int) reader.ReadDouble(); 356 357 // read world flags 358 w.Time = reader.ReadDouble(); 359 w.DayTime = reader.ReadBoolean(); 360 w.MoonPhase = reader.ReadInt32(); 361 w.BloodMoon = reader.ReadBoolean(); 362 363 if (version >= 70) 364 { 365 w.IsEclipse = reader.ReadBoolean(); 366 } 367 368 w.DungeonX = reader.ReadInt32(); 369 w.DungeonY = reader.ReadInt32(); 370 371 if (version >= 56) 372 { 373 w.IsCrimson = reader.ReadBoolean(); 374 } 375 else 376 { 377 w.IsCrimson = false; 378 } 379 380 w.DownedBoss1 = reader.ReadBoolean(); 381 w.DownedBoss2 = reader.ReadBoolean(); 382 w.DownedBoss3 = reader.ReadBoolean(); 383 384 if (version >= 66) 385 { 386 w.DownedQueenBee = reader.ReadBoolean(); 387 } 388 if (version >= 44) 389 { 390 w.DownedMechBoss1 = reader.ReadBoolean(); 391 w.DownedMechBoss2 = reader.ReadBoolean(); 392 w.DownedMechBoss3 = reader.ReadBoolean(); 393 w.DownedMechBossAny = reader.ReadBoolean(); 394 } 395 if (version >= 64) 396 { 397 w.DownedPlantBoss = reader.ReadBoolean(); 398 w.DownedGolemBoss = reader.ReadBoolean(); 399 } 400 if (version >= 29) 401 { 402 w.SavedGoblin = reader.ReadBoolean(); 403 w.SavedWizard = reader.ReadBoolean(); 404 if (version >= 34) 405 { 406 w.SavedMech = reader.ReadBoolean(); 407 } 408 w.DownedGoblins = reader.ReadBoolean(); 409 } 410 if (version >= 32) 411 w.DownedClown = reader.ReadBoolean(); 412 if (version >= 37) 413 w.DownedFrost = reader.ReadBoolean(); 414 if (version >= 56) 415 w.DownedPirates = reader.ReadBoolean(); 416 417 418 w.ShadowOrbSmashed = reader.ReadBoolean(); 419 w.SpawnMeteor = reader.ReadBoolean(); 420 w.ShadowOrbCount = reader.ReadByte(); 421 422 if (version >= 23) 423 { 424 w.AltarCount = reader.ReadInt32(); 425 w.HardMode = reader.ReadBoolean(); 426 } 427 428 w.InvasionDelay = reader.ReadInt32(); 429 w.InvasionSize = reader.ReadInt32(); 430 w.InvasionType = reader.ReadInt32(); 431 w.InvasionX = reader.ReadDouble(); 432 433 if (version >= 53) 434 { 435 w.TempRaining = reader.ReadBoolean(); 436 w.TempRainTime = reader.ReadInt32(); 437 w.TempMaxRain = reader.ReadSingle(); 438 } 439 if (version >= 54) 440 { 441 w.OreTier1 = reader.ReadInt32(); 442 w.OreTier2 = reader.ReadInt32(); 443 w.OreTier3 = reader.ReadInt32(); 444 } 445 else if (version < 23 || w.AltarCount != 0) 446 { 447 w.OreTier1 = 107; 448 w.OreTier2 = 108; 449 w.OreTier3 = 111; 450 } 451 else 452 { 453 w.OreTier1 = -1; 454 w.OreTier2 = -1; 455 w.OreTier3 = -1; 456 } 457 458 if (version >= 55) 459 { 460 w.BgTree = reader.ReadByte(); 461 w.BgCorruption = reader.ReadByte(); 462 w.BgJungle = reader.ReadByte(); 463 } 464 if (version >= 60) 465 { 466 w.BgSnow = reader.ReadByte(); 467 w.BgHallow = reader.ReadByte(); 468 w.BgCorruption = reader.ReadByte(); 469 w.BgDesert = reader.ReadByte(); 470 w.BgOcean = reader.ReadByte(); 471 } 472 473 if (version >= 60) 474 { 475 w.CloudBgActive = reader.ReadInt32(); 476 } 477 else 478 { 479 w.CloudBgActive = -w.Rand.Next(8640, 86400); 480 } 481 482 if (version >= 62) 483 { 484 w.NumClouds = reader.ReadInt16(); 485 w.WindSpeedSet = reader.ReadSingle(); 486 } 487 488 w.Tiles = new Tile[w.TilesWide, w.TilesHigh]; 489 for (int i = 0; i < w.TilesWide; i++) 490 { 491 for (int j = 0; j < w.TilesHigh; j++) 492 { 493 w.Tiles[i, j] = new Tile(); 494 } 495 } 496 497 498 for (int x = 0; x < w.TilesWide; ++x) 499 { 500 OnProgressChanged(null, 501 new ProgressChangedEventArgs(x.ProgressPercentage(w.TilesWide), "Loading UndoTiles...")); 502 503 for (int y = 0; y < w.TilesHigh; y++) 504 { 505 Tile tile = ReadTileDataFromStreamV1(reader, version); 506 507 // read complete, start compression 508 w.Tiles[x, y] = tile; 509 510 if (version >= 25) 511 { 512 int rle = reader.ReadInt16(); 513 514 if (rle < 0) 515 throw new ApplicationException("Invalid Tile Data!"); 516 517 if (rle > 0) 518 { 519 for (int k = y + 1; k < y + rle + 1; k++) 520 { 521 var tcopy = (Tile) tile.Clone(); 522 w.Tiles[x, k] = tcopy; 523 } 524 y = y + rle; 525 } 526 } 527 } 528 } 529 530 if (version < 67) 531 w.FixSunflowers(); 532 if (version < 72) 533 w.FixChand(); 534 535 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading Chests...")); 536 w.Chests.Clear(); 537 ((ObservableCollection<Chest>)w.Chests).AddRange(ReadChestDataFromStreamV1(reader, version)); 538 539 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading Signs...")); 540 w.Signs.Clear(); 541 542 foreach (Sign sign in ReadSignDataFromStreamV1(reader)) 543 { 544 if (w.Tiles[sign.X, sign.Y].IsActive && Tile.IsSign(w.Tiles[sign.X, sign.Y].Type)) 545 { 546 w.Signs.Add(sign); 547 } 548 } 549 550 w.NPCs.Clear(); 551 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading NPC Data...")); 552 while (reader.ReadBoolean()) 553 { 554 var npc = new NPC(); 555 npc.Name = reader.ReadString(); 556 npc.Position = new Vector2(reader.ReadSingle(), reader.ReadSingle()); 557 npc.IsHomeless = reader.ReadBoolean(); 558 npc.Home = new Vector2Int32(reader.ReadInt32(), reader.ReadInt32()); 559 npc.SpriteId = 0; 560 if (NpcIds.ContainsKey(npc.Name)) 561 npc.SpriteId = NpcIds[npc.Name]; 562 563 w.NPCs.Add(npc); 564 } 565 // if (version>=0x1f) read the names of the following npcs: 566 // merchant, nurse, arms dealer, dryad, guide, clothier, demolitionist, 567 // tinkerer and wizard 568 // if (version>=0x23) read the name of the mechanic 569 570 571 if (version >= 31) 572 { 573 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading NPC Names...")); 574 w.CharacterNames.Add(new NpcName(17, reader.ReadString())); 575 w.CharacterNames.Add(new NpcName(18, reader.ReadString())); 576 w.CharacterNames.Add(new NpcName(19, reader.ReadString())); 577 w.CharacterNames.Add(new NpcName(20, reader.ReadString())); 578 w.CharacterNames.Add(new NpcName(22, reader.ReadString())); 579 w.CharacterNames.Add(new NpcName(54, reader.ReadString())); 580 w.CharacterNames.Add(new NpcName(38, reader.ReadString())); 581 w.CharacterNames.Add(new NpcName(107, reader.ReadString())); 582 w.CharacterNames.Add(new NpcName(108, reader.ReadString())); 583 if (version >= 35) 584 w.CharacterNames.Add(new NpcName(124, reader.ReadString())); 585 else 586 w.CharacterNames.Add(new NpcName(124, "Nancy")); 587 588 if (version >= 65) 589 { 590 w.CharacterNames.Add(new NpcName(160, reader.ReadString())); 591 w.CharacterNames.Add(new NpcName(178, reader.ReadString())); 592 w.CharacterNames.Add(new NpcName(207, reader.ReadString())); 593 w.CharacterNames.Add(new NpcName(208, reader.ReadString())); 594 w.CharacterNames.Add(new NpcName(209, reader.ReadString())); 595 w.CharacterNames.Add(new NpcName(227, reader.ReadString())); 596 w.CharacterNames.Add(new NpcName(228, reader.ReadString())); 597 w.CharacterNames.Add(new NpcName(229, reader.ReadString())); 598 } 599 else 600 { 601 w.CharacterNames.Add(GetNewNpc(160)); 602 w.CharacterNames.Add(GetNewNpc(178)); 603 w.CharacterNames.Add(GetNewNpc(207)); 604 w.CharacterNames.Add(GetNewNpc(208)); 605 w.CharacterNames.Add(GetNewNpc(209)); 606 w.CharacterNames.Add(GetNewNpc(227)); 607 w.CharacterNames.Add(GetNewNpc(228)); 608 w.CharacterNames.Add(GetNewNpc(229)); 609 } 610 } 611 else 612 { 613 w.CharacterNames.Add(GetNewNpc(17)); 614 w.CharacterNames.Add(GetNewNpc(18)); 615 w.CharacterNames.Add(GetNewNpc(19)); 616 w.CharacterNames.Add(GetNewNpc(20)); 617 w.CharacterNames.Add(GetNewNpc(22)); 618 w.CharacterNames.Add(GetNewNpc(54)); 619 w.CharacterNames.Add(GetNewNpc(38)); 620 w.CharacterNames.Add(GetNewNpc(107)); 621 w.CharacterNames.Add(GetNewNpc(108)); 622 w.CharacterNames.Add(GetNewNpc(124)); 623 w.CharacterNames.Add(GetNewNpc(160)); 624 w.CharacterNames.Add(GetNewNpc(178)); 625 w.CharacterNames.Add(GetNewNpc(207)); 626 w.CharacterNames.Add(GetNewNpc(208)); 627 w.CharacterNames.Add(GetNewNpc(209)); 628 w.CharacterNames.Add(GetNewNpc(227)); 629 w.CharacterNames.Add(GetNewNpc(228)); 630 w.CharacterNames.Add(GetNewNpc(229)); 631 } 632 if (version >= 7) 633 { 634 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Validating File...")); 635 bool validation = reader.ReadBoolean(); 636 string checkTitle = reader.ReadString(); 637 int checkVersion = reader.ReadInt32(); 638 if (validation && checkTitle == w.Title && checkVersion == w.WorldId) 639 { 640 //w.loadSuccess = true; 641 } 642 else 643 { 644 reader.Close(); 645 throw new FileLoadException( 646 $"Error reading world file validation parameters! {filename}"); 647 } 648 } 649 OnProgressChanged(null, new ProgressChangedEventArgs(0, "World Load Complete.")); 650 } 651 652 public static IEnumerable<Sign> ReadSignDataFromStreamV1(BinaryReader b) 653 { 654 for (int i = 0; i < 1000; i++) 655 { 656 if (b.ReadBoolean()) 657 { 658 var sign = new Sign(); 659 sign.Text = b.ReadString(); 660 sign.X = b.ReadInt32(); 661 sign.Y = b.ReadInt32(); 662 663 yield return sign; 664 } 665 } 666 } 667 668 public static IEnumerable<Chest> ReadChestDataFromStreamV1(BinaryReader b, uint version) 669 { 670 int chestSize = Chest.MaxItems; 671 if (version < 58) 672 chestSize = 20; 673 674 for (int i = 0; i < 1000; i++) 675 { 676 if (b.ReadBoolean()) 677 { 678 var chest = new Chest(b.ReadInt32(), b.ReadInt32()); 679 for (int slot = 0; slot < Chest.MaxItems; slot++) 680 { 681 if (slot < chestSize) 682 { 683 int stackSize = version < 59 ? b.ReadByte() : b.ReadInt16(); 684 chest.Items[slot].StackSize = stackSize; 685 686 if (chest.Items[slot].StackSize > 0) 687 { 688 if (version >= 38) 689 chest.Items[slot].NetId = b.ReadInt32(); 690 else 691 chest.Items[slot].SetFromName(b.ReadString()); 692 693 chest.Items[slot].StackSize = stackSize; 694 // Read prefix 695 if (version >= 36) 696 chest.Items[slot].Prefix = b.ReadByte(); 697 } 698 } 699 } 700 yield return chest; 701 } 702 } 703 } 704 705 public static Tile ReadTileDataFromStreamV1(BinaryReader b, uint version) 706 { 707 var tile = new Tile(); 708 709 tile.IsActive = b.ReadBoolean(); 710 711 TileProperty tileProperty = null; 712 713 if (tile.IsActive) 714 { 715 tile.Type = b.ReadByte(); 716 tileProperty = TileProperties[tile.Type]; 717 718 719 if (tile.Type == (int)TileType.IceByRod) 720 tile.IsActive = false; 721 722 if (version < 72 && 723 (tile.Type == 35 || tile.Type == 36 || tile.Type == 170 || tile.Type == 171 || tile.Type == 172)) 724 { 725 tile.U = b.ReadInt16(); 726 tile.V = b.ReadInt16(); 727 } 728 else if (!tileProperty.IsFramed) 729 { 730 tile.U = -1; 731 tile.V = -1; 732 } 733 else if (version < 28 && tile.Type == (int)(TileType.Torch)) 734 { 735 // torches didn't have extra in older versions. 736 tile.U = 0; 737 tile.V = 0; 738 } 739 else if (version < 40 && tile.Type == (int)TileType.Platform) 740 { 741 tile.U = 0; 742 tile.V = 0; 743 } 744 else 745 { 746 tile.U = b.ReadInt16(); 747 tile.V = b.ReadInt16(); 748 749 if (tile.Type == (int)TileType.Timer) 750 tile.V = 0; 751 } 752 753 754 if (version >= 48 && b.ReadBoolean()) 755 { 756 tile.TileColor = b.ReadByte(); 757 } 758 } 759 760 //skip obsolete hasLight 761 if (version <= 25) 762 b.ReadBoolean(); 763 764 if (b.ReadBoolean()) 765 { 766 tile.Wall = b.ReadByte(); 767 if (version >= 48 && b.ReadBoolean()) 768 tile.WallColor = b.ReadByte(); 769 } 770 771 if (b.ReadBoolean()) 772 { 773 tile.LiquidType = LiquidType.Water; 774 tile.LiquidAmount = b.ReadByte(); 775 if (b.ReadBoolean()) tile.LiquidType = LiquidType.Lava; 776 if (version >= 51) 777 { 778 if (b.ReadBoolean()) tile.LiquidType = LiquidType.Honey; 779 } 780 } 781 782 if (version >= 33) 783 { 784 tile.WireRed = b.ReadBoolean(); 785 } 786 if (version >= 43) 787 { 788 tile.WireGreen = b.ReadBoolean(); 789 tile.WireBlue = b.ReadBoolean(); 790 } 791 792 if (version >= 41) 793 { 794 bool isHalfBrick = b.ReadBoolean(); 795 796 if (tileProperty == null || !tileProperty.IsSolid) 797 isHalfBrick = false; 798 799 if (version >= 49) 800 { 801 tile.BrickStyle = (BrickStyle)b.ReadByte(); 802 803 if (tileProperty == null || !tileProperty.IsSolid) 804 tile.BrickStyle = 0; 805 } 806 } 807 if (version >= 42) 808 { 809 tile.Actuator = b.ReadBoolean(); 810 tile.InActive = b.ReadBoolean(); 811 } 812 return tile; 813 } 814 } 815 }
2.World.FileV2.cs
1 using System.Collections.Generic; 2 using System.ComponentModel; 3 using TEdit.Utility; 4 using TEdit.Geometry.Primitives; 5 using Vector2 = TEdit.Geometry.Primitives.Vector2; 6 using System; 7 using System.IO; 8 9 namespace TEditXNA.Terraria 10 { 11 12 public partial class World 13 { 14 public static uint CompatibleVersion = 192; 15 public static short TileCount = 470; 16 public static short SectionCount = 10; 17 18 public static bool[] TileFrameImportant; 19 20 private static void SaveV2(World world, BinaryWriter bw) 21 { 22 world.Validate(); 23 24 // initialize tileframeimportance array if empty 25 if (TileFrameImportant == null || TileFrameImportant.Length < TileCount) 26 { 27 TileFrameImportant = new bool[TileCount]; 28 for (int i = 0; i < TileCount; i++) 29 { 30 if (TileProperties.Count > i) 31 { 32 TileFrameImportant[i] = TileProperties[i].IsFramed; 33 } 34 } 35 } 36 37 int[] sectionPointers = new int[SectionCount]; 38 39 OnProgressChanged(null, new ProgressChangedEventArgs(0, "Save headers...")); 40 sectionPointers[0] = SaveSectionHeader(world, bw); 41 sectionPointers[1] = SaveHeaderFlags(world, bw); 42 OnProgressChanged(null, new ProgressChangedEventArgs(0, "Save UndoTiles...")); 43 sectionPointers[2] = SaveTiles(world.Tiles, world.TilesWide, world.TilesHigh, bw); 44 45 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Chests...")); 46 sectionPointers[3] = SaveChests(world.Chests, bw); 47 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Signs...")); 48 sectionPointers[4] = SaveSigns(world.Signs, bw); 49 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save NPCs...")); 50 sectionPointers[5] = SaveNPCs(world.NPCs, bw); 51 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Mobs...")); 52 sectionPointers[5] = SaveMobs(world.Mobs, bw); 53 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Tile Entities Section...")); 54 sectionPointers[6] = SaveTileEntities(world, bw); 55 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Weighted Pressure Plates...")); 56 sectionPointers[7] = SavePressurePlate(world.PressurePlates, bw); 57 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Town Manager...")); 58 sectionPointers[8] = SaveTownManager(world.PlayerRooms, bw); 59 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Footers...")); 60 SaveFooter(world, bw); 61 UpdateSectionPointers(sectionPointers, bw); 62 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Save Complete.")); 63 } 64 65 public static int SaveTiles(Tile[,] tiles, int maxX, int maxY, BinaryWriter bw) 66 { 67 for (int x = 0; x < maxX; x++) 68 { 69 OnProgressChanged(null, new ProgressChangedEventArgs(x.ProgressPercentage(maxX), "Saving Tiles...")); 70 71 72 for (int y = 0; y < maxY; y++) 73 { 74 Tile tile = tiles[x, y]; 75 76 int dataIndex; 77 int headerIndex; 78 79 byte[] tileData = SerializeTileData(tile, out dataIndex, out headerIndex); 80 81 // rle compression 82 byte header1 = tileData[headerIndex]; 83 84 short rle = 0; 85 int nextY = y + 1; 86 int remainingY = maxY - y - 1; 87 while (remainingY > 0 && tile.Equals(tiles[x, nextY])) 88 { 89 rle = (short)(rle + 1); 90 remainingY--; 91 nextY++; 92 } 93 94 y = y + rle; 95 96 if (rle > 0) 97 { 98 tileData[dataIndex++] = (byte)(rle & 255); 99 100 if (rle <= 255) 101 { 102 // set bit[6] of header1 for byte size rle 103 header1 = (byte)(header1 | 64); 104 } 105 else 106 { 107 // set bit[7] of header1 for int16 size rle 108 header1 = (byte)(header1 | 128); 109 110 // grab the upper half of the int16 and stick it in tiledata 111 tileData[dataIndex++] = (byte)((rle & 65280) >> 8); 112 } 113 } 114 115 tileData[headerIndex] = header1; 116 // end rle compression 117 118 bw.Write(tileData, headerIndex, dataIndex - headerIndex); 119 } 120 } 121 122 123 return (int)bw.BaseStream.Position; 124 } 125 126 /// <summary> 127 /// BitPack tile data and headers 128 /// </summary> 129 public static byte[] SerializeTileData(Tile tile, out int dataIndex, out int headerIndex) 130 { 131 132 byte[] tileData = new byte[13]; 133 dataIndex = 3; 134 135 byte header3 = (byte)0; 136 byte header2 = (byte)0; 137 byte header1 = (byte)0; 138 139 // tile data 140 if (tile.IsActive) 141 { 142 // activate bit[1] 143 header1 = (byte)(header1 | 2); 144 145 if (tile.Type == (int)TileType.IceByRod && tile.IsActive) 146 { 147 tile.IsActive = false; 148 } 149 150 // save tile type as byte or int16 151 tileData[dataIndex++] = (byte)tile.Type; 152 if (tile.Type > 255) 153 { 154 // write high byte 155 tileData[dataIndex++] = (byte)(tile.Type >> 8); 156 157 // set header1 bit[5] for int16 tile type 158 header1 = (byte)(header1 | 32); 159 } 160 161 if (TileFrameImportant[tile.Type]) 162 { 163 // pack UV coords 164 tileData[dataIndex++] = (byte)(tile.U & 255); 165 tileData[dataIndex++] = (byte)((tile.U & 65280) >> 8); 166 tileData[dataIndex++] = (byte)(tile.V & 255); 167 tileData[dataIndex++] = (byte)((tile.V & 65280) >> 8); 168 } 169 170 if (tile.TileColor != 0) 171 { 172 // set header3 bit[3] for tile color active 173 header3 = (byte)(header3 | 8); 174 tileData[dataIndex++] = tile.TileColor; 175 } 176 } 177 178 // wall data 179 if (tile.Wall != 0) 180 { 181 // set header1 bit[2] for wall active 182 header1 = (byte)(header1 | 4); 183 tileData[dataIndex++] = tile.Wall; 184 185 // save tile wall color 186 if (tile.WallColor != 0) 187 { 188 // set header3 bit[4] for wall color active 189 header3 = (byte)(header3 | 16); 190 tileData[dataIndex++] = tile.WallColor; 191 } 192 } 193 194 // liquid data 195 if (tile.LiquidAmount != 0) 196 { 197 // set bits[3,4] using left shift 198 header1 = (byte)(header1 | (byte)((byte)tile.LiquidType << 3)); 199 tileData[dataIndex++] = tile.LiquidAmount; 200 } 201 202 // wire data 203 if (tile.WireRed) 204 { 205 // red wire = header2 bit[1] 206 header2 = (byte)(header2 | 2); 207 } 208 if (tile.WireBlue) 209 { 210 // blue wire = header2 bit[2] 211 header2 = (byte)(header2 | 4); 212 } 213 if (tile.WireGreen) 214 { 215 // green wire = header2 bit[3] 216 header2 = (byte)(header2 | 8); 217 } 218 219 // brick style 220 byte brickStyle = (byte)((byte)tile.BrickStyle << 4); 221 // set bits[4,5,6] of header2 222 header2 = (byte)(header2 | brickStyle); 223 224 // actuator data 225 if (tile.Actuator) 226 { 227 // set bit[1] of header3 228 header3 = (byte)(header3 | 2); 229 } 230 if (tile.InActive) 231 { 232 // set bit[2] of header3 233 header3 = (byte)(header3 | 4); 234 } 235 if (tile.WireYellow) 236 { 237 header3 = (byte)(header3 | 32); 238 } 239 240 headerIndex = 2; 241 if (header3 != 0) 242 { 243 // set header3 active flag bit[0] of header2 244 header2 = (byte)(header2 | 1); 245 tileData[headerIndex--] = header3; 246 } 247 if (header2 != 0) 248 { 249 // set header2 active flag bit[0] of header1 250 header1 = (byte)(header1 | 1); 251 tileData[headerIndex--] = header2; 252 } 253 254 tileData[headerIndex] = header1; 255 return tileData; 256 } 257 258 public static int SaveChests(IList<Chest> chests, BinaryWriter bw) 259 { 260 bw.Write((Int16)chests.Count); 261 bw.Write((Int16)Chest.MaxItems); 262 263 foreach (Chest chest in chests) 264 { 265 bw.Write(chest.X); 266 bw.Write(chest.Y); 267 bw.Write(chest.Name ?? string.Empty); 268 269 for (int slot = 0; slot < Chest.MaxItems; slot++) 270 { 271 Item item = chest.Items[slot]; 272 if (item != null) 273 { 274 bw.Write((short)item.StackSize); 275 if (item.StackSize > 0) 276 { 277 bw.Write(item.NetId); 278 bw.Write(item.Prefix); 279 } 280 } 281 else 282 { 283 bw.Write((short)0); 284 } 285 } 286 } 287 288 return (int)bw.BaseStream.Position; 289 } 290 291 public static int SaveSigns(IList<Sign> signs, BinaryWriter bw) 292 { 293 bw.Write((Int16)signs.Count); 294 295 foreach (Sign sign in signs) 296 { 297 if (sign.Text != null) 298 { 299 bw.Write(sign.Text); 300 bw.Write(sign.X); 301 bw.Write(sign.Y); 302 } 303 } 304 305 return (int)bw.BaseStream.Position; 306 } 307 308 public static int SaveNPCs(IEnumerable<NPC> npcs, BinaryWriter bw) 309 { 310 foreach (NPC npc in npcs) 311 { 312 bw.Write(true); 313 bw.Write(npc.SpriteId); 314 bw.Write(npc.DisplayName); 315 bw.Write(npc.Position.X); 316 bw.Write(npc.Position.Y); 317 bw.Write(npc.IsHomeless); 318 bw.Write(npc.Home.X); 319 bw.Write(npc.Home.Y); 320 } 321 bw.Write(false); 322 323 return (int)bw.BaseStream.Position; 324 } 325 326 public static int SaveTownManager(IList<TownManager> rooms, BinaryWriter bw) 327 { 328 bw.Write(rooms.Count); 329 foreach (TownManager room in rooms) 330 { 331 bw.Write(room.NpcId); 332 bw.Write(room.Home.X); 333 bw.Write(room.Home.Y); 334 } 335 return (int)bw.BaseStream.Position; 336 } 337 338 public static int SaveMobs(IEnumerable<NPC> mobs, BinaryWriter bw) 339 { 340 foreach (NPC mob in mobs) 341 { 342 bw.Write(true); 343 bw.Write(mob.SpriteId); 344 bw.Write(mob.Position.X); 345 bw.Write(mob.Position.Y); 346 } 347 bw.Write(false); 348 349 return (int)bw.BaseStream.Position; 350 } 351 352 public static int SavePressurePlate(IList<PressurePlate> plates, BinaryWriter bw) 353 { 354 bw.Write(plates.Count); 355 356 foreach (PressurePlate plate in plates) 357 { 358 bw.Write(plate.PosX); 359 bw.Write(plate.PosY); 360 } 361 362 return (int)bw.BaseStream.Position; 363 } 364 365 public static int SaveFooter(World world, BinaryWriter bw) 366 { 367 bw.Write(true); 368 bw.Write(world.Title); 369 bw.Write(world.WorldId); 370 371 return (int)bw.BaseStream.Position; 372 } 373 374 public static int UpdateSectionPointers(int[] sectionPointers, BinaryWriter bw) 375 { 376 bw.BaseStream.Position = 0x18L; 377 bw.Write((short)sectionPointers.Length); 378 379 for (int i = 0; i < sectionPointers.Length; i++) 380 { 381 bw.Write(sectionPointers[i]); 382 } 383 384 return (int)bw.BaseStream.Position; 385 } 386 387 public static int SaveSectionHeader(World world, BinaryWriter bw) 388 { 389 bw.Write(Math.Max(CompatibleVersion, world.Version)); 390 bw.Write((UInt64)0x026369676f6c6572ul); 391 bw.Write((int)world.FileRevision+1); 392 bw.Write(Convert.ToUInt64(world.IsFavorite)); 393 bw.Write(SectionCount); 394 395 // write section pointer placeholders 396 for (int i = 0; i < SectionCount; i++) 397 { 398 bw.Write(0); 399 } 400 401 // write bitpacked tile frame importance 402 WriteBitArray(bw, TileFrameImportant); 403 404 return (int)bw.BaseStream.Position; 405 } 406 407 public static int SaveHeaderFlags(World world, BinaryWriter bw) 408 { 409 bw.Write(world.Title); 410 bw.Write(world.Seed); 411 bw.Write(world.WorldGenVersion); 412 bw.Write(world.Guid.ToByteArray()); 413 bw.Write(world.WorldId); 414 bw.Write((int)world.LeftWorld); 415 bw.Write((int)world.RightWorld); 416 bw.Write((int)world.TopWorld); 417 bw.Write((int)world.BottomWorld); 418 bw.Write(world.TilesHigh); 419 bw.Write(world.TilesWide); 420 bw.Write(world.ExpertMode); 421 bw.Write(world.CreationTime); 422 bw.Write((byte)world.MoonType); 423 bw.Write(world.TreeX0); 424 bw.Write(world.TreeX1); 425 bw.Write(world.TreeX2); 426 bw.Write(world.TreeStyle0); 427 bw.Write(world.TreeStyle1); 428 bw.Write(world.TreeStyle2); 429 bw.Write(world.TreeStyle3); 430 bw.Write(world.CaveBackX0); 431 bw.Write(world.CaveBackX1); 432 bw.Write(world.CaveBackX2); 433 bw.Write(world.CaveBackStyle0); 434 bw.Write(world.CaveBackStyle1); 435 bw.Write(world.CaveBackStyle2); 436 bw.Write(world.CaveBackStyle3); 437 bw.Write(world.IceBackStyle); 438 bw.Write(world.JungleBackStyle); 439 bw.Write(world.HellBackStyle); 440 bw.Write(world.SpawnX); 441 bw.Write(world.SpawnY); 442 bw.Write(world.GroundLevel); 443 bw.Write(world.RockLevel); 444 bw.Write(world.Time); 445 bw.Write(world.DayTime); 446 bw.Write(world.MoonPhase); 447 bw.Write(world.BloodMoon); 448 bw.Write(world.IsEclipse); 449 bw.Write(world.DungeonX); 450 bw.Write(world.DungeonY); 451 bw.Write(world.IsCrimson); 452 bw.Write(world.DownedBoss1); 453 bw.Write(world.DownedBoss2); 454 bw.Write(world.DownedBoss3); 455 bw.Write(world.DownedQueenBee); 456 bw.Write(world.DownedMechBoss1); 457 bw.Write(world.DownedMechBoss2); 458 bw.Write(world.DownedMechBoss3); 459 bw.Write(world.DownedMechBossAny); 460 bw.Write(world.DownedPlantBoss); 461 bw.Write(world.DownedGolemBoss); 462 bw.Write(world.DownedSlimeKingBoss); 463 bw.Write(world.SavedGoblin); 464 bw.Write(world.SavedWizard); 465 bw.Write(world.SavedMech); 466 bw.Write(world.DownedGoblins); 467 bw.Write(world.DownedClown); 468 bw.Write(world.DownedFrost); 469 bw.Write(world.DownedPirates); 470 bw.Write(world.ShadowOrbSmashed); 471 bw.Write(world.SpawnMeteor); 472 bw.Write((byte)world.ShadowOrbCount); 473 bw.Write(world.AltarCount); 474 bw.Write(world.HardMode); 475 bw.Write(world.InvasionDelay); 476 bw.Write(world.InvasionSize); 477 bw.Write(world.InvasionType); 478 bw.Write(world.InvasionX); 479 bw.Write(world.SlimeRainTime); 480 bw.Write((byte)world.SundialCooldown); 481 bw.Write(world.TempRaining); 482 bw.Write(world.TempRainTime); 483 bw.Write(world.TempMaxRain); 484 bw.Write(world.OreTier1); 485 bw.Write(world.OreTier2); 486 bw.Write(world.OreTier3); 487 bw.Write(world.BgTree); 488 bw.Write(world.BgCorruption); 489 bw.Write(world.BgJungle); 490 bw.Write(world.BgSnow); 491 bw.Write(world.BgHallow); 492 bw.Write(world.BgCrimson); 493 bw.Write(world.BgDesert); 494 bw.Write(world.BgOcean); 495 bw.Write((int)world.CloudBgActive); 496 bw.Write(world.NumClouds); 497 bw.Write(world.WindSpeedSet); 498 bw.Write(world.Anglers.Count); 499 foreach (string angler in world.Anglers) 500 { 501 bw.Write(angler); 502 } 503 bw.Write(world.SavedAngler); 504 bw.Write(world.AnglerQuest); 505 bw.Write(world.SavedStylist); 506 bw.Write(world.SavedTaxCollector); 507 bw.Write(world.InvasionSizeStart); 508 bw.Write(world.CultistDelay); 509 bw.Write((Int16)world.NumberOfMobs); 510 foreach (int count in world.KilledMobs) 511 { 512 bw.Write(count); 513 } 514 bw.Write(world.FastForwardTime); 515 bw.Write(world.DownedFishron); 516 bw.Write(world.DownedMartians); 517 bw.Write(world.DownedLunaticCultist); 518 bw.Write(world.DownedMoonlord); 519 bw.Write(world.DownedHalloweenKing); 520 bw.Write(world.DownedHalloweenTree); 521 bw.Write(world.DownedChristmasQueen); 522 bw.Write(world.DownedSanta); 523 bw.Write(world.DownedChristmasTree); 524 bw.Write(world.DownedCelestialSolar); 525 bw.Write(world.DownedCelestialVortex); 526 bw.Write(world.DownedCelestialNebula); 527 bw.Write(world.DownedCelestialStardust); 528 bw.Write(world.CelestialSolarActive); 529 bw.Write(world.CelestialVortexActive); 530 bw.Write(world.CelestialNebulaActive); 531 bw.Write(world.CelestialStardustActive); 532 bw.Write(world.Apocalypse); 533 bw.Write(world.PartyManual); 534 bw.Write(world.PartyGenuine); 535 bw.Write(world.PartyCooldown); 536 bw.Write(world.PartyingNPCs.Count); 537 foreach (int partier in world.PartyingNPCs) 538 { 539 bw.Write(partier); 540 } 541 542 bw.Write(world.SandStormHappening); 543 bw.Write(world.SandStormTimeLeft); 544 bw.Write(world.SandStormSeverity); 545 bw.Write(world.SandStormIntendedSeverity); 546 bw.Write(world.SavedBartender); 547 bw.Write(world.DownedDD2InvasionT1); 548 bw.Write(world.DownedDD2InvasionT2); 549 bw.Write(world.DownedDD2InvasionT3); 550 551 if (world.UnknownData != null && world.UnknownData.Length > 0) 552 bw.Write(world.UnknownData); 553 554 return (int)bw.BaseStream.Position; 555 } 556 557 public static int SaveTileEntities(World w, BinaryWriter bw) 558 { 559 bw.Write(w.TileEntities.Count); 560 561 foreach(TileEntity tentity in w.TileEntities) 562 { 563 bw.Write(tentity.Type); 564 bw.Write(tentity.Id); 565 bw.Write(tentity.PosX); 566 bw.Write(tentity.PosY); 567 switch (tentity.Type) 568 { 569 case 0: //it is a dummy 570 bw.Write(tentity.Npc); 571 break; 572 case 1: //it is a item frame 573 bw.Write((Int16)tentity.NetId); 574 bw.Write(tentity.Prefix); 575 bw.Write(tentity.StackSize); 576 break; 577 case 2: //it is a logic sensor 578 bw.Write(tentity.LogicCheck); 579 bw.Write(tentity.On); 580 break; 581 } 582 } 583 584 return (int)bw.BaseStream.Position; 585 } 586 587 public static void LoadV2(BinaryReader b, string filename, World w) 588 { 589 //throw new NotImplementedException("World Version > 87"); 590 591 bool[] tileFrameImportant; 592 int[] sectionPointers; 593 594 // reset the stream 595 b.BaseStream.Position = (long)0; 596 597 OnProgressChanged(null, new ProgressChangedEventArgs(0, "Loading File Header...")); 598 // read section pointers and tile frame data 599 if (!LoadSectionHeader(b, out tileFrameImportant, out sectionPointers, w)) 600 throw new FileFormatException("Invalid File Format Section"); 601 602 TileFrameImportant = tileFrameImportant; 603 604 // we should be at the end of the first section 605 if (b.BaseStream.Position != sectionPointers[0]) 606 throw new FileFormatException("Unexpected Position: Invalid File Format Section"); 607 608 // Load the flags 609 LoadHeaderFlags(b, w, sectionPointers[1]); 610 if (b.BaseStream.Position != sectionPointers[1]) 611 throw new FileFormatException("Unexpected Position: Invalid Header Flags"); 612 613 OnProgressChanged(null, new ProgressChangedEventArgs(0, "Loading UndoTiles...")); 614 w.Tiles = LoadTileData(b, w.TilesWide, w.TilesHigh); 615 if (b.BaseStream.Position != sectionPointers[2]) 616 throw new FileFormatException("Unexpected Position: Invalid Tile Data"); 617 618 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading Chests...")); 619 620 foreach (Chest chest in LoadChestData(b)) 621 { 622 //Tile tile = w.Tiles[chest.X, chest.Y]; 623 //if (tile.IsActive && (tile.Type == 55 || tile.Type == 85)) 624 { 625 w.Chests.Add(chest); 626 } 627 } 628 629 if (b.BaseStream.Position != sectionPointers[3]) 630 throw new FileFormatException("Unexpected Position: Invalid Chest Data"); 631 632 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading Signs...")); 633 634 foreach (Sign sign in LoadSignData(b)) 635 { 636 Tile tile = w.Tiles[sign.X, sign.Y]; 637 if (tile.IsActive && Tile.IsSign(tile.Type)) 638 { 639 w.Signs.Add(sign); 640 } 641 } 642 643 if (b.BaseStream.Position != sectionPointers[4]) 644 throw new FileFormatException("Unexpected Position: Invalid Sign Data"); 645 646 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading NPCs...")); 647 LoadNPCsData(b, w); 648 if(w.Version >= 140) 649 { 650 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading Mobs...")); 651 LoadMobsData(b, w); 652 if (b.BaseStream.Position != sectionPointers[5]) 653 throw new FileFormatException("Unexpected Position: Invalid Mob and NPC Data"); 654 655 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Loading Tile Entities Section...")); 656 LoadTileEntities(b, w); 657 if (b.BaseStream.Position != sectionPointers[6]) 658 throw new FileFormatException("Unexpected Position: Invalid Tile Entities Section"); 659 } 660 else 661 { 662 if (b.BaseStream.Position != sectionPointers[5]) 663 throw new FileFormatException("Unexpected Position: Invalid NPC Data"); 664 } 665 if(w.Version >= 170) 666 { 667 LoadPressurePlate(b, w); 668 if (b.BaseStream.Position != sectionPointers[7]) 669 throw new FileFormatException("Unexpected Position: Invalid Weighted Pressure Plate Section"); 670 } 671 if(w.Version >= 189) 672 { 673 LoadTownManager(b, w); 674 if (b.BaseStream.Position != sectionPointers[8]) 675 throw new FileFormatException("Unexpected Position: Invalid Town Manager Section"); 676 } 677 678 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Verifying File...")); 679 LoadFooter(b, w); 680 681 OnProgressChanged(null, new ProgressChangedEventArgs(100, "Load Complete.")); 682 } 683 684 public static Tile[,] LoadTileData(BinaryReader r, int maxX, int maxY) 685 { 686 var tiles = new Tile[maxX, maxY]; 687 688 int rle; 689 for (int x = 0; x < maxX; x++) 690 { 691 OnProgressChanged(null, 692 new ProgressChangedEventArgs(x.ProgressPercentage(maxX), "Loading UndoTiles...")); 693 694 for (int y = 0; y < maxY; y++) 695 { 696 Tile tile = DeserializeTileData(r, out rle); 697 698 tiles[x, y] = tile; 699 while (rle > 0) 700 { 701 y++; 702 703 if (y > maxY) 704 throw new FileFormatException( 705 $"Invalid Tile Data: RLE Compression outside of bounds [{x},{y}]"); 706 707 tiles[x, y] = (Tile)tile.Clone(); 708 rle--; 709 } 710 } 711 } 712 713 return tiles; 714 } 715 716 public static Tile DeserializeTileData(BinaryReader r, out int rle) 717 { 718 Tile tile = new Tile(); 719 720 rle = 0; 721 int tileType = -1; 722 // byte header4 = 0; // unused, future proofing 723 byte header3 = 0; 724 byte header2 = 0; 725 byte header1 = r.ReadByte(); 726 727 // check bit[0] to see if header2 has data 728 if ((header1 & 1) == 1) 729 { 730 header2 = r.ReadByte(); 731 732 // check bit[0] to see if header3 has data 733 if ((header2 & 1) == 1) 734 { 735 header3 = r.ReadByte(); 736 737 // this doesn't exist yet 738 // if ((header3 & 1) == 1) 739 // { 740 // header4 = r.ReadByte(); 741 // } 742 } 743 } 744 745 // check bit[1] for active tile 746 if ((header1 & 2) == 2) 747 { 748 tile.IsActive = true; 749 750 // read tile type 751 752 if ((header1 & 32) != 32) // check bit[5] to see if tile is byte or little endian int16 753 { 754 // tile is byte 755 tileType = r.ReadByte(); 756 } 757 else 758 { 759 // tile is little endian int16 760 byte lowerByte = r.ReadByte(); 761 tileType = r.ReadByte(); 762 tileType = tileType << 8 | lowerByte; 763 } 764 tile.Type = (ushort)tileType; // convert type to ushort after bit operations 765 766 // read frame UV coords 767 if (!TileFrameImportant[tileType]) 768 { 769 tile.U = -1; 770 tile.V = -1; 771 } 772 else 773 { 774 // read UV coords 775 tile.U = r.ReadInt16(); 776 tile.V = r.ReadInt16(); 777 778 // reset timers 779 if (tile.Type == (int)TileType.Timer) 780 { 781 tile.V = 0; 782 } 783 } 784 785 // check header3 bit[3] for tile color 786 if ((header3 & 8) == 8) 787 { 788 tile.TileColor = r.ReadByte(); 789 } 790 } 791 792 // Read Walls 793 if ((header1 & 4) == 4) // check bit[3] bit for active wall 794 { 795 tile.Wall = r.ReadByte(); 796 797 // check bit[4] of header3 to see if there is a wall color 798 if ((header3 & 16) == 16) 799 { 800 tile.WallColor = r.ReadByte(); 801 } 802 } 803 804 // check for liquids, grab the bit[3] and bit[4], shift them to the 0 and 1 bits 805 byte liquidType = (byte)((header1 & 24) >> 3); 806 if (liquidType != 0) 807 { 808 tile.LiquidAmount = r.ReadByte(); 809 tile.LiquidType = (LiquidType)liquidType; 810 } 811 812 // check if we have data in header2 other than just telling us we have header3 813 if (header2 > 1) 814 { 815 // check bit[1] for red wire 816 if ((header2 & 2) == 2) 817 { 818 tile.WireRed = true; 819 } 820 // check bit[2] for blue wire 821 if ((header2 & 4) == 4) 822 { 823 tile.WireBlue = true; 824 } 825 // check bit[3] for green wire 826 if ((header2 & 8) == 8) 827 { 828 tile.WireGreen = true; 829 } 830 831 // grab bits[4, 5, 6] and shift 4 places to 0,1,2. This byte is our brick style 832 byte brickStyle = (byte)((header2 & 112) >> 4); 833 if (brickStyle != 0 && TileProperties.Count > tile.Type && TileProperties[tile.Type].IsSolid) 834 { 835 tile.BrickStyle = (BrickStyle)brickStyle; 836 } 837 } 838 839 // check if we have data in header3 to process 840 if (header3 > 0) 841 { 842 // check bit[1] for actuator 843 if ((header3 & 2) == 2) 844 { 845 tile.Actuator = true; 846 } 847 848 // check bit[2] for inactive due to actuator 849 if ((header3 & 4) == 4) 850 { 851 tile.InActive = true; 852 } 853 854 if ((header3 & 32) == 32) 855 { 856 tile.WireYellow = true; 857 } 858 } 859 860 // get bit[6,7] shift to 0,1 for RLE encoding type 861 // 0 = no RLE compression 862 // 1 = byte RLE counter 863 // 2 = int16 RLE counter 864 // 3 = ERROR 865 byte rleStorageType = (byte)((header1 & 192) >> 6); 866 867 // read RLE distance 868 if (rleStorageType == 0) 869 { 870 rle = 0; 871 } 872 else if (rleStorageType != 1) 873 { 874 rle = r.ReadInt16(); 875 } 876 else 877 { 878 rle = r.ReadByte(); 879 } 880 881 return tile; 882 } 883 884 public static IEnumerable<Chest> LoadChestData(BinaryReader r) 885 { 886 int totalChests = r.ReadInt16(); 887 int maxItems = r.ReadInt16(); 888 889 // overflow item check? 890 int itemsPerChest; 891 int overflowItems; 892 if (maxItems > Chest.MaxItems) 893 { 894 itemsPerChest = Chest.MaxItems; 895 overflowItems = maxItems - Chest.MaxItems; 896 } 897 else 898 { 899 itemsPerChest = maxItems; 900 overflowItems = 0; 901 } 902 903 904 // read chests 905 for (int i = 0; i < totalChests; i++) 906 { 907 var chest = new Chest 908 { 909 X = r.ReadInt32(), 910 Y = r.ReadInt32(), 911 Name = r.ReadString() 912 }; 913 914 // read items in chest 915 for (int slot = 0; slot < itemsPerChest; slot++) 916 { 917 var stackSize = r.ReadInt16(); 918 chest.Items[slot].StackSize = stackSize; 919 920 if (stackSize > 0) 921 { 922 int id = r.ReadInt32(); 923 byte prefix = r.ReadByte(); 924 925 chest.Items[slot].NetId = id; 926 chest.Items[slot].StackSize = stackSize; 927 chest.Items[slot].Prefix = prefix; 928 929 } 930 } 931 932 // dump overflow items 933 for (int overflow = 0; overflow < overflowItems; overflow++) 934 { 935 var stackSize = r.ReadInt16(); 936 if (stackSize > 0) 937 { 938 r.ReadInt32(); 939 r.ReadByte(); 940 } 941 } 942 943 yield return chest; 944 } 945 946 } 947 948 public static IEnumerable<Sign> LoadSignData(BinaryReader r) 949 { 950 short totalSigns = r.ReadInt16(); 951 952 for (int i = 0; i < totalSigns; i++) 953 { 954 string text = r.ReadString(); 955 int x = r.ReadInt32(); 956 int y = r.ReadInt32(); 957 yield return new Sign(x, y, text); 958 } 959 } 960 961 public static void LoadNPCsData(BinaryReader r, World w) 962 { 963 int totalNpcs = 0; 964 for (bool i = r.ReadBoolean(); i; i = r.ReadBoolean()) 965 { 966 NPC npc = new NPC(); 967 if (w.Version >= 190) 968 { 969 npc.SpriteId = r.ReadInt32(); 970 if (NpcNames.ContainsKey(npc.SpriteId)) 971 npc.Name = NpcNames[npc.SpriteId]; 972 } 973 else 974 { 975 npc.Name = r.ReadString(); 976 if (NpcIds.ContainsKey(npc.Name)) 977 npc.SpriteId = NpcIds[npc.Name]; 978 } 979 npc.DisplayName = r.ReadString(); 980 npc.Position = new Vector2(r.ReadSingle(), r.ReadSingle()); 981 npc.IsHomeless = r.ReadBoolean(); 982 npc.Home = new Vector2Int32(r.ReadInt32(), r.ReadInt32()); 983 984 985 w.NPCs.Add(npc); 986 totalNpcs++; 987 } 988 } 989 990 public static void LoadMobsData(BinaryReader r, World w) 991 { 992 int totalMobs = 0; 993 bool flag = r.ReadBoolean(); 994 while (flag) 995 { 996 NPC npc = new NPC(); 997 if (w.Version >= 190) 998 { 999 npc.SpriteId = r.ReadInt32(); 1000 } 1001 else 1002 { 1003 npc.Name = r.ReadString(); 1004 if (NpcIds.ContainsKey(npc.Name)) 1005 npc.SpriteId = NpcIds[npc.Name]; 1006 } 1007 npc.Position = new Vector2(r.ReadSingle(), r.ReadSingle()); 1008 w.Mobs.Add(npc); 1009 totalMobs++; 1010 flag = r.ReadBoolean(); 1011 } 1012 } 1013 1014 public static void LoadTownManager(BinaryReader r, World w) 1015 { 1016 int totalRooms = r.ReadInt32(); 1017 for (int i = 0; i < totalRooms; i++) 1018 { 1019 TownManager room = new TownManager(); 1020 room.NpcId = r.ReadInt32(); 1021 room.Home = new Vector2Int32(r.ReadInt32(), r.ReadInt32()); 1022 w.PlayerRooms.Add(room); 1023 } 1024 } 1025 1026 public static void LoadFooter(BinaryReader r, World w) 1027 { 1028 if (!r.ReadBoolean()) 1029 throw new FileFormatException("Invalid Footer"); 1030 1031 if (r.ReadString() != w.Title) 1032 throw new FileFormatException("Invalid Footer"); 1033 1034 if (r.ReadInt32() != w.WorldId) 1035 throw new FileFormatException("Invalid Footer"); 1036 } 1037 1038 public static void LoadTileEntities(BinaryReader r, World w) 1039 { 1040 w.TileEntitiesNumber = r.ReadInt32(); 1041 1042 for (int counter = 0; counter < w.TileEntitiesNumber; counter++ ) 1043 { 1044 TileEntity entity = new TileEntity(); 1045 entity.Type = r.ReadByte(); 1046 entity.Id = r.ReadInt32(); 1047 entity.PosX = r.ReadInt16(); 1048 entity.PosY = r.ReadInt16(); 1049 switch (entity.Type) 1050 { 1051 case 0: //it is a dummy 1052 entity.Npc = r.ReadInt16(); 1053 break; 1054 case 1: //it is a item frame 1055 entity.NetId = (int)r.ReadInt16(); 1056 entity.Prefix = r.ReadByte(); 1057 entity.StackSize = r.ReadInt16(); 1058 break; 1059 case 2: //it is a logic sensor 1060 entity.LogicCheck = r.ReadByte(); 1061 entity.On = r.ReadBoolean(); 1062 break; 1063 } 1064 w.TileEntities.Add(entity); 1065 } 1066 } 1067 public static void LoadPressurePlate(BinaryReader r, World w) 1068 { 1069 int count = r.ReadInt32(); 1070 1071 for (int counter = 0; counter < count; counter++ ) 1072 { 1073 PressurePlate plates = new PressurePlate(); 1074 plates.PosX = r.ReadInt32(); 1075 plates.PosY = r.ReadInt32(); 1076 w.PressurePlates.Add(plates); 1077 } 1078 } 1079 1080 public static void LoadHeaderFlags(BinaryReader r, World w, int expectedPosition) 1081 { 1082 w.Title = r.ReadString(); 1083 if (w.Version >= 179) 1084 { 1085 if (w.Version == 179) 1086 w.Seed = r.ReadInt32().ToString(); 1087 else 1088 w.Seed = r.ReadString(); 1089 w.WorldGenVersion = r.ReadUInt64(); 1090 } 1091 else 1092 w.Seed = ""; 1093 if (w.Version >= 181) 1094 { 1095 w.Guid = new Guid(r.ReadBytes(16)); 1096 } 1097 else 1098 w.Guid = Guid.NewGuid(); 1099 w.WorldId = r.ReadInt32(); 1100 w.LeftWorld = (float)r.ReadInt32(); 1101 w.RightWorld = (float)r.ReadInt32(); 1102 w.TopWorld = (float)r.ReadInt32(); 1103 w.BottomWorld = (float)r.ReadInt32(); 1104 w.TilesHigh = r.ReadInt32(); 1105 w.TilesWide = r.ReadInt32(); 1106 1107 if (w.Version >= 147) 1108 { 1109 w.ExpertMode = r.ReadBoolean(); 1110 w.CreationTime = r.ReadInt64(); 1111 } 1112 else 1113 { 1114 w.CreationTime = DateTime.Now.ToBinary(); 1115 } 1116 1117 w.MoonType = r.ReadByte(); 1118 w.TreeX[0] = r.ReadInt32(); 1119 w.TreeX[1] = r.ReadInt32(); 1120 w.TreeX[2] = r.ReadInt32(); 1121 w.TreeX2 = w.TreeX[2]; 1122 w.TreeX1 = w.TreeX[1]; 1123 w.TreeX0 = w.TreeX[0]; 1124 w.TreeStyle0 = r.ReadInt32(); 1125 w.TreeStyle1 = r.ReadInt32(); 1126 w.TreeStyle2 = r.ReadInt32(); 1127 w.TreeStyle3 = r.ReadInt32(); 1128 w.CaveBackX[0] = r.ReadInt32(); 1129 w.CaveBackX[1] = r.ReadInt32(); 1130 w.CaveBackX[2] = r.ReadInt32(); 1131 w.CaveBackX2 = w.CaveBackX[2]; 1132 w.CaveBackX1 = w.CaveBackX[1]; 1133 w.CaveBackX0 = w.CaveBackX[0]; 1134 w.CaveBackStyle0 = r.ReadInt32(); 1135 w.CaveBackStyle1 = r.ReadInt32(); 1136 w.CaveBackStyle2 = r.ReadInt32(); 1137 w.CaveBackStyle3 = r.ReadInt32(); 1138 w.IceBackStyle = r.ReadInt32(); 1139 w.JungleBackStyle = r.ReadInt32(); 1140 w.HellBackStyle = r.ReadInt32(); 1141 1142 w.SpawnX = r.ReadInt32(); 1143 w.SpawnY = r.ReadInt32(); 1144 w.GroundLevel = r.ReadDouble(); 1145 w.RockLevel = r.ReadDouble(); 1146 w.Time = r.ReadDouble(); 1147 w.DayTime = r.ReadBoolean(); 1148 w.MoonPhase = r.ReadInt32(); 1149 w.BloodMoon = r.ReadBoolean(); 1150 w.IsEclipse = r.ReadBoolean(); 1151 w.DungeonX = r.ReadInt32(); 1152 w.DungeonY = r.ReadInt32(); 1153 1154 w.IsCrimson = r.ReadBoolean(); 1155 1156 w.DownedBoss1 = r.ReadBoolean(); 1157 w.DownedBoss2 = r.ReadBoolean(); 1158 w.DownedBoss3 = r.ReadBoolean(); 1159 w.DownedQueenBee = r.ReadBoolean(); 1160 w.DownedMechBoss1 = r.ReadBoolean(); 1161 w.DownedMechBoss2 = r.ReadBoolean(); 1162 w.DownedMechBoss3 = r.ReadBoolean(); 1163 w.DownedMechBossAny = r.ReadBoolean(); 1164 w.DownedPlantBoss = r.ReadBoolean(); 1165 w.DownedGolemBoss = r.ReadBoolean(); 1166 if (w.Version >= 147) w.DownedSlimeKingBoss = r.ReadBoolean(); 1167 w.SavedGoblin = r.ReadBoolean(); 1168 w.SavedWizard = r.ReadBoolean(); 1169 w.SavedMech = r.ReadBoolean(); 1170 w.DownedGoblins = r.ReadBoolean(); 1171 w.DownedClown = r.ReadBoolean(); 1172 w.DownedFrost = r.ReadBoolean(); 1173 w.DownedPirates = r.ReadBoolean(); 1174 1175 w.ShadowOrbSmashed = r.ReadBoolean(); 1176 w.SpawnMeteor = r.ReadBoolean(); 1177 w.ShadowOrbCount = (int)r.ReadByte(); 1178 w.AltarCount = r.ReadInt32(); 1179 w.HardMode = r.ReadBoolean(); 1180 w.InvasionDelay = r.ReadInt32(); 1181 w.InvasionSize = r.ReadInt32(); 1182 w.InvasionType = r.ReadInt32(); 1183 w.InvasionX = r.ReadDouble(); 1184 1185 if (w.Version >= 147) 1186 { 1187 w.SlimeRainTime = r.ReadDouble(); 1188 w.SundialCooldown = r.ReadByte(); 1189 } 1190 1191 w.TempRaining = r.ReadBoolean(); 1192 w.TempRainTime = r.ReadInt32(); 1193 w.TempMaxRain = r.ReadSingle(); 1194 w.OreTier1 = r.ReadInt32(); 1195 w.OreTier2 = r.ReadInt32(); 1196 w.OreTier3 = r.ReadInt32(); 1197 w.BgTree = r.ReadByte(); 1198 w.BgCorruption = r.ReadByte(); 1199 w.BgJungle = r.ReadByte(); 1200 w.BgSnow = r.ReadByte(); 1201 w.BgHallow = r.ReadByte(); 1202 w.BgCrimson = r.ReadByte(); 1203 w.BgDesert = r.ReadByte(); 1204 w.BgOcean = r.ReadByte(); 1205 w.CloudBgActive = (float)r.ReadInt32(); 1206 w.NumClouds = r.ReadInt16(); 1207 w.WindSpeedSet = r.ReadSingle(); 1208 1209 if (w.Version >= 95) 1210 { 1211 for (int i = r.ReadInt32(); i > 0; i--) 1212 { 1213 w.Anglers.Add(r.ReadString()); 1214 } 1215 } 1216 1217 if (w.Version >= 99) 1218 { 1219 w.SavedAngler = r.ReadBoolean(); 1220 } 1221 1222 if (w.Version >= 101) 1223 { 1224 w.AnglerQuest = r.ReadInt32(); 1225 } 1226 1227 if (w.Version >= 104) 1228 { 1229 w.SavedStylist = r.ReadBoolean(); 1230 } 1231 1232 if (w.Version >= 140) 1233 { 1234 w.SavedTaxCollector = r.ReadBoolean(); 1235 w.InvasionSizeStart = r.ReadInt32(); 1236 w.CultistDelay = r.ReadInt32(); 1237 int numberOfMobs = r.ReadInt16(); 1238 w.NumberOfMobs = numberOfMobs; 1239 for (int counter = 0; counter < numberOfMobs; counter++) 1240 { 1241 w.KilledMobs.Add(r.ReadInt32()); 1242 } 1243 w.FastForwardTime = r.ReadBoolean(); 1244 w.DownedFishron = r.ReadBoolean(); 1245 w.DownedMartians = r.ReadBoolean(); 1246 w.DownedLunaticCultist = r.ReadBoolean(); 1247 w.DownedMoonlord = r.ReadBoolean(); 1248 w.DownedHalloweenKing = r.ReadBoolean(); 1249 w.DownedHalloweenTree = r.ReadBoolean(); 1250 w.DownedChristmasQueen = r.ReadBoolean(); 1251 w.DownedSanta = r.ReadBoolean(); 1252 w.DownedChristmasTree = r.ReadBoolean(); 1253 w.DownedCelestialSolar = r.ReadBoolean(); 1254 w.DownedCelestialVortex = r.ReadBoolean(); 1255 w.DownedCelestialNebula = r.ReadBoolean(); 1256 w.DownedCelestialStardust = r.ReadBoolean(); 1257 w.CelestialSolarActive = r.ReadBoolean(); 1258 w.CelestialVortexActive = r.ReadBoolean(); 1259 w.CelestialNebulaActive = r.ReadBoolean(); 1260 w.CelestialStardustActive = r.ReadBoolean(); 1261 w.Apocalypse = r.ReadBoolean(); 1262 } 1263 if (w.Version >= 170) 1264 { 1265 w.PartyManual = r.ReadBoolean(); 1266 w.PartyGenuine = r.ReadBoolean(); 1267 w.PartyCooldown = r.ReadInt32(); 1268 int numparty = r.ReadInt32(); 1269 for (int counter = 0; counter < numparty; counter++) 1270 { 1271 w.PartyingNPCs.Add(r.ReadInt32()); 1272 } 1273 } 1274 if (w.Version >= 174) 1275 { 1276 w.SandStormHappening = r.ReadBoolean(); 1277 w.SandStormTimeLeft = r.ReadInt32(); 1278 w.SandStormSeverity = r.ReadSingle(); 1279 w.SandStormIntendedSeverity = r.ReadSingle(); 1280 } 1281 if (w.Version >= 178) 1282 { 1283 w.SavedBartender = r.ReadBoolean(); 1284 w.DownedDD2InvasionT1 = r.ReadBoolean(); 1285 w.DownedDD2InvasionT2 = r.ReadBoolean(); 1286 w.DownedDD2InvasionT3 = r.ReadBoolean(); 1287 } 1288 1289 // a little future proofing, read any "unknown" flags from the end of the list and save them. We will write these back after we write our "known" flags. 1290 if (r.BaseStream.Position < expectedPosition) 1291 { 1292 w.UnknownData = r.ReadBytes(expectedPosition - (int)r.BaseStream.Position); 1293 } 1294 } 1295 1296 public static bool LoadSectionHeader(BinaryReader r, out bool[] tileFrameImportant, out int[] sectionPointers, World w) 1297 { 1298 tileFrameImportant = null; 1299 sectionPointers = null; 1300 int versionNumber = r.ReadInt32(); 1301 if(versionNumber > 140) 1302 { 1303 UInt64 versionTypecheck = r.ReadUInt64(); 1304 if (versionTypecheck != 0x026369676f6c6572ul ) 1305 throw new FileFormatException("Invalid Header"); 1306 1307 w.FileRevision = r.ReadUInt32(); 1308 UInt64 temp = r.ReadUInt64();//I have no idea what this is for... 1309 w.IsFavorite = ((temp & 1uL) == 1uL); 1310 } 1311 1312 // read file section stream positions 1313 short fileSectionCount = r.ReadInt16(); 1314 sectionPointers = new int[fileSectionCount]; 1315 for (int i = 0; i < fileSectionCount; i++) 1316 { 1317 sectionPointers[i] = r.ReadInt32(); 1318 } 1319 1320 // Read tile frame importance from bit-packed data 1321 tileFrameImportant = ReadBitArray(r); 1322 1323 return true; 1324 } 1325 1326 /// <summary> 1327 /// Read an array of booleans from a bit-packed array. 1328 /// </summary> 1329 /// <param name="reader">BinaryReader at start of bit array.</param> 1330 /// <returns>Array of booleans</returns> 1331 public static bool[] ReadBitArray(BinaryReader reader) 1332 { 1333 // get the number of bits 1334 int length = reader.ReadInt16(); 1335 1336 // read the bit data 1337 var booleans = new bool[length]; 1338 byte data = 0; 1339 byte bitMask = 128; 1340 for (int i = 0; i < length; i++) 1341 { 1342 // If we read the last bit mask (B1000000 = 0x80 = 128), read the next byte from the stream and start the mask over. 1343 // Otherwise, keep incrementing the mask to get the next bit. 1344 if (bitMask != 128) 1345 { 1346 bitMask = (byte)(bitMask << 1); 1347 } 1348 else 1349 { 1350 data = reader.ReadByte(); 1351 bitMask = 1; 1352 } 1353 1354 // Check the mask, if it is set then set the current boolean to true 1355 if ((data & bitMask) == bitMask) 1356 { 1357 booleans[i] = true; 1358 } 1359 } 1360 1361 return booleans; 1362 } 1363 1364 /// <summary> 1365 /// Write an array of booleans to a binary stream as a bit-packed array. 1366 /// </summary> 1367 /// <param name="writer">BinaryWriter stream.</param> 1368 /// <param name="values">Collection of booleans to write as a bit-packed array.</param> 1369 public static void WriteBitArray(BinaryWriter writer, bool[] values) 1370 { 1371 // write the number of bits 1372 writer.Write((Int16)values.Length); 1373 1374 // write the bit data 1375 byte data = 0; 1376 byte bitMask = 1; 1377 for (int i = 0; i < values.Length; i++) 1378 { 1379 // Check if the current value is true, if it is set then set the bit for the current mask in the data byte. 1380 if (values[i]) 1381 { 1382 data = (byte)(data | bitMask); 1383 } 1384 1385 // If we wrote the last bit mask (B1000000 = 0x80 = 128), write the data byte to the stream and start the mask over. 1386 // Otherwise, keep incrementing the mask to write the next bit. 1387 if (bitMask != 128) 1388 { 1389 bitMask = (byte)(bitMask << 1); 1390 } 1391 else 1392 { 1393 writer.Write(data); 1394 data = 0; 1395 bitMask = 1; 1396 } 1397 } 1398 1399 // Write any remaning data in the buffer. 1400 if (bitMask != 1) 1401 { 1402 writer.Write(data); 1403 } 1404 } 1405 1406 1407 } 1408 }
3.HEX分析示例
下载TEdit源码:
作者发布、转载的任何文章中所涉及的技术、思路、工具仅供以安全目的的学习交流,并严格遵守《中华人民共和国网络安全法》、《中华人民共和国数据安全法》等网络安全法律法规。
任何人不得将技术用于非法用途、盈利用途。否则作者不对未许可的用途承担任何后果。
本文遵守CC BY-NC-SA 3.0协议,您可以在任何媒介以任何形式复制、发行本作品,或者修改、转换或以本作品为基础进行创作
您必须给出适当的署名,提供指向本文的链接,同时标明是否(对原文)作了修改。您可以用任何合理的方式来署名,但是不得以任何方式暗示作者为您或您的使用背书。
同时,本文不得用于商业目的。混合、转换、基于本作品进行创作,必须基于同一协议(CC BY-NC-SA 3.0)分发。
如有问题, 可发送邮件咨询.