def __call__(self, data, replay): # The replay.attribute.events file is comprised of a small header and # single long list of attributes with the 0x00 00 03 E7 header on each # element. Each element holds a four byte attribute id code, a one byte # player id, and a four byte value code. Unlike the other files, this # file is stored in little endian format. # # See: ``objects.Attribute`` for attribute id and value lookup logic # data = ByteDecoder(data, endian='LITTLE') attribute_events = list() data.read_bytes(self.header_length) for attribute in range(data.read_uint32()): info = struct.unpack('<IIB4s', data.read(13)) attribute_events.append(Attribute(*info)) return attribute_events
def __init__(self, contents): # According to http://www.galaxywiki.net/MapInfo_(File_Format) # With a couple small changes for version 0x20+ data = ByteDecoder(contents, endian='LITTLE') magic = data.read_string(4) if magic != 'MapI': self.logger.warn("Invalid MapInfo file: {0}".format(magic)) return #: The map info file format version self.version = data.read_uint32() if self.version >= 0x18: self.unknown1 = data.read_uint32() self.unknown2 = data.read_uint32() #: The full map width self.width = data.read_uint32() #: The full map height self.height = data.read_uint32() #: Small map preview type: 0 = None, 1 = Minimap, 2 = Custom self.small_preview_type = data.read_uint32() #: (Optional) Small map preview path; relative to root of map archive self.small_preview_path = str() if self.small_preview_type == 2: self.small_preview_path = data.read_cstring() #: Large map preview type: 0 = None, 1 = Minimap, 2 = Custom self.large_preview_type = data.read_uint32() #: (Optional) Large map preview path; relative to root of map archive self.large_preview_path = str() if self.large_preview_type == 2: self.large_preview_path = data.read_cstring() if self.version >= 0x1f: self.unknown3 = data.read_cstring() if self.version >= 0x26: self.unknown4 = data.read_cstring() if self.version >= 0x1f: self.unknown5 = data.read_uint32() self.unknown6 = data.read_uint32() #: The type of fog of war used on the map self.fog_type = data.read_cstring() #: The tile set used on the map self.tile_set = data.read_cstring() #: The left bounds for the camera. This value is 7 less than the value shown in the editor. self.camera_left = data.read_uint32() #: The bottom bounds for the camera. This value is 4 less than the value shown in the editor. self.camera_bottom = data.read_uint32() #: The right bounds for the camera. This value is 7 more than the value shown in the editor. self.camera_right = data.read_uint32() #: The top bounds for the camera. This value is 4 more than the value shown in the editor. self.camera_top = data.read_uint32() #: The map base height (what is that?). This value is 4096*Base Height in the editor (giving a decimal value). self.base_height = data.read_uint32()/4096 # Leave early so we dont barf. Turns out ggtracker doesnt need # any of the map data thats loaded below. return #: Load screen type: 0 = default, 1 = custom self.load_screen_type = data.read_uint32() #: (Optional) Load screen image path; relative to root of map archive self.load_screen_path = data.read_cstring() #: Unknown string, usually empty self.unknown7 = data.read_bytes(data.read_uint16()).decode('utf8') #: Load screen image scaling strategy: 0 = normal, 1 = aspect scaling, 2 = stretch the image. self.load_screen_scaling = data.read_uint32() #: The text position on the loading screen. One of: #: #: * 0xffffffff = (Default) #: * 0 = Top Left #: * 1 = Top #: * 2 = Top Right #: * 3 = Left #: * 4 = Center #: * 5 = Right #: * 6 = Bottom Left #: * 7 = Bottom #: * 8 = Bottom Right #: self.text_position = data.read_uint32() #: Loading screen text position offset x self.text_position_offset_x = data.read_uint32() #: Loading screen text position offset y self.text_position_offset_y = data.read_uint32() #: Loading screen text size x self.text_position_size_x = data.read_uint32() #: Loading screen text size y self.text_position_size_y = data.read_uint32() #: A bit array of flags with the following options (possibly incomplete) #: #: * 0x00000001 = Disable Replay Recording #: * 0x00000002 = Wait for Key (Loading Screen) #: * 0x00000004 = Disable Trigger Preloading #: * 0x00000008 = Enable Story Mode Preloading #: * 0x00000010 = Use Horizontal Field of View #: self.data_flags = data.read_uint32() self.unknown8 = data.read_uint32() if self.version >= 0x19: self.unknown9 = data.read_bytes(8) if self.version >= 0x1f: self.unknown10 = data.read_bytes(9) if self.version >= 0x20: self.unknown11 = data.read_bytes(4) #: The number of players enabled via the data editor self.player_count = data.read_uint32() #: A list of references to :class:`MapInfoPlayer` objects self.players = list() for i in range(self.player_count): self.players.append(MapInfoPlayer( pid=data.read_uint8(), control=data.read_uint32(), color=data.read_uint32(), race=data.read_cstring(), unknown=data.read_uint32(), start_point=data.read_uint32(), ai=data.read_uint32(), decal=data.read_cstring(), )) #: A list of the start location point indexes used in Basic Team Settings. #: The editor limits these to only Start Locations and not regular points. self.start_locations = list() for i in range(data.read_uint32()): self.start_locations.append(data.read_uint32()) #: The number of start locations used self.start_location_used = data.read_uint32() #: The number of alliance flags encoded in :attr:`alliance_flags`. self.alliance_flags_length = data.read_uint32() # A set bit (1) indicates that the pair of Start Locations are to be allied. # bit = 1; // Set up a bitmask # // i will be the first Start Location in the Point Indexes array # // j will the the Start Location after i # for(i=0;i< Start Location Count;i++){ # for(j=i+1;j < Start Location Count;j++){ // set j, and then iterate through the rest # bit <<= 1; // Shift left to move the mask to the next bit. # if((Team Enemy Flags & bit) != 0) { // These start locations are allies # // Add more to compensate for byte boundaries. This array can get big. # } # } # } #: A bit array of flags mapping out the player alliances self.alliance_flags = data.read_uint(int(math.ceil(self.alliance_flags_length/8.0))) #: A list of the advanced start location point indexes used in Advanced Team Settings. #: The editor limits these to only Start Locations and not regular points. self.advanced_start_locations = list() for i in range(data.read_uint32()): # point index for each start location used self.advanced_start_locations.append(data.read_uint32()) #: A list of bit arrays marking which start locations below to which team. self.advanced_teams_flags = list() for i in range(data.read_uint32()): # TODO: # One set for each team. Each bit corresponds with the Point Indexes # array index (i.e., bit 0 is PointIndexes[0], bit1 is PointIndex[1], # etc.). If the bit is set, that start location is a part of that team. self.advanced_teams_flags.append(data.read_uint32()) #: Possibly "number of teams used"? Similar to "start locations used" self.advanced_teams_count2 = data.read_uint32() #: The number of enemy flags encoded in :attr:`enemy_flags`. self.enemy_flags_length = data.read_uint32() # A set bit (1) indicates that the pair of teams are to be enemies. # bit = 1; // Set up a bitmask # // i will be the first Team in the Team Members array. # // j will be the Team that comes after i # for(i=0;i< Team Count;i++){ # for(j=i+1;j < Team Count;j++){ // set j, and then iterate through the rest # bit <<= 1; // Shift left to move the mask to the next bit. # if((Team Enemy Flags & bit) != 0) { // These teams are enemies # // Add more code to compensate for byte boundaries. # } # } # } #: A bit array of flags mapping out the player enemies. self.enemy_flags = data.read_uint(int(math.ceil(self.enemy_flags_length/8.0))) if data.length != data.tell(): self.logger.warn("Not all of the MapInfo file was read!")