package de.lighti; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.logging.Logger; import org.xerial.snappy.Snappy; import com.valve.dota2.Demo.EDemoCommands; import de.lighti.util.Utils; /** * A DemoFile wraps the contents of a Dota2 replay file on disc. It provides * low-level access to CDemo packets. * * @author Tobias Mahlmann * */ public class DemoFile { /** * Basic file information. * @author Tobias Mahlmann * */ private class DemoHeader { /** * The expect header of a replay file. */ private final static String PROTODEMO_HEADER_ID = "PBUFDEM\0"; char[] demoFilestamp = new char[8]; // PROTODEMO_HEADER_ID int fileinfoOffset; } /** * Pointer to underlying filestream. */ private FileInputStream inputFile; /** * Header structure. */ private DemoHeader header; public int bytesRemaining() { try { return (inputFile != null) ? inputFile.available() : 0; } catch (final IOException e) { return 0; } } public void close() throws IOException { if (inputFile != null) { inputFile.close(); } header = null; } public boolean isDone() throws IOException { return (inputFile == null) || (inputFile.available() == 0); } public void open( String filename ) throws IOException { close(); final File fp = new File( filename ); if (fp.canRead()) { inputFile = new FileInputStream( fp ); header = new DemoHeader(); for (int i = 0; i < 8; i++) { header.demoFilestamp[i] = (char) inputFile.read(); } final byte[] offset = new byte[4]; offset[0] = (byte) inputFile.read(); offset[1] = (byte) inputFile.read(); offset[2] = (byte) inputFile.read(); offset[3] = (byte) inputFile.read(); header.fileinfoOffset = Utils.toInt( offset, 0 ); Logger.getLogger( DemoFile.class.getName() ).fine( "Fileinfo offset: " + header.fileinfoOffset ); if (!DemoHeader.PROTODEMO_HEADER_ID.equals( new String( header.demoFilestamp ) )) { throw new IllegalStateException( "Open: demofilestamp doesn't match. " + filename ); } } else { throw new IllegalArgumentException( "Can't open file" ); } } public byte[] readMessage( boolean compressed ) throws IOException { final int size = Utils.readVarInt( inputFile ); Logger.getLogger( DemoFile.class.getName() ).fine( "Reading Message of " + size + " bytes. Compression= " + compressed ); final byte[] buffer = new byte[size]; inputFile.read( buffer ); if (!compressed) { return buffer; } else { return Snappy.uncompress( buffer ); } } public EDemoCommands readMessageType( int[] tick, boolean[] compressed ) throws IOException { int cmd = Utils.readVarInt( inputFile ); compressed[0] = (cmd & EDemoCommands.DEM_IsCompressed.getNumber()) != 0; cmd = (cmd & ~EDemoCommands.DEM_IsCompressed.getNumber()); tick[0] = Utils.readVarInt( inputFile ); return EDemoCommands.valueOf( cmd ); } }