Terraria文件结构详解

一、Terraria文件结构树

说明:

    1. {}内为变量,其他固定的字符为一般常量,即在一般情况下不会变动
    2. 本树为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 }
World.FileV1.cs

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 }
World.FileV2.cs

猜你喜欢

转载自www.cnblogs.com/holittech/p/12216092.html