📄 datafile.cs
字号:
/// <param name="isNew">是否新建</param>
/// <param name="level">要保存的关数</param>
public void SaveDesign(bool isNew, int level)
{
if (isNew && level != maxLevel) throw new Exception("新建的关必须在最后一关之后");
bw.BaseStream.Seek(addrs[level], SeekOrigin.Begin);
WriteLevel(level, string.Empty); // 如果不是新建,则关尺寸不能比原来的大
if (isNew)
{
Fcl.Resize(ref addrs, addrs.Length + 1);
addrs[++maxLevel] = (int)bw.BaseStream.Position;
WriteAddrs();
}
DeleteStepsFile(level); // 删除通关步骤文件
}
/// <summary>
/// 删除最后一关
/// </summary>
/// <param name="level">关数(必须是最后一关)</param>
public void DeleteLastLevel(int level)
{
if (level != maxLevel - 1) throw new Exception("要删除的关必须是最后一关");
DeleteLevel(level);
DeleteStepsFile(level); // 删除通关步骤文件,如果被删除的关不是最后一关,以后各关的通关步骤文件就不对了
}
/// <summary>
/// 删除指定的关
/// </summary>
/// <param name="level">关数</param>
void DeleteLevel(int level)
{
bw.Seek(addrs[maxLevel] + 4 * level, SeekOrigin.Begin); // 要删除的关起始地址位置
for (int i = level + 1; i < maxLevel; i++) bw.Write(addrs[i]); // 之后的关起始地址前移
bw.BaseStream.SetLength(bw.BaseStream.Position); // 关起始地址列表位于数据文件最后
bw.Seek(24, SeekOrigin.Begin); // 总关数
bw.Write(--maxLevel);
}
/// <summary>
/// 更新各关起始地址列表及总关数和第1关起始地址位置
/// </summary>
private void WriteAddrs()
{
bw.Seek(addrs[maxLevel], SeekOrigin.Begin);
for (int i = 0; i < maxLevel; i++) bw.Write(addrs[i]); // 各关起始地址
bw.Seek(24, SeekOrigin.Begin);
bw.Write(maxLevel); // 总关数
bw.Write(addrs[maxLevel]); // 第1关起始地址位置
}
/// <summary>
/// 更新组名
/// </summary>
void WriteGroupName()
{
byte[] bs = new byte[16];
byte[] bn = Pub.Encode.GetBytes(groupName);
for (int i = 0; i < bs.Length && i < bn.Length; i++) bs[i] = bn[i];
for (int i = bn.Length; i < bs.Length; i++) bs[i] = 32;
bw.Seek(8, SeekOrigin.Begin);
bw.Write(bs); // 组名
}
/// <summary>
/// 写关数据和通关步骤
/// 注意:调用本函数前必须定位到数据文件的正确位置
/// </summary>
/// <param name="level">关数</param>
/// <param name="steps">通关步骤</param>
/// <returns>本关的统计信息</returns>
string WriteLevel(int level, string steps)
{
bw.Write(LevelFlag); // 关标志
bw.Write((byte)(string.IsNullOrEmpty(steps) ? 0 : 1)); // 标志:是否已通关
bw.Write(steps.Length); // 总步数
bw.Write(GetPushSteps(steps)); // 推箱子步数
bw.Write(new byte[14]); // 保留
bw.Write(levelSize.Width); // 当前关宽度
bw.Write(levelSize.Height); // 当前关高度
mans = slots = boxs = 0;
int lands = 0, walls = 0, bricks = 0;
for (int i = 1; i <= levelSize.Height; i++)
{
for (int j = 1; j <= levelSize.Width; j++)
{
bw.Write(map[i, j]);
switch (map[i, j])
{
case Block.Land: lands++; break;
case Block.Slot: slots++; break;
case Block.Wall: walls++; break;
case Block.Brick: bricks++; break;
case Block.Box0: lands++; boxs++; break;
case Block.Box1: slots++; boxs++; break;
case Block.Man0: lands++; mans++; break;
case Block.Man1: slots++; mans++; break;
}
}
}
if (mans != 1) ErrorExit(true, level + 1, "必须刚好有一个工人");
if (!string.IsNullOrEmpty(steps)) SaveLevelSteps(level, steps);
return string.Format("{1}: {2} {3} {4} {5} {6} {7} {8}{0}",
Fcl.NewLine, level + 1, Pub.ToString(levelSize), walls, bricks, lands, slots, boxs, steps.Length);
}
/// <summary>
/// 根据通关步骤给出推箱子步数
/// </summary>
/// <param name="steps">通关步骤</param>
/// <returns>推箱子步数</returns>
int GetPushSteps(string steps)
{
int n = 0;
foreach (char c in steps) if (((Step)c).IsBox) n++;
return n;
}
/// <summary>
/// 数据导入
/// </summary>
/// <param name="name">数据文件主名</param>
/// <param name="maxLevelSize">最大关尺寸</param>
/// <param name="tbxMsg">显示相关信息的文本框</param>
public void Import(string name, int maxLevelSize, TextBox tbxMsg)
{
try
{
tbxMsg.Text = string.Format("{1} => {2}{0}", Fcl.NewLine, name + Pub.TextExtName, name + Pub.DataExtName);
if (!Directory.Exists(Pub.DataDirectory)) Directory.CreateDirectory(Pub.DataDirectory);
using (StreamReader sr = new StreamReader(Path.Combine(Pub.TextDirectory, name + Pub.TextExtName), Pub.Encode))
{
Dispose();
fs = new FileStream(Path.Combine(Pub.DataDirectory, name + Pub.DataExtName), FileMode.Create, FileAccess.Write);
bw = new BinaryWriter(fs, Pub.Encode);
byte[] buf = new byte[32];
buf[4] = DataVersion;
buf[5] = (byte)'B';
buf[6] = (byte)'O';
buf[7] = (byte)'X';
bw.Write(buf);
map = new byte[maxLevelSize + 2, maxLevelSize + 2];
List<int> addrList = new List<int>(); // 各关起始地址列表,最后一项为第1关起始地址位置
addrList.Add((int)bw.BaseStream.Position); // 第1关起始地址
groupName = name; // 组名
int level = 0;
levelSize = Size.Empty;
string steps = ""; // 通关步骤
bool isFirst = true;
for (int line = 1; ; line++)
{
string s = sr.ReadLine();
if (s != null) s = s.Trim();
if (line == 1 && s != null && s.Length > 0 && s[0] == '!')
{
groupName = s.Substring(1).Trim();
tbxMsg.Text += "组名: [" + groupName + "]" + Fcl.NewLine;
continue;
}
if (isFirst)
{
isFirst = false;
tbxMsg.Text += "#: 宽x高 墙 砖 地 槽 箱 通关步数" + Fcl.NewLine;
}
if ((s == null || s.Length == 0) && levelSize != Size.Empty)
{
tbxMsg.Text += WriteLevel(level, steps);
addrList.Add((int)bw.BaseStream.Position); // 下一关起始地址
level++;
levelSize = Size.Empty;
steps = "";
}
if (s == null) break;
if (s.Length == 0 || s[0] == RemChar) continue;
if (s[0] == StepsChar)
{
steps = s.Substring(1).Trim(); // 通关步骤
continue;
}
levelSize.Height++;
if (levelSize.Height == 1) levelSize.Width = s.Length;
else if (levelSize.Width != s.Length) ErrorExit(false, line, "宽度不齐");
if (levelSize.Width > maxLevelSize) ErrorExit(false, line, GetMessage("宽度太大", true));
if (levelSize.Height > maxLevelSize) ErrorExit(false, line, GetMessage("高度太大", true));
for (int i = 0; i < levelSize.Width; i++)
if (!Block.IsBlock(map[levelSize.Height, i + 1] = Block.GetByte(s[i])))
ErrorExit(false, line, "非法字符:[" + s[i] + "]");
}
addrs = addrList.ToArray();
maxLevel = level;
WriteAddrs();
WriteGroupName();
}
}
catch (OutOfMemoryException ex)
{
throw new Exception(GetMessage("内存不足", false), ex);
}
finally
{
Dispose();
}
tbxMsg.Text += "导入完成";
}
string GetMessage(string msg1, bool isIncrease)
{
return msg1 + ",请在“菜单 -> 选项”对话框中" + (isIncrease ? "增加" : "减少") +"“最大关尺寸”";
}
/// <summary>
/// 数据导出
/// </summary>
/// <param name="name">数据文件主名</param>
/// <param name="tbxMsg">显示相关信息的文本框</param>
public void Export(string name, TextBox tbxMsg)
{
try
{
tbxMsg.Text = string.Format("{1} => {2}{0}", Fcl.NewLine, name + Pub.DataExtName, name + Pub.TextExtName);
LoadGroup(name);
if (!Directory.Exists(Pub.TextDirectory)) Directory.CreateDirectory(Pub.TextDirectory);
using (StreamWriter sw = new StreamWriter(
Path.Combine(Pub.TextDirectory, name + Pub.TextExtName), false, Pub.Encode))
{
sw.WriteLine("! {0}", groupName);
tbxMsg.Text += "组名: [" + groupName + "]" + Fcl.NewLine;
tbxMsg.Text += "#: 宽x高 总任务数 通关步数" + Fcl.NewLine;
for (int level = 0; level < maxLevel; level++)
{
LoadLevel(level);
sw.WriteLine("{0}[{1}]", RemChar, level + 1); // 注释:第几关
for (int y = 0; y < levelSize.Height; y++)
{
for (int x = 0; x < levelSize.Width; x++) sw.Write(Block.GetChar(map[y + 1, x + 1]));
sw.WriteLine();
}
string steps = GetSteps(name, level); // 通关步骤
if (!string.IsNullOrEmpty(steps)) sw.WriteLine(StepsChar + steps);
sw.WriteLine();
tbxMsg.Text += string.Format("{1}: {2} {3} {4}{0}",
Fcl.NewLine, level + 1, Pub.ToString(levelSize), tasks, steps.Length);
}
}
}
finally
{
Dispose();
}
tbxMsg.Text += "导出完成";
}
void ErrorExit(bool isLevel, int idx, string msg)
{
throw new Exception(string.Format("错误:第{0}{1}:{2}", idx, isLevel ? "关" : "行", msg));
}
public void Dispose()
{
if (br != null) br.Close();
if (bw != null) bw.Close();
if (fs != null) fs.Close();
br = null;
bw = null;
fs = null;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -