您的位置:首页 > 其它

使用 C# 开发智能手机软件:推箱子(十)

2007-08-29 22:08 609 查看
这是“使用 C# 开发智能手机软件:推箱子”系列文章的第十篇。在这篇文章中,介绍 Common/DataFile.cs 源程序文件。这个源程序文件中包含密封类 DataFile,用来管理数据文件。



上图是数据文件 konka.bxb 的结构图。该数据文件大小为 297 字节,包含三个关卡,各个关卡的大小分别为:“8x7”、“8x7”和“9x5”。内容如下:

1. 文件头(32字节,图中的青色部分)。首先是保留的四个字节。然后是一个字节的数据文件版本号(目前为“2”)。接着是三个字节的标志(内容为“BOX”)。接着是十六字节的组名(编码为“GB2312”,本数据文件中为“康佳”)。接着是总关数(Int32,四个字节,因为本组共有三个关卡,所以内容为“3”)。最后是第一关起始地址位置(Int32,四个字节,本数据文件中的内容为“0x11D”)。

2. 以关数据头(32字节,图中的绿色部分)开始的各关数据。首先是一个字节的关数据头开始标志(“@”)。然后是一个字节的标志(最低位:0:未通关,1:已通关)。接着是通关总步数(Int32,四个字节)。接着是通关推箱子步数(Int32,四个字节)。接着是十四个字节的保留字段。接着是本关的宽度(Int32,四个字节)。接着是本关的高度(Int32,四个字节)。最后是“本关宽度x本关高度”个字节的关数据,也就是说地图中每个单元格占一个字节,取值范围是“0”到“7”,分别表示:0:地,1:槽,2:墙,3:砖,4:箱子放在地上,5:箱子放在槽上,6:工人站在地上,7:工人站在槽上。注意,每一个关卡必须刚好有一个工人。

3. 数据文件的最后是各关起始地址列表(“总关数x4”个字节,图中的黄色部分)。每关的起始地址均为四个字节(Int32),所以共有“总关数x4”个字节。

密封类 DataFile 的源代码中有详细的注释,很容易看懂。

1. InitMap 方法用来初始化地图。地图的大小是“(关的高度+2) x (关的宽度+2)”,这是为了在地图四周砌上围墙,以免搜索算法越出地图边界。

2. DeleteLevel 方法用来删除指定的关。注意,删除关时并不删除关数据,只是将该关的起始地址从各关地址列表中删除,然后将文件缩短四个字节(因为各关地址列表在数据文件的最后)。这样数据文件中就可能包含不需要的冗余数据。通过“菜单 -> 数据 -> 转换”,先“导出”,然后“导入”,可以消除冗余数据。

1 using System;

2 using System.IO;

3 using System.Drawing;

4 using System.Collections.Generic;

5 using System.Windows.Forms;

6

7 namespace Skyiv.Ben.PushBox.Common

8 {

9 // data/<group>.bxb 文件格式

10 // 保留 ver(2) BOX 组名- 总关数 第1关起始地址位置

11 // 0--3 4----- 5-7 8--23 24--27 28-------------31

12 //

13 // @ Flag 总步数 推箱子步数 保留- wide- high- data

14 // 0 1--- 2----5 6--------9 10-23 24-27 28-31 32..

15 // Flag: 最低位: 0:未通关 1:已通关

16 //

17 // 第1关起始地址 第2关起始地址 . 最后一关起始地址

18 // 0-----------3 4-----------7 . (文件最后四字节)

19 //

20 // steps/<group><level>.bxs 文件格式见 Step.cs

21 // 其中<level>为关数(1起始),最少四位,不足前补零

22 //

23 // text/<group>.bxa 文件格式

24 // 0 - land SPACE

25 // 1 + slot .

26 // 2 # wall #

27 // 3 % brick N/A

28 // 4 x box on land $

29 // 5 X box on slot *

30 // 6 ( man on land @

31 // 7 ) man on slot + .XSB 文件格式

32 // 第一行如果以!开头的话, 则为组名(不能超过16个字符)

33 // 以:开头的行为通关步骤, 格式同(.bxs)文件

34 // 以'开头的行为注释, 完全忽略

35 // 各关之间必须以空行分隔

36

37 /// <summary>

38 /// 管理数据文件: *.bxb *.bxa *.bxs

39 /// </summary>

40 sealed class DataFile : IDisposable

41 {

42 const byte DataVersion = 2; // 数据文件(.bxb)的版本

43 const byte LevelFlag = (byte)'@'; // 数据文件(.bxb)的关标志

44 const char RemChar = '\''; // 文本文件(.bxa)的注释

45 const char StepsChar = ':'; // 文本文件(.bxa)的通关步骤

46

47 FileStream fs; // 数据文件基础流

48 BinaryReader br; // 数据文件读取器

49 BinaryWriter bw; // 数据文件写入器

50 string groupName; // 当前组名称

51 int[] addrs; // 各关起始地址列表,最后一项为第1关起始地址位置

52 byte[,] map; // 当前关地图

53 int maxLevel; // 总关数

54 Size levelSize; // 当前关尺寸(以单元格为单位)

55 Point worker; // 当前工人位置(以单元格为单位)

56 int mans; // 工人数

57 int boxs; // 箱子数

58 int slots; // 槽数

59 int tasks; // 总任务数

60 int boths; // 已完成任务数

61 bool isFinished; // 是否曾经通关

62 int movedSteps; // 通关的总步数

63 int pushedSteps; // 通关的推箱子步数

64 string fileName { get { return Path.GetFileNameWithoutExtension(fs.Name); } } // 数据文件主名

65

66 public string GroupName { get { return groupName; } }

67 public int MaxLevel { get { return maxLevel; } }

68 public byte[,] Map { get { return map; } }

69 public Size LevelSize { get { return levelSize; } }

70 public bool IsFinished { get { return isFinished; } }

71 public int MovedSteps { get { return movedSteps; } }

72 public int PushedSteps { get { return pushedSteps; } }

73 public Point Worker { get { return worker; } }

74 public bool HasWorker { get { return mans != 0; } }

75 public int Boxs { get { return boxs; } }

76 public int Slots { get { return slots; } }

77 public int Tasks { get { return tasks; } }

78 public int Boths { get { return boths; } set { boths = value; } }

79

80 /// <summary>

81 /// 装入组数据

82 /// </summary>

83 /// <param name="name">组文件名</param>

84 public void LoadGroup(string name)

85 {

86 Dispose();

87 fs = new FileStream(Path.Combine(Pub.DataDirectory, name + Pub.DataExtName), FileMode.Open);

88 br = new BinaryReader(fs, Pub.Encode);

89 bw = new BinaryWriter(fs, Pub.Encode);

90 br.ReadInt32(); // 保留

91 if (br.ReadByte() != DataVersion) throw new Exception("数据文件版本错");

92 byte[] bs = br.ReadBytes(3); // 数据文件标志:BOX

93 for (int i = 0; i < bs.Length; i++) if (bs[i] != "BOX"[i]) throw new Exception("数据文件标志错");

94 bs = br.ReadBytes(16); // 组名

95 for (int i = 0; i < bs.Length; i++) if (bs[i] == 0) bs[i] = 32;

96 groupName = Pub.Encode.GetString(bs, 0, bs.Length).Trim();

97 if (groupName.Length == 0) groupName = fileName; // 如果数据文件中组名为空,则用数据文件主名代替

98 maxLevel = br.ReadInt32(); // 总关数

99 int addrPos = br.ReadInt32(); // 第1关起始地址位置

100 br.BaseStream.Seek(addrPos, SeekOrigin.Begin);

101 addrs = new int[maxLevel + 1]; // 各关起始地址列表,最后一项为第1关起始地址位置

102 for (int i = 0; i < maxLevel; i++) addrs[i] = br.ReadInt32();

103 addrs[maxLevel] = addrPos; // 第1关起始地址位置

104 if (addrPos + 4 * maxLevel != br.BaseStream.Length) throw new Exception("数据文件地址表必须位于数据最后");

105 }

106

107 /// <summary>

108 /// 装入关数据

109 /// </summary>

110 /// <param name="level">关数</param>

111 public void LoadLevel(int level)

112 {

113 LoadLevelHead(level);

114 InitMap();

115 for (int i = 1; i <= levelSize.Height; i++)

116 {

117 for (int j = 1; j <= levelSize.Width; j++)

118 {

119 map[i, j] = br.ReadByte();

120 UpdateCounts(j, i, true);

121 }

122 }

123 if (mans != 1) throw new Exception("读取关数据失败:必须刚好有一个工人");

124 tasks = Math.Min(boxs, slots);

125 }

126

127 /// <summary>

128 /// 新建一关

129 /// </summary>

130 /// <param name="isCopy">是否复制当前关</param>

131 /// <param name="size">新建关的尺寸</param>

132 public void NewLevel(bool isCopy, Size size)

133 {

134 Size levelSizeOem = levelSize;

135 byte[,] mapOem = isCopy ? (byte[,])map.Clone() : null;

136 levelSize = size;

137 InitMap();

138 for (int i = 1; i <= levelSize.Height; i++)

139 {

140 for (int j = 1; j <= levelSize.Width; j++)

141 {

142 map[i, j] = (isCopy && i <= levelSizeOem.Height && j <= levelSizeOem.Width) ? mapOem[i, j] : Block.Land;

143 UpdateCounts(j, i, true);

144 }

145 }

146 if (mans != 1 && mans != 0) throw new Exception("不能超过一个工人");

147 tasks = Math.Min(boxs, slots);

148 }

149

150 /// <summary>

151 /// 初始化地图

152 /// </summary>

153 private void InitMap()

154 {

155 map = new byte[levelSize.Height + 2, levelSize.Width + 2];

156 for (int i = 0; i <= levelSize.Height + 1; i++) map[i, 0] = map[i, levelSize.Width + 1] = Block.Wall;

157 for (int j = 0; j <= levelSize.Width + 1; j++) map[0, j] = map[levelSize.Height + 1, j] = Block.Wall;

158 mans = boxs = slots = boths = 0;

159 }

160

161 /// <summary>

162 /// 根据地图项目更新统计信息

163 /// </summary>

164 /// <param name="x">当前位置横坐标</param>

165 /// <param name="y">当前位置纵坐标</param>

166 /// <param name="isAdd">加或减</param>

167 public void UpdateCounts(int x, int y, bool isAdd)

168 {

169 int sign = isAdd ? 1 : -1;

170 if (Block.IsBox(map[y, x])) boxs += sign;

171 if (Block.IsSlot(map[y, x])) slots += sign;

172 if (Block.Box1 == map[y, x]) boths += sign;

173 if (Block.IsMan(map[y, x]))

174 {

175 mans += sign;

176 worker = isAdd ? new Point(x, y) : Point.Empty;

177 }

178 }

179

180 /// <summary>

181 /// 装入关数据头

182 /// </summary>

183 /// <param name="level">关数</param>

184 void LoadLevelHead(int level)

185 {

186 if (level > maxLevel - 1) throw new Exception(string.Format("当前关数({0})不能大于总关数({1})", level + 1, maxLevel));

187 br.BaseStream.Seek(addrs[level], SeekOrigin.Begin);

188 if (br.ReadByte() != LevelFlag) throw new Exception("关数据标志错");

189 isFinished = (br.ReadByte() & 1) == 1; // 是否曾经通关

190 movedSteps = br.ReadInt32(); // 通关的总步数

191 pushedSteps = br.ReadInt32(); // 通关的推箱子步数

192 br.ReadBytes(14); // 保留

193 levelSize.Width = br.ReadInt32();

194 levelSize.Height = br.ReadInt32();

195 }

196

197 /// <summary>

198 /// 更新当前关数据

199 /// </summary>

200 /// <param name="level">关数</param>

201 /// <param name="steps">通关步骤</param>

202 /// <param name="pushs">推箱子步数</param>

203 public void SaveLevel(int level, Step[] steps, int pushs)

204 {

205 SaveLevelHead(level, steps.Length, pushs);

206 SaveLevelSteps(level, Pub.ToString(steps));

207 LoadLevelHead(level);

208 }

209

210 /// <summary>

211 /// 更新当前关头数据

212 /// </summary>

213 /// <param name="level">关数</param>

214 /// <param name="moves">通关步数</param>

215 /// <param name="pushs">推箱子步数</param>

216 void SaveLevelHead(int level, int moves, int pushs)

217 {

218 if (level > maxLevel - 1) throw new Exception("关数太大");

219 bw.BaseStream.Seek(addrs[level] + 1, SeekOrigin.Begin);

220 bw.Write((byte)1); // 是否曾经通关

221 bw.Write(moves); // 通关的总步数

222 bw.Write(pushs); // 通关的推箱子步数

223 }

224

225 /// <summary>

226 /// 保存通关步骤

227 /// </summary>

228 /// <param name="level">关数</param>

229 /// <param name="steps">通关步骤</param>

230 void SaveLevelSteps(int level, string steps)

231 {

232 if (!Directory.Exists(Pub.StepsDirectory)) Directory.CreateDirectory(Pub.StepsDirectory);

233 Fcl.WriteAllText(GetStepsFileName(fileName, level), steps);

234 }

235

236 /// <summary>

237 /// 给出通关步骤

238 /// </summary>

239 /// <param name="level">关数</param>

240 /// <returns>通关步骤</returns>

241 public string GetSteps(int level)

242 {

243 return GetSteps(fileName, level);

244 }

245

246 string GetSteps(string name, int level)

247 {

248 return Fcl.ReadAllText(GetStepsFileName(name, level));

249 }

250

251 string GetStepsFileName(string name, int level)

252 {

253 return Path.Combine(Pub.StepsDirectory, name + (level + 1).ToString("D4") + Pub.StepsExtName);

254 }

255

256 /// <summary>

257 /// 删除通关步骤文件

258 /// </summary>

259 /// <param name="level">关数</param>

260 private void DeleteStepsFile(int level)

261 {

262 // 虽然 File.Delete(): 删除指定的文件。如果指定的文件不存在,则不引发异常。

263 // 但是: 如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。

264 // 所以需要先用 File.Exists() 判断一下文件是否存在

265 string name = GetStepsFileName(fileName, level);

266 if (File.Exists(name)) File.Delete(name);

267 }

268

269 /// <summary>

270 /// 保存设计数据

271 /// </summary>

272 /// <param name="isNew">是否新建</param>

273 /// <param name="level">要保存的关数</param>

274 public void SaveDesign(bool isNew, int level)

275 {

276 if (isNew && level != maxLevel) throw new Exception("新建的关必须在最后一关之后");

277 bw.BaseStream.Seek(addrs[level], SeekOrigin.Begin);

278 WriteLevel(level, string.Empty); // 如果不是新建,则关尺寸不能比原来的大

279 if (isNew)

280 {

281 Fcl.Resize(ref addrs, addrs.Length + 1);

282 addrs[++maxLevel] = (int)bw.BaseStream.Position;

283 WriteAddrs();

284 }

285 DeleteStepsFile(level); // 删除通关步骤文件

286 }

287

288 /// <summary>

289 /// 删除最后一关

290 /// </summary>

291 /// <param name="level">关数(必须是最后一关)</param>

292 public void DeleteLastLevel(int level)

293 {

294 if (level != maxLevel - 1) throw new Exception("要删除的关必须是最后一关");

295 DeleteLevel(level);

296 DeleteStepsFile(level); // 删除通关步骤文件,如果被删除的关不是最后一关,以后各关的通关步骤文件就不对了

297 }

298

299 /// <summary>

300 /// 删除指定的关

301 /// </summary>

302 /// <param name="level">关数</param>

303 void DeleteLevel(int level)

304 {

305 for (int i = level + 1; i <= maxLevel; i++) addrs[i - 1] = addrs[i]; // 之后的关起始地址前移

306 --maxLevel; // 更新总关数

307 WriteAddrs();

308 }

309

310 /// <summary>

311 /// 更新各关起始地址列表及总关数和第1关起始地址位置

312 /// </summary>

313 private void WriteAddrs()

314 {

315 bw.Seek(addrs[maxLevel], SeekOrigin.Begin);

316 for (int i = 0; i < maxLevel; i++) bw.Write(addrs[i]); // 各关起始地址

317 bw.BaseStream.SetLength(bw.BaseStream.Position); // 关起始地址列表位于数据文件最后, 用于删除关的情况

318 bw.Seek(24, SeekOrigin.Begin);

319 bw.Write(maxLevel); // 总关数

320 bw.Write(addrs[maxLevel]); // 第1关起始地址位置

321 }

322

323 /// <summary>

324 /// 更新组名

325 /// </summary>

326 void WriteGroupName()

327 {

328 byte[] bs = new byte[16];

329 byte[] bn = Pub.Encode.GetBytes(groupName);

330 for (int i = 0; i < bs.Length && i < bn.Length; i++) bs[i] = bn[i];

331 for (int i = bn.Length; i < bs.Length; i++) bs[i] = 32;

332 bw.Seek(8, SeekOrigin.Begin);

333 bw.Write(bs); // 组名

334 }

335

336 /// <summary>

337 /// 写关数据和通关步骤

338 /// 注意:调用本函数前必须定位到数据文件的正确位置

339 /// </summary>

340 /// <param name="level">关数</param>

341 /// <param name="steps">通关步骤</param>

342 /// <returns>本关的统计信息</returns>

343 string WriteLevel(int level, string steps)

344 {

345 bw.Write(LevelFlag); // 关标志

346 bw.Write((byte)(string.IsNullOrEmpty(steps) ? 0 : 1)); // 标志:是否已通关

347 bw.Write(steps.Length); // 总步数

348 bw.Write(GetPushSteps(steps)); // 推箱子步数

349 bw.Write(new byte[14]); // 保留

350 bw.Write(levelSize.Width); // 当前关宽度

351 bw.Write(levelSize.Height); // 当前关高度

352 mans = slots = boxs = 0;

353 int lands = 0, walls = 0, bricks = 0;

354 for (int i = 1; i <= levelSize.Height; i++)

355 {

356 for (int j = 1; j <= levelSize.Width; j++)

357 {

358 bw.Write(map[i, j]);

359 switch (map[i, j])

360 {

361 case Block.Land: lands++; break;

362 case Block.Slot: slots++; break;

363 case Block.Wall: walls++; break;

364 case Block.Brick: bricks++; break;

365 case Block.Box0: lands++; boxs++; break;

366 case Block.Box1: slots++; boxs++; break;

367 case Block.Man0: lands++; mans++; break;

368 case Block.Man1: slots++; mans++; break;

369 }

370 }

371 }

372 if (mans != 1) ErrorExit(true, level + 1, "必须刚好有一个工人");

373 if (!string.IsNullOrEmpty(steps)) SaveLevelSteps(level, steps);

374 return string.Format("{1}: {2} {3} {4} {5} {6} {7} {8}{0}",

375 Fcl.NewLine, level + 1, Pub.ToString(levelSize), walls, bricks, lands, slots, boxs, steps.Length);

376 }

377

378 /// <summary>

379 /// 根据通关步骤给出推箱子步数

380 /// </summary>

381 /// <param name="steps">通关步骤</param>

382 /// <returns>推箱子步数</returns>

383 int GetPushSteps(string steps)

384 {

385 int n = 0;

386 foreach (char c in steps) if (((Step)c).IsBox) n++;

387 return n;

388 }

389

390 /// <summary>

391 /// 数据导入

392 /// </summary>

393 /// <param name="name">数据文件主名</param>

394 /// <param name="maxLevelSize">最大关尺寸</param>

395 /// <param name="tbxMsg">显示相关信息的文本框</param>

396 public void Import(string name, int maxLevelSize, TextBox tbxMsg)

397 {

398 try

399 {

400 tbxMsg.Text = string.Format("{1} => {2}{0}", Fcl.NewLine, name + Pub.TextExtName, name + Pub.DataExtName);

401 if (!Directory.Exists(Pub.DataDirectory)) Directory.CreateDirectory(Pub.DataDirectory);

402 using (StreamReader sr = new StreamReader(Path.Combine(Pub.TextDirectory, name + Pub.TextExtName), Pub.Encode))

403 {

404 Dispose();

405 fs = new FileStream(Path.Combine(Pub.DataDirectory, name + Pub.DataExtName), FileMode.Create, FileAccess.Write);

406 bw = new BinaryWriter(fs, Pub.Encode);

407 byte[] buf = new byte[32];

408 buf[4] = DataVersion;

409 buf[5] = (byte)'B';

410 buf[6] = (byte)'O';

411 buf[7] = (byte)'X';

412 bw.Write(buf);

413 map = new byte[maxLevelSize + 2, maxLevelSize + 2];

414 List<int> addrList = new List<int>(); // 各关起始地址列表,最后一项为第1关起始地址位置

415 addrList.Add((int)bw.BaseStream.Position); // 第1关起始地址

416 groupName = name; // 组名

417 int level = 0;

418 levelSize = Size.Empty;

419 string steps = ""; // 通关步骤

420 bool isFirst = true;

421 for (int line = 1; ; line++)

422 {

423 string s = sr.ReadLine();

424 if (s != null) s = s.Trim();

425 if (line == 1 && s != null && s.Length > 0 && s[0] == '!')

426 {

427 groupName = s.Substring(1).Trim();

428 tbxMsg.Text += "组名: [" + groupName + "]" + Fcl.NewLine;

429 continue;

430 }

431 if (isFirst)

432 {

433 isFirst = false;

434 tbxMsg.Text += "#: 宽x高 墙 砖 地 槽 箱 通关步数" + Fcl.NewLine;

435 }

436 if ((s == null || s.Length == 0) && levelSize != Size.Empty)

437 {

438 tbxMsg.Text += WriteLevel(level, steps);

439 addrList.Add((int)bw.BaseStream.Position); // 下一关起始地址

440 level++;

441 levelSize = Size.Empty;

442 steps = "";

443 }

444 if (s == null) break;

445 if (s.Length == 0 || s[0] == RemChar) continue;

446 if (s[0] == StepsChar)

447 {

448 steps = s.Substring(1).Trim(); // 通关步骤

449 continue;

450 }

451 levelSize.Height++;

452 if (levelSize.Height == 1) levelSize.Width = s.Length;

453 else if (levelSize.Width != s.Length) ErrorExit(false, line, "宽度不齐");

454 if (levelSize.Width > maxLevelSize) ErrorExit(false, line, GetMessage("宽度太大", true));

455 if (levelSize.Height > maxLevelSize) ErrorExit(false, line, GetMessage("高度太大", true));

456 for (int i = 0; i < levelSize.Width; i++)

457 if (!Block.IsBlock(map[levelSize.Height, i + 1] = Block.GetByte(s[i])))

458 ErrorExit(false, line, "非法字符:[" + s[i] + "]");

459 }

460 addrs = addrList.ToArray();

461 maxLevel = level;

462 WriteAddrs();

463 WriteGroupName();

464 }

465 }

466 catch (OutOfMemoryException ex)

467 {

468 throw new Exception(GetMessage("内存不足", false), ex);

469 }

470 finally

471 {

472 Dispose();

473 }

474 tbxMsg.Text += "导入完成";

475 }

476

477 string GetMessage(string msg1, bool isIncrease)

478 {

479 return msg1 + ",请在“菜单 -> 选项”对话框中" + (isIncrease ? "增加" : "减少") +"“最大关尺寸”";

480 }

481

482 /// <summary>

483 /// 数据导出

484 /// </summary>

485 /// <param name="name">数据文件主名</param>

486 /// <param name="tbxMsg">显示相关信息的文本框</param>

487 public void Export(string name, TextBox tbxMsg)

488 {

489 try

490 {

491 tbxMsg.Text = string.Format("{1} => {2}{0}", Fcl.NewLine, name + Pub.DataExtName, name + Pub.TextExtName);

492 LoadGroup(name);

493 if (!Directory.Exists(Pub.TextDirectory)) Directory.CreateDirectory(Pub.TextDirectory);

494 using (StreamWriter sw = new StreamWriter(

495 Path.Combine(Pub.TextDirectory, name + Pub.TextExtName), false, Pub.Encode))

496 {

497 sw.WriteLine("! {0}", groupName);

498 tbxMsg.Text += "组名: [" + groupName + "]" + Fcl.NewLine;

499 tbxMsg.Text += "#: 宽x高 总任务数 通关步数" + Fcl.NewLine;

500 for (int level = 0; level < maxLevel; level++)

501 {

502 LoadLevel(level);

503 sw.WriteLine("{0}[{1}]", RemChar, level + 1); // 注释:第几关

504 for (int y = 0; y < levelSize.Height; y++)

505 {

506 for (int x = 0; x < levelSize.Width; x++) sw.Write(Block.GetChar(map[y + 1, x + 1]));

507 sw.WriteLine();

508 }

509 string steps = GetSteps(name, level); // 通关步骤

510 if (!string.IsNullOrEmpty(steps)) sw.WriteLine(StepsChar + steps);

511 sw.WriteLine();

512 tbxMsg.Text += string.Format("{1}: {2} {3} {4}{0}",

513 Fcl.NewLine, level + 1, Pub.ToString(levelSize), tasks, steps.Length);

514 }

515 }

516 }

517 finally

518 {

519 Dispose();

520 }

521 tbxMsg.Text += "导出完成";

522 }

523

524 void ErrorExit(bool isLevel, int idx, string msg)

525 {

526 throw new Exception(string.Format("错误:第{0}{1}:{2}", idx, isLevel ? "关" : "行", msg));

527 }

528

529 public void Dispose()

530 {

531 if (br != null) br.Close();

532 if (bw != null) bw.Close();

533 if (fs != null) fs.Close();

534 br = null;

535 bw = null;

536 fs = null;

537 }

538 }

539 }

540

上一篇:使用 C# 开发智能手机软件:推箱子(九)

下一篇:使用 C# 开发智能手机软件:推箱子(十一)

返回目录
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: