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

 这是“ 使用 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
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值