Changeset 8145bd3


Ignore:
Timestamp:
Aug 10, 2010, 1:19:58 PM (7 years ago)
Author:
Michael Seifert <mseifert@…>
Branches:
master
Children:
99a84cd
Parents:
b471e8d
Message:

Improved parsing routine for replay.details in SC2Replay files;
DefaultPlayer? is immutable by non-package members and non-inheriting classes;
Added realID property to SC2Player

Files:
7 edited

Legend:

Unmodified
Added
Removed
  • core/src/main/java/de/erichseifert/warp/core/replay/DefaultPlayer.java

    rb471e8d r8145bd3  
    5151        }
    5252
    53         public void addAction(GameAction action) {
    54                 actions.add(action);
    55         }
    56 
    57         public void setActions(List<? extends GameAction> actions) {
     53        protected void setActions(List<? extends GameAction> actions) {
    5854                this.actions.clear();
    59                 this.actions.addAll(actions);
     55                if (actions != null) {
     56                        this.actions.addAll(actions);
     57                }
    6058        }
    6159
     
    6563        }
    6664
    67         public void setColor(Color color) {
     65        protected void setColor(Color color) {
    6866                this.color = color;
    6967        }
     
    7573        }
    7674
    77         public void addMessage(ChatMessage message) {
    78                 messages.add(message);
    79         }
    80 
    81         public void setMessages(List<? extends ChatMessage> messages) {
     75        protected void setMessages(List<? extends ChatMessage> messages) {
    8276                this.messages.clear();
    83                 this.messages.addAll(messages);
     77                if (messages != null) {
     78                        this.messages.addAll(messages);
     79                }
    8480        }
    8581
     
    9692        }
    9793
    98         public void setRace(String race) {
     94        protected void setRace(String race) {
    9995                this.race = race;
    10096        }
     
    105101        }
    106102
    107         public void setObserver(boolean observer) {
     103        protected void setObserver(boolean observer) {
    108104                this.observer = observer;
    109105        }
  • sc2replay/src/main/java/de/erichseifert/warp/sc2replay/ReplayMetaData.java

    rb471e8d r8145bd3  
    6262                                        playerChat = Collections.EMPTY_LIST;
    6363                                }
    64                                 SC2Player player = new SC2Player(playerName, actions.get(playerIndex), playerChat);
     64                                SC2Player player = new SC2Player(playerName);
     65                                player.setActions(actions.get(playerIndex));
     66                                player.setMessages(playerChat);
    6567                                // TODO: Retrieve "true" information about a player being an observer
    6668                                player.setObserver(true);
  • sc2replay/src/main/java/de/erichseifert/warp/sc2replay/SC2Player.java

    rb471e8d r8145bd3  
    3636        private static Color COLOR_OBSERVER = Color.GRAY;
    3737
    38         private boolean observer;
     38        private String realID;
    3939
    4040        /**
     
    4242         * @param name Name of the player.
    4343         */
    44         protected SC2Player(String name, List<? extends GameAction> actions, List<? extends ChatMessage> chatMessages) {
     44        protected SC2Player(String name) {
    4545                super(name);
    46                 setActions(actions);
    47                 setMessages(chatMessages);
    4846        }
    4947
    5048        @Override
    51         public boolean isObserver() {
    52                 return observer;
     49        protected void setActions(List<? extends GameAction> actions) {
     50                super.setActions(actions);
    5351        }
    5452
    5553        @Override
    56         public void setObserver(boolean observer) {
    57                 this.observer = observer;
     54        protected void setMessages(List<? extends ChatMessage> messages) {
     55                super.setMessages(messages);
     56        }
     57
     58        @Override
     59        protected void setRace(String race) {
     60                super.setRace(race);
     61        }
     62
     63        @Override
     64        protected void setColor(Color color) {
     65                super.setColor(color);
     66        }
     67
     68        @Override
     69        protected void setObserver(boolean observer) {
     70                super.setObserver(observer);
    5871                if (observer) {
    5972                        setColor(COLOR_OBSERVER);
     
    6174                }
    6275        }
     76
     77        public String getRealID() {
     78                return realID;
     79        }
     80
     81        protected void setRealID(String realID) {
     82                this.realID = realID;
     83        }
    6384}
  • sc2replay/src/main/java/de/erichseifert/warp/sc2replay/SC2ReplayDetail.java

    rb471e8d r8145bd3  
    2222package de.erichseifert.warp.sc2replay;
    2323
     24import java.awt.Color;
     25import java.io.DataOutputStream;
     26import java.io.File;
     27import java.io.FileOutputStream;
     28import java.io.IOException;
     29import java.io.OutputStream;
    2430import java.nio.ByteBuffer;
    25 import java.util.Collections;
    2631import java.util.HashMap;
     32import java.util.LinkedList;
     33import java.util.List;
    2734import java.util.Map;
    2835
    2936import de.erichseifert.warp.core.util.ReplayUtil;
     37import de.erichseifert.warp.sc2replay.util.DataCluster;
     38import de.erichseifert.warp.sc2replay.util.SC2ReplayUtil;
    3039
    3140public class SC2ReplayDetail {
    32         private final Map<String, PlayerDetail> playerDetails;
     41        private final List<SC2Player> players;
    3342
    34         protected class PlayerDetail {
    35                 private static final String NAME_SEPARATOR = "#";
    36 
    37                 public static final int KEY_COLOR_ALPHA = 0x0009;
    38                 public static final int KEY_COLOR_RED = 0x0209;
    39                 public static final int KEY_COLOR_GREEN = 0x0409;
    40                 public static final int KEY_COLOR_BLUE = 0x0609;
    41 
    42                 private final Map<Integer, Integer> properties;
    43                 private final String nickname;
    44                 private final String realID;
    45                 private final String raceName;
    46 
    47                 public PlayerDetail(ByteBuffer src) {
    48                         properties = new HashMap<Integer, Integer>(9);
    49 
    50                         // Skip header
    51                         src.position(src.position()+4);
    52 
    53                         int nicknameLength = (src.get() & 0xFF)/2;
    54                         nickname = ReplayUtil.getString(src, nicknameLength);
    55 
    56                         // Skip unknown bytes
    57                         src.position(src.position()+3);
    58 
    59                         MapEntry entry1 = new MapEntry(src);
    60                         properties.put(entry1.getKey(), entry1.getValue());
    61 
    62                         // Skip unknown bytes
    63                         src.position(src.position()+6);
    64 
    65                         MapEntry entry2 = new MapEntry(src);
    66                         properties.put(entry2.getKey(), entry2.getValue());
    67 
    68                         // Skip unknown bytes
    69                         src.position(src.position()+2);
    70 
    71                         int fullNameLength = src.get()/2;
    72                         String fullName = ReplayUtil.getString(src, fullNameLength);
    73                         if (!fullName.isEmpty()) {
    74                                 String[] fullNameSplit = fullName.split(NAME_SEPARATOR);
    75                                 if (!fullNameSplit[0].equals(nickname)) {
    76                                         throw new IllegalArgumentException("A player's nickname does not match his full name. Nickname="+nickname+" Fullname="+fullName);
    77                                 }
    78                                 realID = fullNameSplit[1];
    79                         }
    80                         else {
    81                                 realID = null;
    82                         }
    83 
    84                         // Skip unknown bytes
    85                         src.position(src.position()+2);
    86 
    87                         int raceNameLength = src.get()/2;
    88                         raceName = ReplayUtil.getString(src, raceNameLength);
    89 
    90                         // Skip unknown bytes
    91                         src.position(src.position()+3);
    92 
    93                         for (int i = 0; i < 9; i++) {
    94                                 MapEntry entry = new MapEntry(src);
    95                                 int key = entry.getKey();
    96                                 int value = entry.getValue();
    97                                 properties.put(key, value);
    98                         }
     43        public SC2ReplayDetail(ByteBuffer src) {
     44                File dumpFile = null;
     45                OutputStream dumpStream = null;
     46                DataOutputStream dump = null;
     47                try {
     48                        dumpFile = new File("dump");
     49                        dumpStream = new FileOutputStream(dumpFile);
     50                        dump = new DataOutputStream(dumpStream);
     51                } catch (IOException e) {
     52                        // TODO Auto-generated catch block
     53                        e.printStackTrace();
     54                }
     55                Map<Integer, DataCluster> data = null;
     56                try {
     57                        DataCluster cluster = SC2ReplayUtil.readDataCluster(src);
     58                        data = cluster.getDict(0);
     59                        SC2ReplayUtil.dumpData(cluster, dump);
     60                        dumpStream.close();
     61                        dump.close();
     62                } catch (IOException e) {
     63                        // TODO Auto-generated catch block
     64                        e.printStackTrace();
    9965                }
    10066
    101                 public Integer get(int key) {
    102                         return properties.get(key);
    103                 }
     67                players = new LinkedList<SC2Player>();
    10468
    105                 public String getNickname() {
    106                         return nickname;
    107                 }
     69                DataCluster[] playerData = (DataCluster[]) data.get(0).getArray(0);
     70                for (int playerIndex = 0; playerIndex < playerData.length; playerIndex++) {
     71                        Map<Integer, DataCluster> playerDict = playerData[playerIndex].getDict(0);
     72                        String name = playerDict.get(0).getString(0);
     73                        Map<Integer, DataCluster> playerDetails = playerDict.get(1).getDict(0);
     74                        String raceLocalized = playerDict.get(2).getString(0);
     75                        Map<Integer, DataCluster> colorMap = playerDict.get(3).getDict(0);
    10876
    109                 public String getRealID() {
    110                         return realID;
    111                 }
    112 
    113                 public String getRaceName() {
    114                         return raceName;
     77                        Color color = new Color(
     78                                colorMap.get(1).getInt(0),
     79                                colorMap.get(2).getInt(0),
     80                                colorMap.get(3).getInt(0),
     81                                colorMap.get(0).getInt(0)
     82                        );
     83                        if (!name.isEmpty()) {
     84                                SC2Player player = new SC2Player(name);
     85                                player.setRealID(playerDetails.get(3).getString(0));
     86                                player.setRace(raceLocalized);
     87                                player.setColor(color);
     88                                players.add(player);
     89                        }
    11590                }
    11691        }
    11792
    118         private class MapEntry {
    119                 private final int key;
    120                 private int value;
    121 
    122                 public MapEntry(ByteBuffer src) {
    123                         key = src.getShort();
    124                         byte mixed = src.get();
    125                         value = (mixed & 0xFF) >> 1;
    126                         if (((mixed >> 7) & 1) != 0) {
    127                                 value = ((src.get() & 0xFF) << 6) + (value & 0x3F);
    128                         }
    129                         // All values are double within an entry
    130                         //value = value/2;
    131                 }
    132 
    133                 public int getKey() {
    134                         return key;
    135                 }
    136 
    137                 public int getValue() {
    138                         return value;
    139                 }
    140         }
    141 
    142         public SC2ReplayDetail(ByteBuffer src) {
    143 
    144                 // Skip header
    145                 src.position(src.position()+6);
    146 
    147                 // Read player information
    148                 int playerCount = src.get()/2;
    149                 playerDetails = new HashMap<String, PlayerDetail>();
    150                 for (int playerIndex = 0; playerIndex < playerCount; playerIndex++) {
    151                         PlayerDetail playerInfo = new PlayerDetail(src);
    152                         if (!playerInfo.getNickname().isEmpty()) {
    153                                 playerDetails.put(playerInfo.getNickname(), playerInfo);
    154                         }
    155                 }
    156 
    157                 // Skip unknown bytes
    158                 src.position(src.position()+9);
    159 
    160                 int minimapFilenameLength = (src.get() & 0xFF)/2;
    161                 String minimapFilename = ReplayUtil.getString(src, minimapFilenameLength);
    162 
    163                 // Skip unknown bytes
    164                 src.position(src.position()+28);
    165         }
    166 
    167         public Map<String, PlayerDetail> getPlayerDetails() {
    168                 return Collections.unmodifiableMap(playerDetails);
     93        public List<SC2Player> getPlayers() {
     94                return players;
    16995        }
    17096}
  • sc2replay/src/main/java/de/erichseifert/warp/sc2replay/SC2ReplayParser_v2.java

    rb471e8d r8145bd3  
    2222package de.erichseifert.warp.sc2replay;
    2323
    24 import java.awt.Color;
    2524import java.io.File;
    2625import java.io.IOException;
     
    3029import java.nio.channels.FileChannel;
    3130import java.nio.channels.FileChannel.MapMode;
    32 import java.util.ArrayList;
    33 import java.util.Collections;
    3431import java.util.HashMap;
    3532import java.util.List;
     
    4037import de.erichseifert.warp.core.replay.GameAction;
    4138import de.erichseifert.warp.core.replay.GameActionFactory;
    42 import de.erichseifert.warp.sc2replay.SC2ReplayDetail.PlayerDetail;
    4339import de.erichseifert.warp.sc2replay.actions.SC2Action;
    4440import de.erichseifert.warp.sc2replay.actions.SC2ActionFactory;
     
    112108                }
    113109
     110                // Read details
     111                ByteBuffer detailBytes = ByteBuffer.wrap(archive.getFiles()[FILE_INDEX_REPLAY_DETAIL].getBytes());
     112                SC2ReplayDetail details = new SC2ReplayDetail(detailBytes);
     113                List<SC2Player> playerList = details.getPlayers();
     114
    114115                // Read init data
    115116                ByteBuffer initDataBytes = ByteBuffer.wrap(archive.getFiles()[FILE_INDEX_REPLAY_INIT_DATA].getBytes());
     
    117118                List<String> playerNames = initData.getPlayerNames();
    118119
    119                 // Read details
    120                 ByteBuffer detailBytes = ByteBuffer.wrap(archive.getFiles()[FILE_INDEX_REPLAY_DETAIL].getBytes());
    121                 SC2ReplayDetail details = new SC2ReplayDetail(detailBytes);
    122                 Map<String, PlayerDetail> playerDetails = details.getPlayerDetails();
     120                // Add observers
     121                for (int playerIndex = playerList.size(); playerIndex < playerNames.size(); playerIndex++) {
     122                        String name = playerNames.get(playerIndex);
     123                        SC2Player observer = new SC2Player(name);
     124                        observer.setObserver(true);
     125                        playerList.add(observer);
     126                }
    123127
    124128                // Assemble player data
    125                 List<SC2Player> playerList = new ArrayList<SC2Player>(playerDetails.size());
    126                 for (int i = 0; i < playerNames.size(); i++) {
    127                         String playerName = playerNames.get(i);
    128                         PlayerDetail info = playerDetails.get(playerName);
    129                         List<? extends GameAction> playerActions = actionList.get(i);
    130                         if (playerActions == null) {
    131                                 playerActions = Collections.EMPTY_LIST;
    132                         }
    133                         List<? extends ChatMessage> playerMessages = messages.get(i);
    134                         if (playerMessages == null) {
    135                                 playerMessages = Collections.EMPTY_LIST;
    136                         }
    137                         SC2Player player;
    138                         if (info == null) {
    139                                 player = new SC2Player(playerName, playerActions, playerMessages);
    140                                 player.setColor(Color.GRAY);
    141                                 player.setObserver(true);
    142                                 player.setRace("Observer");
    143                         }
    144                         else {
    145                                 int r = info.get(PlayerDetail.KEY_COLOR_RED);
    146                                 int g = info.get(PlayerDetail.KEY_COLOR_GREEN);
    147                                 int b = info.get(PlayerDetail.KEY_COLOR_BLUE);
    148                                 Color playerColor = new Color(r, g, b);
    149                                 player = new SC2Player(playerName, playerActions, playerMessages);
    150                                 player.setColor(playerColor);
    151                                 player.setRace(info.getRaceName());
    152                         }
    153 
    154                         playerList.add(player);
     129                for (int playerIndex = 0; playerIndex < playerList.size(); playerIndex++) {
     130                        SC2Player player = playerList.get(playerIndex);
     131                        player.setActions(actionList.get(playerIndex));
     132                        player.setMessages(messages.get(playerIndex));
    155133                }
    156134                replay.setPlayers(playerList);
  • sc2replay/src/main/java/de/erichseifert/warp/sc2replay/util/SC2ReplayUtil.java

    rb471e8d r8145bd3  
    2222package de.erichseifert.warp.sc2replay.util;
    2323
     24import java.io.DataOutputStream;
     25import java.io.IOException;
    2426import java.nio.ByteBuffer;
     27import java.util.LinkedHashMap;
     28import java.util.Map;
    2529
    2630/**
     
    2832 */
    2933public class SC2ReplayUtil {
     34        private static final byte TYPE_BINARY = 2;
     35        private static final byte TYPE_ARRAY  = 4;
     36        private static final byte TYPE_MAP   = 5;
     37        private static final byte TYPE_BYTE   = 6;
     38        private static final byte TYPE_INT    = 7;
     39        private static final byte TYPE_NUMBER = 9;
    3040
    3141        /**
     
    5363                return timeOffset;
    5464        }
     65
     66        public static long readNumber(ByteBuffer src) {
     67                int byteCount = 0;
     68                long value = 0;
     69                long byteCur;
     70                do {
     71                        byteCur = src.get() & 0xFF;
     72                        value = ((byteCur & 0x7F) << byteCount*7) | value;
     73                        byteCount++;
     74                }
     75                while ((byteCur & 0x80) != 0);
     76                boolean isNegative = (value & 0x01) != 0;
     77                value = value >> 1;
     78                if (isNegative) {
     79                        value = -value;
     80                }
     81                return value;
     82        }
     83
     84        public static DataCluster readDataCluster(ByteBuffer src) throws IOException {
     85                DataCluster cluster = new DataCluster();
     86                byte type = src.get();
     87                if (type == TYPE_BINARY) {
     88                        int size = (int) readNumber(src);
     89                        byte[] data = new byte[size];
     90                        src.get(data);
     91                        cluster.add(data);
     92                }
     93                else if (type == TYPE_ARRAY) {
     94                        byte unknown1 = src.get();
     95                        byte unknown2 = src.get();
     96                        if (unknown1 != 1 || unknown2 != 0) {
     97                                System.err.printf("Unknown array format: %X %X", unknown1, unknown2);
     98                        }
     99                        int size = (int) readNumber(src);
     100                        DataCluster[] data = new DataCluster[size];
     101                        for (int i = 0; i < data.length; i++) {
     102                                data[i] = readDataCluster(src);
     103                        }
     104                        cluster.add(data);
     105                }
     106                else if (type == TYPE_MAP) {
     107                        int size = (int) readNumber(src);
     108                        Map<Integer, DataCluster> data = new LinkedHashMap<Integer, DataCluster>(size);
     109                        for (int i = 0; i < size; i++) {
     110                                int index = (int) readNumber(src);
     111                                DataCluster element = readDataCluster(src);
     112                                data.put(index, element);
     113                        }
     114                        cluster.add(data);
     115                }
     116                else if (type == TYPE_BYTE) {
     117                        byte data = src.get();
     118                        cluster.add(data);
     119                }
     120                else if (type == TYPE_INT) {
     121                        int data = src.getInt();
     122                        cluster.add(data);
     123                }
     124                else if (type == TYPE_NUMBER) {
     125                        long data = readNumber(src);
     126                        cluster.add(data);
     127                }
     128                else {
     129                        throw new IOException("Unknown data type "+type+" at offset "+src.position()+")");
     130                }
     131                return cluster;
     132        }
     133
     134        public static void dumpData(DataCluster data, DataOutputStream dst) throws IOException {
     135                dumpData(data, dst, 0);
     136        }
     137
     138        private static void dumpData(DataCluster data, DataOutputStream dst, int depthCur) throws IOException {
     139                for (Object element : data) {
     140                        if (element instanceof byte[]) {
     141                                String dataString = new String((byte[]) element);
     142                                dst.writeBytes("\""+dataString+"\"");
     143                                dst.writeBytes(",");
     144                        }
     145                        else if (element instanceof Map<?, ?>) {
     146                                dst.writeBytes("{"+System.getProperty("line.separator"));
     147                                depthCur++;
     148                                indent(dst, depthCur);
     149                                for (DataCluster elem : ((Map<Integer, DataCluster>) element).values()) {
     150                                        dumpData(elem, dst, depthCur);
     151                                }
     152                                depthCur--;
     153                                dst.writeBytes(System.getProperty("line.separator"));
     154                                indent(dst, depthCur);
     155                                dst.writeBytes("},"+System.getProperty("line.separator"));
     156                                indent(dst, depthCur);
     157                        }
     158                        else if (element instanceof Object[]) {
     159                                dst.writeBytes("["+System.getProperty("line.separator"));
     160                                depthCur++;
     161                                indent(dst, depthCur);
     162                                for (DataCluster elem : (DataCluster[]) element) {
     163                                        dumpData(elem, dst, depthCur);
     164                                }
     165                                depthCur--;
     166                                dst.writeBytes(System.getProperty("line.separator"));
     167                                indent(dst, depthCur);
     168                                dst.writeBytes("],"+System.getProperty("line.separator"));
     169                                indent(dst, depthCur);
     170                        }
     171                        else if (element instanceof Byte) {
     172                                dst.writeBytes(String.valueOf(element));
     173                                dst.writeBytes(",");
     174                        }
     175                        else if (element instanceof Integer) {
     176                                dst.writeBytes(String.valueOf(element));
     177                                dst.writeBytes(",");
     178                        }
     179                        else if (element instanceof Long) {
     180                                dst.writeBytes(String.valueOf(element));
     181                                dst.writeBytes(",");
     182                        }
     183                        else {
     184                                throw new IOException();
     185                        }
     186                }
     187        }
     188
     189        private static void indent(DataOutputStream dst, int depth) throws IOException {
     190                for (int i = 0; i < depth; i++) {
     191                        dst.writeBytes("\t");
     192                }
     193        }
    55194}
  • sc2replay/src/test/java/de/erichseifert/warp/sc2replay/Sc2replayTests.java

    rb471e8d r8145bd3  
    2626
    2727import de.erichseifert.warp.sc2replay.actions.ActionTests;
     28import de.erichseifert.warp.sc2replay.util.UtilTests;
    2829
    2930
    3031@RunWith(Suite.class)
    3132@Suite.SuiteClasses({
    32         ActionTests.class
     33        ActionTests.class,
     34        UtilTests.class
    3335})
    3436public class Sc2replayTests {
Note: See TracChangeset for help on using the changeset viewer.