FMFファイル読込スクリプト
多機能な汎用マップエディタ「Platinum」(http://www.vector.co.jp/games/soft/win95/game/se231004.html)
のFMFファイルを読み込むスクリプト。
最近、kenmoは関数型プログラミングにはまっているので、
ところどころでおかしな書き方をしていますが、、。
#!/usr/bin/env python # -*- coding: utf-8 -*- import struct class Layer2D: """ 2次元レイヤー """ def __init__(self, width, height): """ コンストラクタ @param width 幅 @param height 高さ """ self.width = width self.height = height self.size = width*height # データサイズ self.data = () def get(self, i, j): if 0 <= i < self.width: if 0 <= j < self.height: return self.data[j*self.width + i] return None def __str__(self): strData = " " for i, data in enumerate(self.data): strData += "%r,"%data if (i+1) % self.width == 0: strData += "\n " return "(Width,Height)=(%d,%d)\n%s"%( self.width, self.height, strData) class FMFLoader: """ FMFファイル読込クラス """ # データのbit数に対応するバイトコード変換関数テーブル READ_FUNC_TBL = { 8: lambda b: struct.unpack("B", b)[0], 16: lambda b: struct.unpack("H", b)[0], } def __init__(self, filepath, layer2d=Layer2D): """ コンストラクタ @param filepath FMFファイルパス @param layer レイヤーとして使用するクラス(Layer2Dの派生クラス) """ self.filepath = filepath self.file = open(filepath, "rb") # ヘッダ self._readHeader() # データ( レイヤーリスト) self.layerList = map(self._createLayer, [layer2d]*self.layerCount) def _readHeader(self): """ ヘッダ読込[20byte] """ b = self.file.read(4) self.identifier = b # ファイル識別子 [4byte] b = self.file.read(16) (self.dataSize, # ヘッダを除いたサイズ [4byte] self.width, # マップの幅 [4byte] self.height, # マップの高さ [4byte] self.chipWidth, # チップの幅 [1byte] self.chipHeight, # チップの高さ [1byte] self.layerCount, # レイヤー数 [1byte] self.bitCount # データのbit数(8/16) [1byte] ) = struct.unpack("LLLBBBB", b) def _createLayer(self, layer2d): """ レイヤーの生成 """ return self._setLayerData( apply(layer2d, (self.width, self.height)), 0, self._readData()) def _setLayerData(self, layer, idx, var): """ レイヤーにデータを設定 @param layer レイヤーオブジェクト @param idx インデックス @param var 値 """ if idx < layer.size: layer.data += var, return self._setLayerData(layer, idx+1, self._readData()) else: self.file.seek(-self.bitCount/8, 1) # 1つ戻す return layer def _readData(self): """ データ読込 @return 読込データ(EOFの場合None) """ return self._convertData( self.file.read(self.bitCount/8), self.READ_FUNC_TBL[self.bitCount]) def _convertData(self, b, convert): """ データの変換 @param b バイナリコード @param convert 変換関数 @return 変換後のデータ(終わりの場合None) """ if b == "": return None # 終わり else : return convert(b) def getLayer(self, idx): """ レイヤーオブジェクト取得 @parma idx レイヤー番号 """ if 0 <= idx < self.layerCount: return self.layers[idx] return None def strHeader(self): return "%s%s%s%s%s%s%s%s"%( " Identifier :%s\n"%self.identifier, " Size(except Header):%i\n"%self.dataSize, " Width :%i\n"%self.width, " Height :%i\n"%self.height, " ChipWidth :%i\n"%self.chipWidth, " ChipHeight :%i\n"%self.chipHeight, " LayerCount :%i\n"%self.layerCount, " BitCount :%i\n"%self.bitCount, ) def strData(self): result = " " for idx, layer in enumerate(self.layerList): result += "[Layer:%d]\n "%idx for i, data in enumerate(layer.data): result += "%r,"%data if (i+1) % layer.width == 0: result += "\n " return result def __str__(self): header = self.strHeader() data = self.strData() return "Header ... \n%s\nData ... \n%s"%(header, data) def main(): fmf = FMFLoader("area.fmf") print fmf if __name__ == "__main__": main()
あと、なんでLayer2Dオブジェクトを渡しているのかというと、
継承による拡張性を考慮しているからです。
AdapterでLayer2Dをラップしてもいいですが、
継承の方がすっきりする、、、はずです。
実行結果の例はこんな感じ。
Header ... Identifier :FMF_ Size(except Header):200 Width :10 Height :10 ChipWidth :32 ChipHeight :32 LayerCount :2 BitCount :8 Data ... [Layer:0] 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, [Layer:1] 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3, 3,3,2,2,1,1,0,0,3,3,
FMF読込クラス
もう少し変態的な書き方に修正。
#!/usr/bin/env python # -*- coding: utf-8 -*- import struct class Layer2D: """ 2次元レイヤー """ def __init__(self, width, height, data): """ コンストラクタ @param width 幅 @param height 高さ @param data データ """ self.width = width self.height = height self.data = data def get(self, x, y): """ 座標を指定して値を取得 @param x, y 座標 @return 値 """ if 0 <= x < self.width: if 0 <= y < self.height: return self.data[y*self.width + x] return None def __str__(self): strData = " " for i, data in enumerate(self.data): strData += "%r,"%data if (i+1) % self.width == 0: strData += "\n " return "(Width,Height)=(%d,%d)\n%s"%( self.width, self.height, strData) class FMFLoader: """ FMFファイル読込クラス """ # データのbit数に対応するバイナリコード変換関数テーブル CONVERT_BIN_FUNC_TBL = { 8: lambda cnt, b: struct.unpack("%dB"%cnt, b), 16: lambda cnt, b: struct.unpack("%dH"%cnt, b), } def __init__(self, filepath, layer2d=Layer2D): """ コンストラクタ @param filepath FMFファイルパス @param layer レイヤーとして使用するクラス(Layer2Dの派生クラス) """ self.filepath = filepath self.file = open(filepath, "rb") # ヘッダ self._readHeader() # データ( レイヤーリスト) self.layerList = map(self._createLayer, [layer2d]*self.layerCount) def _readHeader(self): """ ヘッダ読込[20byte] """ b = self.file.read(4) assert b == "FMF_", "FileformatError: Expected 'FMF'file. (file:'%s')"%self.filepath self.identifier = b # ファイル識別子 [4byte] b = self.file.read(16) (self.dataSize, # ヘッダを除いたサイズ [4byte] self.width, # マップの幅 [4byte] self.height, # マップの高さ [4byte] self.chipWidth, # チップの幅 [1byte] self.chipHeight, # チップの高さ [1byte] self.layerCount, # レイヤー数 [1byte] self.bitCount # データのbit数(8/16) [1byte] ) = struct.unpack("3L4B", b) def _createLayer(self, layer2d): """ レイヤーの生成 """ return apply(layer2d, (self.width, self.height, self._createLayerData(self.width*self.height))) def _createLayerData(self, size): """ レイヤーデータを生成 @param size レイヤーデータのサイズ """ return self.CONVERT_BIN_FUNC_TBL[self.bitCount]( size, self.file.read(size*self.bitCount/8)) def getLayer(self, index): """ レイヤーオブジェクト取得 @parma index レイヤー番号 """ if 0 <= index < self.layerCount: return self.layerList[index] return None def toStringHeader(self): """ ヘッダを文字列に変換 """ return "%s%s%s%s%s%s%s%s"%( " Identifier :%s\n"%self.identifier, " Size(except Header):%i\n"%self.dataSize, " Width :%i\n"%self.width, " Height :%i\n"%self.height, " ChipWidth :%i\n"%self.chipWidth, " ChipHeight :%i\n"%self.chipHeight, " LayerCount :%i\n"%self.layerCount, " BitCount :%i\n"%self.bitCount, ) def toStringData(self): """ データを文字列に変換 """ result = " " for idx, layer in enumerate(self.layerList): result += "[Layer:%d]\n "%idx for i, data in enumerate(layer.data): result += "%r,"%data if (i+1) % layer.width == 0: result += "\n " return result def __str__(self): return "Header ... \n%s\nData ... \n%s"%( self.toStringHeader(), self.toStringData()) def main(): fmf = FMFLoader("area.fmf") print fmf if __name__ == "__main__": main()