Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

104211
LINES
[SHOW ALL]

< > BotCompany Repo | #1005016 // Mocha Doom Single Source (include)

JavaX source code [tags: use-pretranspiled] - run with: x30.jar

Libraryless. Compilation Failed (104211L/700K).

package utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;

import p.Resettable;
import w.InputStreamSugar;

/**
 * Some utilities that emulate C stlib methods or provide convenient functions
 * to do repetitive system and memory related stuff.
 * 
 * @author Maes
 */

public final class C2JUtils {

    static public final char[] strcpy(char[] s1, final char[] s2) {
        for (int i = 0; i < Math.min(s1.length, s2.length); i++) {
            s1[i] = s2[i];
        }
        return s1;
    }

    static public final char[] strcpy(char[] s1, final char[] s2, int off, int len) {
        for (int i = 0; i < len; i++) {
            s1[i] = s2[i + off];
        }
        return s1;
    }

    static public final char[] strcpy(char[] s1, final char[] s2, int off) {
        for (int i = 0; i < Math.min(s1.length, s2.length - off); i++) {
            s1[i] = s2[i + off];
        }
        return s1;
    }

    static public final char[] strcpy(char[] s1, String s2) {
        for (int i = 0; i < Math.min(s1.length, s2.length()); i++) {
            s1[i] = s2.charAt(i);
        }
        return s1;
    }

    
    /** Return a byte[] array from the string's chars,
     *  ANDed to the lowest 8 bits.
     * 
     * @param str
     * @return
     */
    public static final byte[] toByteArray(String str) {
        byte[] retour = new byte[str.length()];
        for (int i = 0; i < str.length(); i++) {
            retour[i] = (byte) (str.charAt(i) & 0xFF);
        }
        return retour;
    }

    /**
     * Finds index of first element of array matching key. Useful whenever an
     * "indexOf" property is required or you encounter the C-ism [pointer-
     * array_base] used to find array indices in O(1) time. However, since this
     * is just a dumb unsorted search, running time is O(n), so use this method
     * only sparingly and in scenarios where it won't occur very frequently
     * -once per level is probably OK-, but watch out for nested loops, and
     * cache the result whenever possible. Consider adding an index or ID type
     * of field to the searched type if you require to use this property too
     * often.
     * 
     * @param array
     * @param key
     * @return
     */

    public static int indexOf(Object[] array, Object key) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == key) {
                return i;
            }
        }

        return -1;
    }

    /**
     * Emulates C-style "string comparison". "Strings" are considered
     * null-terminated, and comparison is performed only up to the smaller of
     * the two.
     * 
     * @param s1
     * @param s2
     * @return
     */

    static public final boolean strcmp(char[] s1, final char[] s2) {
        boolean match = true;
        for (int i = 0; i < Math.min(s1.length, s2.length); i++) {
            if (s1[i] != s2[i]) {
                match = false;
                break;
            }
        }
        return match;
    }

    static public final boolean strcmp(char[] s1, String s2) {
        return strcmp(s1, s2.toCharArray());
    }

    /**
     * C-like string length (null termination).
     * 
     * @param s1
     * @return
     */
    static public final int strlen(char[] s1) {
        if (s1 == null)
            return 0;
        int len = 0;

        while (s1[len++] > 0) {
            if (len >= s1.length)
                break;
        }

        return len - 1;
    }

    /**
     * Return a new String based on C-like null termination.
     * 
     * @param s
     * @return
     */
    static public final String nullTerminatedString(char[] s) {
        if (s == null)
            return "";
        int len = 0;

        while (s[len++] > 0) {
            if (len >= s.length)
                break;
        }

        return new String(s, 0, len - 1);
    }

    /**
     * Automatically "initializes" arrays of objects with their default
     * constuctor. It's better than doing it by hand, IMO. If you have a better
     * way, be my guest.
     * 
     * @param os
     * @param c
     * @throws Exception
     * @throws
     */

    public static final <T> void initArrayOfObjects(T[] os, Class<T> c) {
        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + os.length
                    + " objects of class" + c.getName() + "!");
            System.exit(-1);
        }
    }

    /**
     * Automatically "initializes" arrays of objects with their default
     * constuctor. It's better than doing it by hand, IMO. If you have a better
     * way, be my guest.
     * 
     * @param os
     * @throws Exception
     * @throws
     */

    public static final <T> void initArrayOfObjects(T[] os) {
        Class<T> c = (Class<T>) os.getClass().getComponentType();
        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = (T) c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + os.length
                    + " objects of class " + c.getName() + "!");

            System.exit(-1);
        }
    }

    /**
     * Uses reflection to automatically create and initialize an array of
     * objects of the specified class. Does not require casting on "reception".
     * 
     * @param <T>
     * @param c
     * @param num
     * @return
     * @return
     */

    public static final <T> T[] createArrayOfObjects(Class<T> c, int num) {
        T[] os = null;

        os=getNewArray(c,num);

        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = (T) c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to instantiate " + os.length
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }

        return os;
    }
    
    /**
     * Uses reflection to automatically create and initialize an array of
     * objects of the specified class. Does not require casting on "reception".
     * Requires an instance of the desired class. This allows getting around
     * determining the runtime type of parametrized types.
     * 
     * 
     * @param <T>
     * @param instance An instance of a particular class. 
     * @param num
     * @return
     * @return
     */

    @SuppressWarnings("unchecked")
    public static final <T> T[] createArrayOfObjects(T instance, int num) {
        T[] os = null;
        
        Class<T> c=(Class<T>) instance.getClass();

        os=getNewArray(c,num);

        try {
            for (int i = 0; i < os.length; i++) {
                os[i] = (T) c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to instantiate " + os.length
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }

        return os;
    }


    /**
     * Automatically "initializes" arrays of objects with their default
     * constuctor. It's better than doing it by hand, IMO. If you have a better
     * way, be my guest.
     * 
     * @param os
     * @param startpos inclusive
     * @param endpos non-inclusive
     * @throws Exception
     * @throws
     */

    public static final<T> void initArrayOfObjects(T[] os, int startpos,
            int endpos) {
        @SuppressWarnings("unchecked")
		Class<T> c = (Class<T>) os.getClass().getComponentType();
        try {
            for (int i = startpos; i < endpos; i++) {
                os[i] = c.newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + os.length
                    + " objects of class " + c.getName() + "!");

            System.exit(-1);
        }
    }

    /** This method gets eventually inlined, becoming very fast */

    public static final int toUnsignedByte(byte b) {
        return (0x000000FF & b);
    }

    // Optimized array-fill methods designed to operate like C's memset.

    public static final void memset(boolean[] array, boolean value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }

    public static final void memset(char[] array, char value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }

    public static final void memset(int[] array, int value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }
    
    public static final void memset(short[] array, short value, int len) {
        if (len > 0)
            array[0] = value;
        for (int i = 1; i < len; i += i) {
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i)
                    : i);
        }
    }

    public static final long unsigned(int num) {
        return 0xFFFFFFFFL & num;
    }

    public static final char unsigned(short num) {
        return (char) num;
    }

    /**
     * Convenient alias for System.arraycopy(src, 0, dest, 0, length);
     * 
     * @param dest
     * @param src
     * @param length
     */
    public static final void memcpy(Object dest, Object src, int length) {
        System.arraycopy(src, 0, dest, 0, length);
    }

    public static final boolean testReadAccess(String URI) {
    	InputStream in=null;
    	
        // This is bullshit.
        if (URI == null)
            return false;
        if (URI.length() == 0)
            return false;

        try {
            in=new FileInputStream(URI);
        } catch (Exception e) {
            // Not a file...
            URL u;
			try {
				u = new URL(URI);
			} catch (MalformedURLException e1) {
				return false;
			}
            try {
				in=u.openConnection().getInputStream();
			} catch (IOException e1) {
				return false;
			}

        }
        
        if (in!=null) {
        	try {
				in.close();
			} catch (IOException e) {

			}
        	return true;
        }
        // All is well. Go on...
        return true;

    }
    
    public static final boolean testWriteAccess(String URI) {
    	OutputStream out=null;
    	
        // This is bullshit.
        if (URI == null)
            return false;
        if (URI.length() == 0)
            return false;

        try {
            out=new FileOutputStream(URI);
        } catch (Exception e) {
            // Not a file...
            URL u;
			try {
				u = new URL(URI);
			} catch (MalformedURLException e1) {
				return false;
			}
            try {
				out=u.openConnection().getOutputStream();
			} catch (IOException e1) {
				return false;
			}

        }

        if (out!=null) {
        	try {
				out.close();
			} catch (IOException e) {

			}
        	return true;
        }
        // All is well. Go on...
        return true;
    }

    /**
     * Returns true if flags are included in arg. Synonymous with (flags &
     * arg)!=0
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean flags(int flags, int arg) {
        return ((flags & arg) != 0);
    }
    
    public static final boolean flags(long flags, long arg) {
        return ((flags & arg) != 0);
    }

    /**
     * Returns 1 for true and 0 for false. Useful, given the amount of
     * "arithmetic" logical functions in legacy code. Synonymous with
     * (expr?1:0);
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final int eval(boolean expr) {
        return (expr ? 1 : 0);
    }

    /**
     * Returns 1 for non-null and 0 for null objects. Useful, given the amount
     * of "existential" logical functions in legacy code. Synonymous with
     * (expr!=null);
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean eval(Object expr) {
        return (expr != null);
    }

    /**
     * Returns true for expr!=0, false otherwise.
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean eval(int expr) {
        return expr != 0;
    }

    /**
     * Returns true for expr!=0, false otherwise.
     * 
     * @param flags
     * @param arg
     * @return
     */
    public static final boolean eval(long expr) {
        return expr != 0;
    }

    public static final void resetAll(Resettable[] r) {
        for (int i = 0; i < r.length; i++) {
            r[i].reset();
        }
    }

    /**
     * Useful for unquoting strings, since StringTokenizer won't do it for us.
     * Returns null upon any failure.
     * 
     * @param s
     * @param c
     * @return
     */

    public static final String unquote(String s, char c) {

        int firstq = s.indexOf(c);
        int lastq = s.lastIndexOf(c);
        // Indexes valid?
        if (isQuoted(s,c))
                return s.substring(firstq + 1, lastq);
        
        return null;
    }

    public static final boolean isQuoted(String s, char c) {

        int q1 = s.indexOf(c);
        int q2 = s.lastIndexOf(c);
        char c1,c2;
        
        // Indexes valid?
        if (q1 != -1 && q2 != -1) {
            if (q1 < q2) {
                c1=s.charAt(q1);
                c2=s.charAt(q2);
                return (c1==c2);
            }
        }
        
        return false;
    }
    
    public static final String unquoteIfQuoted(String s, char c) {

        String tmp = unquote(s, c);
        if (tmp != null)
            return tmp;
        return s;
    }

    /** Return either 0 or a hashcode 
     * 
     * @param o 
     */
    public static final int pointer(Object o) {
        if (o == null)
            return 0;
        else
            return o.hashCode();
    }

 
    public static final boolean checkForExtension(String filename, String ext) {
        
        // Null filenames satisfy null extensions.
        if ((filename == null || filename.isEmpty())
                && (ext == null || ext.isEmpty()))
            return true;
        
        String separator = System.getProperty("file.separator");

        // Remove the path upto the filename.
        int lastSeparatorIndex = filename.lastIndexOf(separator);
        if (lastSeparatorIndex != -1) {
            filename = filename.substring(lastSeparatorIndex + 1);
        }

        String realext = null;

        // Get extension separator. It SHOULD be . on all platforms, right?
        int pos = filename.lastIndexOf('.');

        if (pos >= 0 && pos <= filename.length() - 2) { // Extension present
            
            // Null comparator on valid extension
            if (ext == null || ext.isEmpty()) return false;
            
            realext = filename.substring(pos + 1);
            return realext.compareToIgnoreCase(ext) == 0;
        } else // No extension, and null/empty comparator
        if (ext == null || ext.isEmpty())
            return true;

        // No extension, and non-null/nonempty comparator.
        return false;
    }    
    
    /** Return the filename without extension, and stripped
     * of the path.
     * 
     * @param s
     * @return
     */
    
    public static final String removeExtension(String s) {

        String separator = System.getProperty("file.separator");
        String filename;

        // Remove the path upto the filename.
        int lastSeparatorIndex = s.lastIndexOf(separator);
        if (lastSeparatorIndex == -1) {
            filename = s;
        } else {
            filename = s.substring(lastSeparatorIndex + 1);
        }

        // Remove the extension.
        int extensionIndex = filename.lastIndexOf(".");
        if (extensionIndex == -1)
            return filename;

        return filename.substring(0, extensionIndex);
    }

    
    /**
     * This method is supposed to return the "name" part of a filename. It was
     * intended to return length-limited (max 8 chars) strings to use as lump
     * indicators. There's normally no need to enforce this behavior, as there's
     * nothing preventing the engine from INTERNALLY using lump names with >8
     * chars. However, just to be sure...
     * 
     * @param path
     * @param limit  Set to any value >0 to enforce a length limit
     * @param whole keep extension if set to true
     * @return
     */

    public static final String extractFileBase(String path, int limit, boolean whole) {
    	
    	if (path==null) return path;
    	
        int src = path.length() - 1;

        String separator = System.getProperty("file.separator");
        src = path.lastIndexOf(separator)+1;

        if (src < 0) // No separator
            src = 0;

        int len = path.lastIndexOf('.');
        if (whole || len<0 ) len=path.length()-src; // No extension.
        else  len-= src;        

        // copy UP to the specific number of characters, or all        
        if (limit > 0) len = Math.min(limit, len);
        
        return path.substring(src, src + len);
    }

    /** Maes: File intead of "inthandle" */

    public static long filelength(File handle) {
        try {
            return handle.length();
        } catch (Exception e) {
            System.err.println("Error fstating");
            return -1;
        }

    }

	@SuppressWarnings("unchecked")
	public static final <T> T[] resize(T[] oldarray, int newsize) {
		
		if (oldarray[0]!=null) return resize(oldarray[0],oldarray,newsize); 
		
		T cls=null;
		try {
			cls = (T) oldarray.getClass().getComponentType().newInstance();
			return resize(cls,oldarray,newsize);
		} catch (Exception e) {
			System.err.println("Cannot autodetect type in resizeArray.\n");
			return null;
		}


	}
    
	/** Generic array resizing method. Calls Arrays.copyOf but then also
	 *  uses initArrayOfObject for the "abundant" elements.
	 * 
	 * @param <T>
	 * @param instance
	 * @param oldarray
	 * @param newsize
	 * @return
	 */
	
	public static final <T> T[] resize(T instance,T[] oldarray, int newsize) {
		
		//  Hmm... nope.
		if (newsize<=oldarray.length) return oldarray;
		
		// Copy old array with built-in stuff.
		T[] tmp =Arrays.copyOf(oldarray,newsize);
		
		// Init the null portions as well
		C2JUtils.initArrayOfObjects(tmp, oldarray.length,tmp.length);

		System.out.printf("Old array of type %s resized. New capacity: %d\n"
				,instance.getClass(),newsize);
		
		return tmp;

	}
	
	/** Resize an array without autoinitialization. Same as Arrays.copyOf(..), just
	 * prints a message.
	 *  
	 * @param <T>
	 * @param oldarray
	 * @param newsize
	 * @return
	 */
	
	public static final <T> T[] resizeNoAutoInit(T[] oldarray, int newsize) {
		
		// For non-autoinit types, this is enough.
		T[] tmp =Arrays.copyOf(oldarray,newsize);
		
		System.out.printf("Old array of type %s resized without auto-init. New capacity: %d\n"
				,tmp.getClass().getComponentType(),newsize);
		
		return tmp;

	}
	
	@SuppressWarnings("unchecked")
	public final static<T> T[] getNewArray(T instance,int size){
		
		T[] os=null;
		Class<T> c=(Class<T>) instance.getClass();
		
        try {
            os = (T[]) Array.newInstance(c, size);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + size
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }
        
        return os;
	}
	
	public final static<T> T[] getNewArray(int size,T instance){
		@SuppressWarnings("unchecked")
		Class<T> c=(Class<T>) instance.getClass();
		return getNewArray(c,size);
	}
	
	@SuppressWarnings("unchecked")
	public final static<T> T[] getNewArray(Class<T> c,int size){
		T[] os=null;
        try {
            os = (T[]) Array.newInstance(c, size);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("Failure to allocate " + size
                    + " objects of class " + c.getName() + "!");
            System.exit(-1);
        }
        
        return os;
	}

    /**
     * Try to guess whether a URI represents a local file, a network any of the
     * above but zipped. Returns
     * 
     * @param URI
     * @return an int with flags set according to InputStreamSugar
     */

    public static int guessResourceType(String URI) {

        int result = 0;
        InputStream in = null;

        // This is bullshit.
        if (URI == null || URI.length() == 0)
            return InputStreamSugar.BAD_URI;

        try {
            in = new FileInputStream(new File(URI));
            // It's a file
            result |= InputStreamSugar.FILE;
        } catch (Exception e) {
            // Not a file...
            URL u;
            try {
                u = new URL(URI);
            } catch (MalformedURLException e1) {
                return InputStreamSugar.BAD_URI;
            }
            try {
                in = u.openConnection().getInputStream();
                result |= InputStreamSugar.NETWORK_FILE;
            } catch (IOException e1) {
                return InputStreamSugar.BAD_URI;
            }

        }

        // Try guessing if it's a ZIP file. A bit lame, really
        // TODO: add proper validation, and maybe MIME type checking
        // for network streams, for cases that we can't really
        // tell from extension alone.

        if (checkForExtension(URI, "zip")) {
            result |= InputStreamSugar.ZIP_FILE;

        }

        try {
            in.close();
        } catch (IOException e) {

        }

        // All is well. Go on...
        return result;
    }

}

package utils;

/** Half-assed way of finding the OS we're running under, shamelessly 
 * ripped from:
 * 
 *  http://www.mkyong.com/java/how-to-detect-os-in-java-systemgetpropertyosname/
 * .
 * This is required, as some things in AWT don't work exactly consistently cross-OS
 * (AWT frame size is the first thing that goes wrong, but also mouse grabbing
 * behavior).
 * 
 * TODO: replace with Apache Commons library?
 *  
 * @author velktron
 *
 */

public class OSValidator{
 
	public static boolean isWindows(){
 
		String os = System.getProperty("os.name").toLowerCase();
		//windows
	    return (os.indexOf( "win" ) >= 0); 
 
	}
 
	public static boolean isMac(){
 
		String os = System.getProperty("os.name").toLowerCase();
		//Mac
	    return (os.indexOf( "mac" ) >= 0); 
 
	}
 
	public static boolean isUnix(){
 
		String os = System.getProperty("os.name").toLowerCase();
		//linux or unix
	    return (os.indexOf( "nix") >=0 || os.indexOf( "nux") >=0);
 
	}
	
	public static boolean isUnknown(){
		return (!isWindows() && !isUnix() && !isMac());
	}
}

package doom;

import defines.skill_t;

/** Groups functions formerly in d_game, 
 * in case you want to provide a different implementation 
 */

public interface IDoomGame {

	 void ExitLevel ();
	 void WorldDone ();

     boolean CheckDemoStatus();
     
     /** Can be called by the startup code or M_Responder.
     A normal game starts at map 1,
     but a warp test can start elsewhere */
     
     public void DeferedInitNew (skill_t skill, int episode, int map);
     
     /** Can be called by the startup code or M_Responder,
     calls P_SetupLevel or W_EnterWorld. */
   public void LoadGame (String name);

   /** Called by M_Responder. */
   public void SaveGame (int slot, String description);
   
   /** Takes a screenshot *NOW*
    * 
    */
   public void ScreenShot() ;
   
   public void StartTitle();
   
   public gameaction_t getGameAction();

   public void setGameAction(gameaction_t ga);
   
   // public void PlayerReborn(int player);
   
   void DeathMatchSpawnPlayer(int playernum);

}

package doom;

public interface NetConsts {

    public static int    NCMD_EXIT=      0x80000000;
    public static int    NCMD_RETRANSMIT     =0x40000000;
    public static int    NCMD_SETUP      =0x20000000;
    public static int   NCMD_KILL    =   0x10000000; // kill game
    public static int    NCMD_CHECKSUM   =   0x0fffffff;

    public static int  DOOMCOM_ID =     0x12345678;


    //Networking and tick handling related. Moved to DEFINES
    //protected static int    BACKUPTICS     = 12;


    // command_t
    public  static short CMD_SEND    = 1;
    public  static short CMD_GET = 2; 
    
}

package doom;

import n.DoomSystemNetworking;
import f.EndLevel;
import f.Finale;
import f.Wiper;
import hu.HU;
import i.IDiskDrawer;
import i.IDoomSystem;
import i.DoomVideoInterface;
import m.IDoomMenu;
import m.IRandom;
import m.IVariablesManager;
import p.AbstractLevelLoader;
import p.Actions;
import rr.ISpriteManager;
import rr.Renderer;
import rr.TextureManager;
import s.IDoomSound;
import s.IMusic;
import s.ISoundDriver;
import st.AbstractStatusBar;
import timing.ITicker;
import v.DoomVideoRenderer;
import w.IWadLoader;
import automap.IAutoMap;

/** Since a lot of stuff requires shared/global access to 
 *  the WadLoader, the Renderer, the Video system etc. and
 *  we're trying to depart from the global/static mentality,
 *  a common sharing is required. Ideally, this would be a perfect
 *  example of where multiple inheritance could be adopted, since most
 *  stuff needs to share this status anyway. The next best thing is
 *  to have local references of any used fields in the classes that use them.
 * 
 *  About generics: T refers to the type of the graphics resources, and is
 *  currently byte[], as all graphics resources are 8-bit indexed. There are
 *  no plans that this will change anytime soon. Some classes should allow 
 *  different types in theory, but it would be too complex and pointless to 
 *  make everything fully compliant at the moment.
 *  
 *  V refers to the type of DISPLAY, and can be 8-bit (byte[]), 16-bit (short[] 
 *  for HiColor and lesser modes such as ARGB4444, etc.), and, in the future, 
 *  int[] (truecolor).
 * 
 *  The general approach is sharing as much code as possible between different 
 *  implementations (e.g. rendering code), and only specialize methods/classes when
 *  the abstraction of generics isn't enough (typically, when you have to assign
 *  directly to primitive arrays or deal with primitive method signatures).
 *  
 *  Classes that have specialized code for indexed and hicolor modes should be top-level
 *  classes in their package, and contain two nested, static, extending classes called
 *  Indexed and HiColor e.g. new MyClass.Indexed() and new MyClass.HiColor(), while any common 
 *  code should reside in MyClass.
 * 
 * @author velktron
 *
 */

public class DoomContext<T,V>{
	
    public DoomMain<T,V> DM;
    public IDoomGame DG;
    public IWadLoader W;
    public IRandom RND;
    public IDoomSystem I;
    public IDoomSound S;
    public ISoundDriver ISND;
    public IMusic IMUS;
    public DoomVideoInterface<V> VI;
    public AbstractStatusBar ST;
    public DoomVideoRenderer<T,V> V;
    public DoomSystemNetworking DNI;
    public IDoomGameNetworking DGN;
    public AbstractLevelLoader LL;
    public IDoomMenu M;
    public Actions P;
    public Renderer<T,V> R;
    public HU HU;
    public IAutoMap<T,V> AM;
    public Finale<T> F;
    public EndLevel<T,V> WI;
    public Wiper<T,V> WIPE;
    public TextureManager<T> TM;
    public ISpriteManager SM;
    public ICommandLineManager CM;
    public ITicker TICK; 
    public IDiskDrawer DD;
    public IVariablesManager VM;
}

package doom;

import java.util.Hashtable;

import utils.C2JUtils;

import defines.GameMode_t;

public class DoomVersions {

	public final static Hashtable<String,GameMode_t> VersionChecker=new Hashtable<String,GameMode_t>();
	
	static {
		VersionChecker.put("doom.wad",GameMode_t.registered);
		VersionChecker.put("doom2.wad",GameMode_t.commercial);
		VersionChecker.put("udoom.wad",GameMode_t.retail);
		VersionChecker.put("tnt.wad",GameMode_t.pack_tnt);
		VersionChecker.put("plutonia.wad",GameMode_t.pack_plut);
		VersionChecker.put("doom1.wad",GameMode_t.shareware);
		VersionChecker.put("xbla.wad",GameMode_t.pack_xbla);
	}
	
	public DoomVersions(){
		
	}
	
	/** Try all versions in given doommwaddir
	 * 
	 */
	
	public void tryThemAll(String doomwaddir){
		 // Commercial.
        doom2wad = (doomwaddir+ "/doom2.wad");

        // Retail.
        doomuwad = (doomwaddir+ "/doomu.wad");    

        // Registered.
        doomwad = (doomwaddir+ "/doom.wad");

        // Shareware.
        doom1wad = (doomwaddir+ "/doom1.wad");

        // Bug, dear Shawn.
        // Insufficient malloc, caused spurious realloc errors.
        plutoniawad = (doomwaddir+ "/plutonia.wad");

        tntwad = (doomwaddir+ "/tnt.wad");

        xblawad = (doomwaddir+ "/xbla.wad");
        
        // French stuff.
        doom2fwad=(doomwaddir+ "/doom2f.wad");
	}
	
	public String 
	doom1wad,
    doomwad,
    doomuwad,
    doom2wad,
    doom2fwad,
    plutoniawad,    
    tntwad,
    xblawad;
	
	/** Try only one IWAD. 
	 * 
	 * @param iwad
	 * @return
	 */

	public GameMode_t tryOnlyOne(String iwad, String doomwaddir) {
		
		// Is it a known and valid version?
		GameMode_t tmp=VersionChecker.get(iwad.toLowerCase());
		if (tmp!=null) {
		// Can we read it?
		if (C2JUtils.testReadAccess(doomwaddir+iwad))
			return tmp; // Yes, so communicate the gamemode back.
		}
		
		// It's either invalid or we can't read it.
		// Fuck that.
		return null;
	}
	
	
}

package doom;

/** Meant to provide a more lightweight alternative to Java's serialization model,
 *  specifically for the purpose of sending
 *  Objects implementing this can return references to one same byte array, with minimal
 *  overhead. Since it's for send-only purposes, it won't matter if it's  modified.
 *   *  
 *  But don't use it in lieu of CacheableDoomObject! 
 * 
 * @author admin
 *
 */

public interface IDatagramSerializable {
    
        /** Packs object into a byte array suitable to send over 
         * datagram networks. Typically, objects cache this array
         * for later use, and is availabe through cached()
         * 
         * @return
         */
    
        public byte[] pack();
        
        /** Packs object into a byte array suitable to send over 
         * datagram networks. The array is supplied externally 
         * (good for daisy-chaining stuff into a single packet).
         * 
         * @return
         */
    
        public void pack(byte[] buf, int offset);
        
        /** Deserializes an object from a given byte buffer.
         *  Only the first (sizeof) bytes will be used, dependant
         *  on each object's implementation. Will NOT also copy
         *  the byte[] caches. 
         */
        
        public void unpack(byte[] buf);
        
        /** Deserializes an object from a given byte buffer.
         *  Only the first (sizeof) bytes will be used, starting
         *  from a specified offset, dependant on each object's 
         *  implementation.
         */
        public void unpack(byte[] buf, int offset);
        
        /** Only use this if you are 100% sure that the object's content
         *  won't have changed since the last call of pack(). 
         *
         * @return Should return the underlying byte[] array directly.
         */
        public byte[] cached();
       
}

package doom;

// Event structure.

public class event_t {
	
	public event_t(){
	}

	   public event_t(event_t t){
	        this.type = t.type;
	        this.data1 = t.data1;
	        this.data2 = t.data2;
	        this.data3 = t.data3;
	    }

	
    public event_t(evtype_t type, int data) {
        this.type = type;
        this.data1 = data;
    }

    public event_t(char c) {
        this.type = evtype_t.ev_keydown;
        this.data1 = c;
    }

    public event_t(evtype_t type, int data1, int data2, int data3) {
        this.type = type;
        this.data1 = data1;
        this.data2 = data2;
        this.data3 = data3;
    }
    
    public void setFrom(event_t t){
        this.type = t.type;
        this.data1 = t.data1;
        this.data2 = t.data2;
        this.data3 = t.data3;
    }

    public evtype_t type;

    /** keys / mouse/joystick buttons, or wheel rotation amount */
    public int data1;

    /** mouse/joystick x move */
    public int data2;

    /** mouse/joystick y move */
    public int data3;

};

package doom;

import static data.Defines.*;
import static g.Keys.*;
import static data.Limits.*;
import java.io.OutputStreamWriter;

import automap.IAutoMap;
import m.IUseVariables;
import m.IVariablesManager;
import m.Settings;
import p.mobj_t;
import rr.Renderer;
import utils.C2JUtils;
import v.DoomVideoRenderer;
import data.mapthing_t;
import defines.*;
import demo.IDoomDemo;
import f.Finale;
import f.Wiper;

/**
 * We need globally shared data structures, for defining the global state
 * variables. MAES: in pure OO style, this should be a global "Doom state"
 * object to be passed along various modules. No ugly globals here!!! Now, some
 * of the variables that appear here were actually defined in separate modules.
 * Pretty much, whatever needs to be shared with other modules was placed here,
 * either as a local definition, or as an extern share. The very least, I'll
 * document where everything is supposed to come from/reside.
 */

public abstract class DoomStatus<T,V> extends DoomContext<T,V> implements IUseVariables {

	public static final int	BGCOLOR=		7;
	public static final int	FGCOLOR		=8;
	public static int   RESENDCOUNT =10;
	public static int   PL_DRONE    =0x80;  // bit flag in doomdata->player

	public String[]		wadfiles=new String[MAXWADFILES];

	boolean         drone;
	
    /** Command line parametersm, actually defined in d_main.c */
    public boolean nomonsters; // checkparm of -nomonsters

    public boolean respawnparm; // checkparm of -respawn

    public boolean fastparm; // checkparm of -fast

    public boolean devparm; // DEBUG: launched with -devparm

 // MAES: declared as "extern", shared with Menu.java
    public  boolean	inhelpscreens;

    boolean		advancedemo;
    
    /////////// Local to doomstat.c ////////////
    // TODO: hide those behind getters
    
    /** Game Mode - identify IWAD as shareware, retail etc. 
     *  This is now hidden behind getters so some cases like plutonia
     *  etc. can be handled more cleanly.
     * */

    private GameMode_t gamemode;
    
    public void setGameMode(GameMode_t mode){
    	this.gamemode=mode;
    }
    
    public GameMode_t getGameMode(){
    	return gamemode;
    }
    
    public boolean isShareware(){
    	return (gamemode== GameMode_t.shareware);
    }    
    
    /** Commercial means Doom 2, Plutonia, TNT, and possibly others like XBLA.
     * 
     * @return
     */
    public boolean isCommercial(){
    	return (gamemode== GameMode_t.commercial ||
    			gamemode== GameMode_t.pack_plut ||
    			gamemode== GameMode_t.pack_tnt ||
    			gamemode== GameMode_t.pack_xbla);
    }
    
    /** Retail means Ultimate.
     * 
     * @return
     */
    public boolean isRetail(){
    	return (gamemode== GameMode_t.retail );
    }
    
    /** Registered is a subset of Ultimate 
     * 
     * @return
     */

    public boolean isRegistered(){
    	return (gamemode== GameMode_t.registered || gamemode== GameMode_t.retail );
    }
    
    public GameMission_t gamemission;

    /** Set if homebrew PWAD stuff has been added. */
    public boolean modifiedgame;

    /** Language. */
    public Language_t language;

    // /////////// Normally found in d_main.c ///////////////

    // Selected skill type, map etc.

    /** Defaults for menu, methinks. */
    public skill_t startskill;

    public int startepisode;

    public int startmap;

    public boolean autostart;

    /** Selected by user */
    public skill_t gameskill;

    public int gameepisode;

    public int gamemap;

    /** Nightmare mode flag, single player. */
    public boolean respawnmonsters;

    /** Netgame? Only true if >1 player. */
    public boolean netgame;

    /**
     * Flag: true only if started as net deathmatch. An enum might handle
     * altdeath/cooperative better. Use altdeath for the "2" value
     */
    public boolean deathmatch;

    /** Use this instead of "deathmatch=2" which is bullshit. */
    public boolean altdeath;
    
    //////////// STUFF SHARED WITH THE RENDERER ///////////////
    
    // -------------------------
    // Status flags for refresh.
    //

    public boolean nodrawers;

    public boolean noblit;
    
    public boolean viewactive;
    
    // Player taking events, and displaying.
    public int consoleplayer;

    public int displayplayer;
    
    // Depending on view size - no status bar?
    // Note that there is no way to disable the
    // status bar explicitely.
    public boolean statusbaractive;

    public boolean automapactive; // In AutoMap mode?

    public boolean menuactive; // Menu overlayed?

    public boolean paused; // Game Pause?

    // -------------------------
    // Internal parameters for sound rendering.
    // These have been taken from the DOS version,
    // but are not (yet) supported with Linux
    // (e.g. no sound volume adjustment with menu.

    // These are not used, but should be (menu).
    // From m_menu.c:
    // Sound FX volume has default, 0 - 15
    // Music volume has default, 0 - 15
    // These are multiplied by 8.
    /** maximum volume for sound */
    public int snd_SfxVolume;

    /** maximum volume for music */
    public int snd_MusicVolume;

    /** Maximum number of sound channels */
    public int numChannels;

    // Current music/sfx card - index useless
    // w/o a reference LUT in a sound module.
    // Ideally, this would use indices found
    // in: /usr/include/linux/soundcard.h
    public int snd_MusicDevice;

    public int snd_SfxDevice;

    // Config file? Same disclaimer as above.
    public int snd_DesiredMusicDevice;

    public int snd_DesiredSfxDevice;

    
    // -------------------------------------
    // Scores, rating.
    // Statistics on a given map, for intermission.
    //
    public int totalkills;

    public int totalitems;

    public int totalsecret;

    /** TNTHOM "cheat" for flashing HOM-detecting BG */
	public boolean flashing_hom;
    
    // Added for prBoom+ code
    public int totallive;
    
    // Timer, for scores.
    public int levelstarttic; // gametic at level start

    public int leveltime; // tics in game play for par

    // --------------------------------------
    // DEMO playback/recording related stuff.
    // No demo, there is a human player in charge?
    // Disable save/end game?
    public boolean usergame;

    // ?
    public boolean demoplayback;

    public boolean demorecording;

    // Quit after playing a demo from cmdline.
    public boolean singledemo;

    /** Set this to GS_DEMOSCREEN upon init, else it will be null*/
    public gamestate_t gamestate=gamestate_t.GS_DEMOSCREEN;

    // -----------------------------
    // Internal parameters, fixed.
    // These are set by the engine, and not changed
    // according to user inputs. Partly load from
    // WAD, partly set at startup time.

    public int gametic;

    // Bookkeeping on players - state.
    public player_t[] players;

    // Alive? Disconnected?
    public boolean[] playeringame = new boolean[MAXPLAYERS];

    public mapthing_t[] deathmatchstarts = new mapthing_t[MAX_DM_STARTS];

    /** pointer into deathmatchstarts */
    public int deathmatch_p;

    /** Player spawn spots. */
    public mapthing_t[] playerstarts = new mapthing_t[MAXPLAYERS];

    /** Intermission stats.
      Parameters for world map / intermission. */
    public wbstartstruct_t wminfo;

    /** LUT of ammunition limits for each kind.
        This doubles with BackPack powerup item.
        NOTE: this "maxammo" is treated like a global.
         */
    public final static int[] maxammo =  {200, 50, 300, 50};

    // -----------------------------------------
    // Internal parameters, used for engine.
    //

    // File handling stuff.
    
    public OutputStreamWriter debugfile;

    // if true, load all graphics at level load
    public boolean precache;

    // wipegamestate can be set to -1
    // to force a wipe on the next draw
    // wipegamestate can be set to -1 to force a wipe on the next draw
    public gamestate_t     wipegamestate = gamestate_t.GS_DEMOSCREEN;
    
    public int mouseSensitivity=15;
    
    // debug flag to cancel adaptiveness
    // Set to true during timedemos.
    public boolean singletics=false;
    
    /* A "fastdemo" is a demo with a clock that tics as
     * fast as possible, yet it maintains adaptiveness and doesn't
     * try to render everything at all costs.
     */
    protected boolean fastdemo;
    protected boolean normaldemo;
    
    protected String loaddemo;
    
    public int bodyqueslot;

    // Needed to store the number of the dummy sky flat.
    // Used for rendering,
    // as well as tracking projectiles etc.
    //public int skyflatnum;

    // TODO: Netgame stuff (buffers and pointers, i.e. indices).

    // TODO: This is ???
    public doomcom_t doomcom;

    // TODO: This points inside doomcom.
    public doomdata_t netbuffer;

    public ticcmd_t[] localcmds = new ticcmd_t[BACKUPTICS];

    public int rndindex;

    public int maketic;

    public int[] nettics = new int[MAXNETNODES];

    public ticcmd_t[][] netcmds;// [MAXPLAYERS][BACKUPTICS];
      
    /** MAES: this WAS NOT in the original. 
     *  Remember to call it!
     */
    protected void initNetGameStuff() {
        //this.netbuffer = new doomdata_t();
        this.doomcom = new doomcom_t();
        this.netcmds = new ticcmd_t[MAXPLAYERS][BACKUPTICS];

        C2JUtils.initArrayOfObjects(localcmds);
        for (int i=0;i<MAXPLAYERS;i++){
        C2JUtils.initArrayOfObjects(netcmds[i]);
        }
        
    }
    
    public abstract void Init();

    // Fields used for selecting variable BPP implementations.
    
    protected abstract Finale<T> selectFinale();
    
    protected abstract DoomVideoRenderer<T,V> selectVideoRenderer();
    
    protected abstract Renderer<T,V> selectRenderer();
    
    protected abstract Wiper<T,V> selectWiper();
    
    protected abstract IAutoMap<T,V> selectAutoMap();
    
    // MAES: Fields specific to DoomGame. A lot of them were
    // duplicated/externalized
    // in d_game.c and d_game.h, so it makes sense adopting a more unified
    // approach.
    protected gameaction_t gameaction=gameaction_t.ga_nothing;

    public boolean sendpause; // send a pause event next tic

    protected boolean sendsave; // send a save event next tic

    protected int starttime;

    protected boolean timingdemo; // if true, exit with report on completion

    public boolean getPaused() {
        return paused;
    }

    public void setPaused(boolean paused) {
        this.paused = paused;
    }

    // ////////// DEMO SPECIFIC STUFF/////////////

    protected String demoname;

    protected boolean netdemo;

    //protected IDemoTicCmd[] demobuffer;

    protected IDoomDemo demobuffer;
    /** pointers */
    // USELESS protected int demo_p;

   // USELESS protected int demoend;

    protected short[][] consistancy = new short[MAXPLAYERS][BACKUPTICS];

    protected byte[] savebuffer;

    /* TODO Proper reconfigurable controls. Defaults hardcoded for now. T3h h4x, d00d. */

    public int key_right=KEY_RIGHTARROW;

    public int key_left=KEY_LEFTARROW;

    public int key_up='w';

    public int key_down='a';

    public int key_strafeleft='s';

    public int key_straferight='d';

    public int key_fire=KEY_CTRL;

    public int key_use=' ';
    
    public int key_strafe=KEY_ALT;

    public int key_speed=KEY_SHIFT;
    
    // Heretic stuff
    public int		key_lookup=KEY_PGUP;
    public int		key_lookdown=KEY_PGDN;
    public int      key_lookcenter=KEY_END;

    public int mousebfire=0;

    public int mousebstrafe=1;

    public int mousebforward=2;

    public int joybfire;

    public int joybstrafe;

    public int joybuse;

    public int joybspeed;
    
    /** Cancel vertical mouse movement by default */
    protected boolean novert=true;

    protected int MAXPLMOVE() {
        return forwardmove[1];
    }

    protected static final int TURBOTHRESHOLD = 0x32;

    /** fixed_t */
    protected final int[] forwardmove = { 0x19, 0x32 }; // + slow turn

    protected final int[] sidemove = { 0x18, 0x28 };

    protected final int[] angleturn = { 640, 1280, 320 };

    protected static final int SLOWTURNTICS = 6;

    protected static final int NUMKEYS = 256;

    protected boolean[] gamekeydown = new boolean[NUMKEYS];

    protected boolean keysCleared;
    
    public boolean alwaysrun;

    protected int turnheld; // for accelerative turning
    protected int lookheld; // for accelerative looking?

    protected boolean[] mousearray = new boolean[4];

    /** This is an alias for mousearray [1+i] */
    protected boolean mousebuttons(int i) {
        return mousearray[1 + i]; // allow [-1]
    }

    protected void mousebuttons(int i, boolean value) {
        mousearray[1 + i] = value; // allow [-1]
    }

    protected void mousebuttons(int i, int value) {
        mousearray[1 + i] = value != 0; // allow [-1]
    }

    /** mouse values are used once */
    protected int mousex, mousey;

    protected int dclicktime;

    protected int dclickstate;

    protected int dclicks;

    protected int dclicktime2, dclickstate2, dclicks2;

    /** joystick values are repeated */
    protected int joyxmove, joyymove;

    protected boolean[] joyarray = new boolean[5];

    protected boolean joybuttons(int i) {
        return joyarray[1 + i]; // allow [-1]
    }

    protected void joybuttons(int i, boolean value) {
        joyarray[1 + i] = value; // allow [-1]
    }

    protected void joybuttons(int i, int value) {
        joyarray[1 + i] = value != 0; // allow [-1]
    }

    protected int savegameslot;

    protected String savedescription;

    protected static final int BODYQUESIZE = 32;

    protected mobj_t[] bodyque = new mobj_t[BODYQUESIZE];

    public String statcopy; // for statistics driver

    
    
    /** Not documented/used in linuxdoom. I supposed it could be used to
     *  ignore mouse input?
     */
    
    public boolean use_mouse,use_joystick;
    
    
    /** More prBoom+ stuff. Used mostly for code uhm..reuse, rather
     *  than to actually change the way stuff works.
     *   
     */
    
    public static int compatibility_level;
    
    public DoomStatus(){
    	players = new player_t[MAXPLAYERS];
    	C2JUtils.initArrayOfObjects(players);

    	this.wminfo=new wbstartstruct_t();
    	initNetGameStuff();
    }

    @Override
    public void registerVariableManager(IVariablesManager manager) {
        this.VM=manager;        
    }

    @Override
    public void update() {

           this.snd_SfxVolume=VM.getSetting(Settings.sfx_volume).getInteger();
           this.snd_MusicVolume=VM.getSetting(Settings.music_volume).getInteger();
           this.alwaysrun=VM.getSetting(Settings.alwaysrun).getBoolean();
                     
          
           // Keys...
           this.key_right=VM.getSetting(Settings.key_right).getChar();
           this.key_left=VM.getSetting(Settings.key_left).getChar();
           this.key_up=VM.getSetting(Settings.key_up).getChar();
           this.key_down=VM.getSetting(Settings.key_down).getChar();
           this.key_strafeleft=VM.getSetting(Settings.key_strafeleft).getChar();
           this.key_straferight=VM.getSetting(Settings.key_straferight).getChar();
           this.key_fire=VM.getSetting(Settings.key_fire).getChar();
           this.key_use=VM.getSetting(Settings.key_use).getChar();
           this.key_strafe=VM.getSetting(Settings.key_strafe).getChar();
           this.key_speed=VM.getSetting(Settings.key_speed).getChar();
           

           // Mouse buttons
           this.use_mouse=VM.getSetting(Settings.use_mouse).getBoolean();
           this.mousebfire=VM.getSetting(Settings.mouseb_fire).getInteger();
           this.mousebstrafe=VM.getSetting(Settings.mouseb_strafe).getInteger();
           this.mousebforward=VM.getSetting(Settings.mouseb_forward).getInteger();
           
           // Joystick

           this.use_joystick=VM.getSetting(Settings.use_joystick).getBoolean();
           this.joybfire=VM.getSetting(Settings.joyb_fire).getInteger();
           this.joybstrafe=VM.getSetting(Settings.joyb_strafe).getInteger();
           this.joybuse=VM.getSetting(Settings.joyb_use).getInteger();
           this.joybspeed=VM.getSetting(Settings.joyb_speed).getInteger();

           // Sound
           this.numChannels=VM.getSetting(Settings.snd_channels).getInteger();

           // Video...so you should wait until video renderer is active.           
           this.V.setUsegamma(VM.getSetting(Settings.usegamma).getInteger());
           
           // These should really be handled by the menu.
           this.M.setShowMessages(VM.getSetting(Settings.show_messages).getBoolean());
           this.M.setScreenBlocks(VM.getSetting(Settings.screenblocks).getInteger());

           // These should be handled by the HU

           for (int i=0;i<=9;i++){
               
           String chatmacro=String.format("chatmacro%d",i);
           this.HU.setChatMacro(i,VM.getSetting(chatmacro).toString());
           }
        }

    @Override
    public void commit() {
        VM.putSetting(Settings.sfx_volume,this.snd_SfxVolume);
        VM.putSetting(Settings.music_volume,this.snd_MusicVolume);
        VM.putSetting(Settings.alwaysrun, this.alwaysrun);
       
       
        // Keys...
        VM.putSetting(Settings.key_right,this.key_right);
        VM.putSetting(Settings.key_left,this.key_left);
        VM.putSetting(Settings.key_up,this.key_up);
        VM.putSetting(Settings.key_down,this.key_down);
        VM.putSetting(Settings.key_strafeleft,this.key_strafeleft);
        VM.putSetting(Settings.key_straferight,this.key_straferight);
        VM.putSetting(Settings.key_fire,this.key_fire);
        VM.putSetting(Settings.key_use,this.key_use);
        VM.putSetting(Settings.key_strafe,this.key_strafe);
        VM.putSetting(Settings.key_speed,this.key_speed);
        

        // Mouse buttons
        VM.putSetting(Settings.use_mouse,this.use_mouse);
        VM.putSetting(Settings.mouseb_fire,this.mousebfire);
        VM.putSetting(Settings.mouseb_strafe,this.mousebstrafe);
        VM.putSetting(Settings.mouseb_forward,this.mousebforward);
        
        // Joystick

        VM.putSetting(Settings.use_joystick,this.use_joystick);
        VM.putSetting(Settings.joyb_fire,this.joybfire);
        VM.putSetting(Settings.joyb_strafe,this.joybstrafe);
        VM.putSetting(Settings.joyb_use,this.joybuse);
        VM.putSetting(Settings.joyb_speed,this.joybspeed);

        // Sound
        VM.putSetting(Settings.snd_channels,this.numChannels);
        
        // Video...         
        VM.putSetting(Settings.usegamma,V.getUsegamma());
        
        // These should really be handled by the menu.
        VM.putSetting(Settings.show_messages,this.M.getShowMessages());
        VM.putSetting(Settings.screenblocks,this.M.getScreenBlocks());
        
        // These should be handled by the HU

        for (int i=0;i<=9;i++){
            
        String chatmacro=String.format("chatmacro%d",i);
        VM.putSetting(chatmacro,this.HU.chat_macros[i]);
        }
        
    }
    

    

}

// $Log: DoomStatus.java,v $
// Revision 1.36  2012/11/06 16:04:58  velktron
// Variables manager less tightly integrated.
//
// Revision 1.35  2012/09/24 17:16:22  velktron
// Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD.
//
// Revision 1.34.2.3  2012/09/24 16:58:06  velktron
// TrueColor, Generics.
//
// Revision 1.34.2.2  2012/09/20 14:25:13  velktron
// Unified DOOM!!!
//
// Revision 1.34.2.1  2012/09/17 16:06:52  velktron
// Now handling updates of all variables, though those specific to some subsystems should probably be moved???
//
// Revision 1.34  2011/11/01 23:48:10  velktron
// Added tnthom stuff.
//
// Revision 1.33  2011/10/24 02:11:27  velktron
// Stream compliancy
//
// Revision 1.32  2011/10/07 16:01:16  velktron
// Added freelook stuff, using Keys.
//
// Revision 1.31  2011/09/27 16:01:41  velktron
// -complevel_t
//
// Revision 1.30  2011/09/27 15:54:51  velktron
// Added some more prBoom+ stuff.
//
// Revision 1.29  2011/07/28 17:07:04  velktron
// Added always run hack.
//
// Revision 1.28  2011/07/16 10:57:50  velktron
// Merged finnw's changes for enabling polling of ?_LOCK keys.
//
// Revision 1.27  2011/06/14 20:59:47  velktron
// Channel settings now read from default.cfg. Changes in sound creation order.
//
// Revision 1.26  2011/06/04 11:04:25  velktron
// Fixed registered/ultimate identification.
//
// Revision 1.25  2011/06/01 17:35:56  velktron
// Techdemo v1.4a level. Default novert and experimental mochaevents interface.
//
// Revision 1.24  2011/06/01 00:37:58  velktron
// Changed default keys to WASD.
//
// Revision 1.23  2011/05/31 21:45:51  velktron
// Added XBLA version as explicitly supported.
//
// Revision 1.22  2011/05/30 15:50:42  velktron
// Changed to work with new Abstract classes
//
// Revision 1.21  2011/05/26 17:52:11  velktron
// Now using ICommandLineManager
//
// Revision 1.20  2011/05/26 13:39:52  velktron
// Now using ICommandLineManager
//
// Revision 1.19  2011/05/25 17:56:52  velktron
// Introduced some fixes for mousebuttons etc.
//
// Revision 1.18  2011/05/24 17:44:37  velktron
// usemouse added for defaults
//
package doom;

import i.DiskDrawer;
import i.DoomStatusAware;
import i.DoomSystem;
import i.DoomVideoInterface;
import i.Strings;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import n.DummyNetworkDriver;
import static data.dstrings.*;
import p.Actions;
import p.BoomLevelLoader;
import p.mobj_t;
import automap.IAutoMap;
import automap.Map;
import awt.AWTDoom;
import f.EndLevel;
import f.Finale;
import f.Wiper;
import hu.HU;
import m.Menu;
import m.MenuMisc;
import m.DoomRandom;
import m.Settings;
import m.VarsManager;
import static doom.NetConsts.*;
import static doom.englsh.*;
import data.Tables;
import data.dstrings;
import data.mapthing_t;
import data.mobjtype_t;
import defines.*;
import demo.IDemoTicCmd;
import demo.IDoomDemo;
import demo.VanillaDoomDemo;
import demo.VanillaTiccmd;
import data.sounds.musicenum_t;
import data.sounds.sfxenum_t;
import static data.Defines.BACKUPTICS;
import static g.Keys.*;
import static data.Defines.NORMALUNIX;
import static data.Defines.PU_STATIC;
import static data.Defines.VERSION;
import rr.Renderer;
import rr.SimpleTextureManager;
import rr.SpriteManager;
import rr.UnifiedRenderer;
import rr.ViewVars;
import rr.subsector_t;
import rr.parallel.ParallelRenderer;
import rr.parallel.ParallelRenderer2;
import s.AbstractDoomAudio;
import s.ClassicDoomSoundDriver;
import s.ClipSFXModule;
import s.DavidMusicModule;
import s.DavidSFXModule;
import s.DummyMusic;
import s.DummySFX;
import s.DummySoundDriver;
import s.SpeakerDoomSoundDriver;
import s.SuperDoomSoundDriver;
//import s.SpeakerDoomSoundDriver;

import savegame.IDoomSaveGame;
import savegame.IDoomSaveGameHeader;
import savegame.VanillaDSG;
import savegame.VanillaDSGHeader;
import st.StatusBar;
import timing.FastTicker;
import timing.ITicker;
import timing.MilliTicker;
import timing.NanoTicker;
import utils.C2JUtils;
import v.BufferedRenderer;
import v.BufferedRenderer16;
import v.BufferedRenderer32;
import v.ColorTint;
import v.DoomVideoRenderer;
import v.GammaTables;
import v.IVideoScale;
import v.IVideoScaleAware;
import v.PaletteGenerator;
//import v.VideoScaleInfo;
import v.VisualSettings;
import w.DoomBuffer;
import w.WadLoader;
import static data.Defines.*;
import static data.Limits.*;
import static data.Tables.*;
import static data.dstrings.SAVEGAMENAME;
import static data.info.mobjinfo;
import static data.info.states;
import static m.fixed_t.FRACBITS;
import static m.fixed_t.MAPFRACUNIT;
import static utils.C2JUtils.*;

// Emacs style mode select   -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id: DoomMain.java,v 1.110 2016/06/06 22:21:24 velktron Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//	DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
//	plus functions to determine game mode (shareware, registered),
//	parse command line parameters, configure game parameters (turbo),
//	and call the startup functions.
//
//  In Mocha Doom, this was unified with d_game and doomstat.c
//
//-----------------------------------------------------------------------------

public abstract class DoomMain<T,V> extends DoomStatus<T,V> implements IDoomGameNetworking, IDoomGame, IDoom, IVideoScaleAware{

    public static final String rcsid = "$Id: DoomMain.java,v 1.110 2016/06/06 22:21:24 velktron Exp $";

    //
    // EVENT HANDLING
    //
    // Events are asynchronous inputs generally generated by the game user.
    // Events can be discarded if no responder claims them
    //
    public event_t[]         events=new event_t[MAXEVENTS];
    public int             eventhead;
    public int 		eventtail;

    /**
     * D_PostEvent
     * Called by the I/O functions when input is detected
     */

    public void PostEvent (event_t ev)
    {
        // TODO create a pool of reusable messages?
        // NEVERMIND we can use the original system.
        events[eventhead].setFrom(ev);
        eventhead = (++eventhead)&(MAXEVENTS-1);
    }


    /**
     * D_ProcessEvents
     * Send all the events of the given timestamp down the responder chain
     */ 
    public void ProcessEvents ()
    {
        event_t	ev;

        // IF STORE DEMO, DO NOT ACCEPT INPUT

        if ( ( isCommercial() )
                && (W.CheckNumForName("MAP01")<0) )
            return; 

        for ( ; eventtail != eventhead ; eventtail = (++eventtail)&(MAXEVENTS-1) )
        {
            ev = events[eventtail];
            if (M.Responder (ev)){
                //epool.checkIn(ev);
                continue;               // menu ate the event
            }
            Responder (ev);
            // We're done with it, return it to the pool.
            //epool.checkIn(ev);
        }
    }

    // "static" to Display, don't move.
    private  boolean		viewactivestate = false;
    private  boolean		menuactivestate = false;
    private  boolean		inhelpscreensstate = false;
    private  boolean		fullscreen = false;
    private  gamestate_t	oldgamestate = gamestate_t.GS_MINUS_ONE;
    private  int			borderdrawcount;

    /**
     * D_Display
     * draw current display, possibly wiping it from the previous
     * @throws IOException 
     */

    public void Display () throws IOException
    {
        int				nowtime;
        int				tics;
        int				wipestart;
        int				y;
        boolean			done;
        boolean			wipe;
        boolean			redrawsbar;

        if (nodrawers)
            return;                    // for comparative timing / profiling

        redrawsbar = false;

        // change the view size if needed
        if (R.getSetSizeNeeded())
        {
            R.ExecuteSetViewSize ();
            oldgamestate = gamestate_t.GS_MINUS_ONE;                      // force background redraw
            borderdrawcount = 3;
        }

        // save the current screen if about to wipe
        if (wipe=(gamestate != wipegamestate))
        {
            wipe = true;
            WIPE.StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);
        }
        else
            wipe = false;

        if (gamestate == gamestate_t.GS_LEVEL && eval(gametic))
            HU.Erase();

        // do buffered drawing
        switch (gamestate)
        {
        case GS_LEVEL:
            if (!eval(gametic))
                break;
            if (automapactive)
                AM.Drawer ();
            if (wipe || (!R.isFullHeight() && fullscreen) ||
            		(inhelpscreensstate && !inhelpscreens)
            		|| (DD.justDoneReading()))
                redrawsbar = true; // just put away the help screen
            ST.Drawer (R.isFullHeight(), redrawsbar );
            fullscreen = R.isFullHeight();
            break;

        case GS_INTERMISSION:
            WI.Drawer ();
            break;

        case GS_FINALE:
            F.Drawer ();
            break;

        case GS_DEMOSCREEN:
            PageDrawer ();
            break;
        }

        // draw the view directly
        if (gamestate == gamestate_t.GS_LEVEL && !automapactive && eval(gametic)){
        	if (flashing_hom){
                V.FillRect(gametic%256,0,view.getViewWindowX(),view.getViewWindowY(),
                    view.getScaledViewWidth(),view.getScaledViewHeight());
        	    }
            R.RenderPlayerView (players[displayplayer]);
        	}

        // Automap was active, update only HU.    
        if (gamestate == gamestate_t.GS_LEVEL && eval(gametic))
            HU.Drawer ();

        // clean up border stuff
        if (gamestate != oldgamestate && gamestate != gamestate_t.GS_LEVEL)
            VI.SetPalette (0);

        // see if the border needs to be initially drawn
        if (gamestate == gamestate_t.GS_LEVEL && oldgamestate != gamestate_t.GS_LEVEL)
        {
            viewactivestate = false;        // view was not active
            R.FillBackScreen ();    // draw the pattern into the back screen
        }

        // see if the border needs to be updated to the screen
        if (gamestate == gamestate_t.GS_LEVEL && !automapactive && !R.isFullScreen())
        {
            if (menuactive || menuactivestate || !viewactivestate)
                borderdrawcount = 3;
            if (eval(borderdrawcount))
            {
                R.DrawViewBorder ();    // erase old menu stuff
                borderdrawcount--;
            }

        }

        menuactivestate = menuactive;
        viewactivestate = viewactive;
        inhelpscreensstate = inhelpscreens;
        oldgamestate = wipegamestate = gamestate;

        // draw pause pic
        if (paused)
        {
            if (automapactive)
                y = 4;
            else
                y = view.getViewWindowY()+4;
            V.DrawPatchDirect(view.getViewWindowX()+(view.getScaledViewWidth()-68)/2,
                y,0,W.CachePatchName ("M_PAUSE", PU_CACHE));
        }


        // menus go directly to the screen
        M.Drawer ();          // menu is drawn even on top of everything
        NetUpdate ();         // send out any new accumulation

        // Disk access goes after everything.
        DD.Drawer();
        
        // normal update
        if (!wipe)
        {
            //System.out.print("Tick "+DM.gametic+"\t");
            //System.out.print(DM.players[0]);
            VI.FinishUpdate ();              // page flip or blit buffer
            return;
        }

        // wipe update. At this point, AT LEAST one frame of the game must have been
        // rendered for this to work. 22/5/2011: Fixed a vexing bug with the wiper.
        // Jesus Christ with a Super Shotgun!
        WIPE.EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);

        wipestart = TICK.GetTime () - 1;

        do
        {
            do
            {
                nowtime = TICK.GetTime ();
                tics = nowtime - wipestart;
            } while (tics==0); // Wait until a single tic has passed.
            wipestart = nowtime;
            done = WIPE.ScreenWipe(Wiper.wipe.Melt.ordinal()
                , 0, 0, SCREENWIDTH, SCREENHEIGHT, tics);
            ISND.UpdateSound();
            ISND.SubmitSound();             // update sounds after one wipe tic.
            VI.UpdateNoBlit ();
            M.Drawer ();                    // menu is drawn even on top of wipes
            VI.FinishUpdate ();             // page flip or blit buffer
        } while (!done);

    }



    /**
     * D-DoomLoop()
     * Not a globally visible function,
     *  just included for source reference,
     *  called by D_DoomMain, never exits.
     * Manages timing and IO,
     *  calls all ?_Responder, ?_Ticker, and ?_Drawer,
     *  calls I_GetTime, I_StartFrame, and I_StartTic
     * @throws IOException 
     */

    public void DoomLoop () throws IOException
    {
        if (demorecording)
            BeginRecording ();

        if (eval(CM.CheckParm ("-debugfile")))
        {
            String    filename="debug"+consoleplayer+".txt";
            System.out.println("debug output to: "+filename);
            try {
                debugfile = new OutputStreamWriter(new FileOutputStream(filename));
            } catch (FileNotFoundException e) {
                System.err.println("Couldn't open debugfile. Now, that sucks some putrid shit out of John Romero's asshole!");
                e.printStackTrace();
            }
        }
        
        view=R.getView();
        
        while (true)
        {
            // frame syncronous IO operations
            VI.StartFrame ();                

            // process one or more tics
            if (singletics)
            {
                VI.StartTic ();
                ProcessEvents ();
                BuildTiccmd (netcmds[consoleplayer][maketic%BACKUPTICS]);
                if (advancedemo)
                    DoAdvanceDemo ();
                M.Ticker ();
                Ticker ();
                gametic++;
                maketic++;
            }
            else
            {
                DGN.TryRunTics (); // will run at least one tic (in NET)

            }

            S.UpdateSounds (players[consoleplayer].mo);// move positional sounds

            // Update display, next frame, with current state.
            Display ();
            
//#ifndef SNDSERV
	// Sound mixing for the buffer is snychronous.
	ISND.UpdateSound();
//#endif	
	// Synchronous sound output is explicitly called.
//#ifndef SNDINTR
	// Update sound output.
	ISND.SubmitSound();
//#endif
             
        }
    }
    
    // To keep an "eye" on the renderer.
    protected ViewVars view;

    //
    //  DEMO LOOP
    //
    int             demosequence;
    int             pagetic;
    String pagename;


    /**
     * D_PageTicker
     * Handles timing for warped projection
     */
    public final void PageTicker ()
    {
        if (--pagetic < 0)
            AdvanceDemo ();
    }



    /**
     * D_PageDrawer
     */

    public final void PageDrawer ()
    {

        // FIXME: this check wasn't necessary in vanilla, since pagename was 
        // guaranteed(?) not to be null or had a safe default value.  
        if (pagename != null)
            V.DrawPatchSolidScaled (0,0, SAFE_SCALE, SAFE_SCALE,0,W.CachePatchName(pagename, PU_CACHE));
    }


    /**
     * D_AdvanceDemo
     * Called after each demo or intro demosequence finishes
     */

    public void AdvanceDemo ()
    {
        advancedemo = true;
    }


    //
    // This cycles through the demo sequences.
    // FIXME - version dependant demo numbers?
    //
    public void DoAdvanceDemo ()
    {
        players[consoleplayer].playerstate = PST_LIVE;  // not reborn
        advancedemo = false;
        usergame = false;               // no save / end game here
        paused = false;
        gameaction = gameaction_t.ga_nothing;
        
        if ( isRetail()) // Allows access to a 4th demo.
            demosequence = (demosequence+1)%7;
        else
            demosequence = (demosequence+1)%6;

        switch (demosequence)
        {
        case 0:
            if ( isCommercial() )
                pagetic = 35 * 11;
            else
                pagetic = 170;
            gamestate = gamestate_t.GS_DEMOSCREEN;
            
            
            if (W.CheckNumForName("TITLEPIC")!=-1){
                pagename = "TITLEPIC";    
            } else {
                if (W.CheckNumForName("DMENUPIC")!=-1){
                    pagename = "DMENUPIC";
                }
            }
            
            if ( isCommercial() )
                S.StartMusic(musicenum_t.mus_dm2ttl);

            else
                S.StartMusic (musicenum_t.mus_intro); 
            break;
        case 1:
            DeferedPlayDemo ("demo1");
            break;
        case 2:
            pagetic = 200;
            gamestate = gamestate_t.GS_DEMOSCREEN;
            pagename = "CREDIT";
            break;
        case 3:
            DeferedPlayDemo ("demo2");
            break;
        case 4:
            gamestate = gamestate_t.GS_DEMOSCREEN;
            if ( isCommercial())
            {
                pagetic = 35 * 11;
                pagename = "TITLEPIC";
                S.StartMusic(musicenum_t.mus_dm2ttl);
            }
            else
            {
                pagetic = 200;

                if ( isRetail() )
                    pagename = "CREDIT";
                else
                    pagename = "HELP1";
            }
            break;
        case 5:
            DeferedPlayDemo ("demo3");
            break;
            // THE DEFINITIVE DOOM Special Edition demo
        case 6:
            DeferedPlayDemo ("demo4");
            break;
        }
    }



    /**
     * D_StartTitle
     */

    public void StartTitle ()
    {
        gameaction = gameaction_t.ga_nothing;
        demosequence = -1;
        AdvanceDemo ();
    }




    //      printtitle for every printed line
    StringBuffer            title=new StringBuffer();



    /**
     * D_AddFile
     * 
     * Adds file to the end of the wadfiles[] list.
     * Quite crude, we could use a listarray instead.
     * 
     * @param file
     */
    void AddFile (String file)
    {
        int     numwadfiles;
        String newfile;

        for (numwadfiles = 0 ; eval(wadfiles[numwadfiles]) ; numwadfiles++)
            ;

        newfile = new String(file);

        wadfiles[numwadfiles] = newfile;
    }
    

    /**
     * IdentifyVersion
     * Checks availability of IWAD files by name,
     * to determine whether registered/commercial features
     * should be executed (notably loading PWAD's).
     */

    public String IdentifyVersion ()
    {

        String home;
        String doomwaddir;
        DoomVersions vcheck=new DoomVersions();
        
        // By default.
        language=Language_t.english;
        
        // First, check for -iwad parameter.
        // If valid, then it trumps all others.
        
        int p;
        p=CM.CheckParm("-iwad");
        if (eval(p))
        {
        	System.out.println("-iwad specified. Will be used with priority\n");
            String test=CM.getArgv(p+1);
            
            // It might be quoted.
            test=C2JUtils.unquoteIfQuoted(test,'"');
            
            String separator=System.getProperty("file.separator");
            doomwaddir=test.substring(0, 1+test.lastIndexOf(separator));
            String iwad=test.substring( 1+test.lastIndexOf(separator));
            GameMode_t attempt=vcheck.tryOnlyOne(iwad,doomwaddir);
            // Note: at this point we can't distinguish between "doom" retail
            // and "doom" ultimate yet.
            if (attempt!=null) {
            	AddFile(doomwaddir+iwad);
            	this.setGameMode(attempt);
            	return (doomwaddir+iwad);
            }
        } else {
        // Unix-like checking. Might come in handy sometimes.   
        // This should ALWAYS be activated, else doomwaddir etc. won't be defined.

        doomwaddir = System.getenv("DOOMWADDIR");
        if (doomwaddir!=null){
                System.out.println("DOOMWADDIR found. Will be used with priority\n");
        }
        
        home = System.getenv("HOME");
        if (NORMALUNIX){
            if (!eval(home))
                I.Error("Please set $HOME to your home directory");
        }
        
        Settings.basedefault=home+"/.doomrc";   

        // None found, using current.
        if (!eval(doomwaddir))
            doomwaddir = ".";
    
        	vcheck.tryThemAll(doomwaddir);
        }

        // MAES: Interesting. I didn't know of that :-o
        if (eval(CM.CheckParm ("-shdev")))
        {
            setGameMode(GameMode_t.shareware);
            devparm = true;
            AddFile (dstrings.DEVDATA+"doom1.wad");
            AddFile (dstrings.DEVMAPS+"data_se/texture1.lmp");
            AddFile (dstrings.DEVMAPS+"data_se/pnames.lmp");
            Settings.basedefault=dstrings.DEVDATA+"default.cfg";
            return (dstrings.DEVDATA+"doom1.wad");
        }

        if (eval(CM.CheckParm ("-regdev")))
        {
            setGameMode(GameMode_t.registered);
            devparm = true;
            AddFile (dstrings.DEVDATA+"doom.wad");
            AddFile (dstrings.DEVMAPS+"data_se/texture1.lmp");
            AddFile (dstrings.DEVMAPS+"data_se/texture2.lmp");
            AddFile (dstrings.DEVMAPS+"data_se/pnames.lmp");
            Settings.basedefault=dstrings.DEVDATA+"default.cfg";
            return (dstrings.DEVDATA+"doom.wad");
        }

        if (eval(CM.CheckParm ("-comdev")))
        {
            setGameMode(GameMode_t.commercial);
            devparm = true;
            /* I don't bother
	if(plutonia)
	    D_AddFile (DEVDATA"plutonia.wad");
	else if(tnt)
	    D_AddFile (DEVDATA"tnt.wad");
	else*/

            AddFile (dstrings.DEVDATA+"doom2.wad");	    
            AddFile (dstrings.DEVMAPS+"cdata/texture1.lmp");
            AddFile (dstrings.DEVMAPS+"cdata/pnames.lmp");
            Settings.basedefault=dstrings.DEVDATA+"default.cfg";
            return (dstrings.DEVDATA+"doom2.wad");
        }


        if ( testReadAccess(vcheck.doom2fwad))
        {
            setGameMode(GameMode_t.commercial);
            // C'est ridicule!
            // Let's handle languages in config files, okay?
            language = Language_t.french;
            System.out.println("French version\n");
            AddFile (vcheck.doom2fwad);
            return vcheck.doom2fwad;
        }


        if ( testReadAccess(vcheck.doom2wad))
        {
            setGameMode(GameMode_t.commercial);
            AddFile (vcheck.doom2wad);
            return vcheck.doom2wad;
        }

        if ( testReadAccess (vcheck.plutoniawad) )
        {
            setGameMode(GameMode_t.pack_plut);
            AddFile (vcheck.plutoniawad);
            return vcheck.plutoniawad;
        }

        if ( testReadAccess ( vcheck.tntwad) )
        {
            setGameMode(GameMode_t.pack_tnt);
            AddFile (vcheck.tntwad);
            return vcheck.tntwad;
        }
        
        if ( testReadAccess ( vcheck.tntwad) )
        {
            setGameMode(GameMode_t.pack_xbla);
            AddFile (vcheck.xblawad);
            return vcheck.xblawad;
        }

        if ( testReadAccess (vcheck.doomuwad) )
        {
            // TODO auto-detect ultimate Doom even from doom.wad
        	// Maes: this is done later on.
            setGameMode(GameMode_t.retail);
            AddFile (vcheck.doomuwad);
            return vcheck.doomuwad;
        }

        if ( testReadAccess (vcheck.doomwad) )
        {
            setGameMode(GameMode_t.registered);
            AddFile (vcheck.doomwad);
            return vcheck.doomwad;
        }

        if ( testReadAccess (vcheck.doom1wad) )
        {
            setGameMode(GameMode_t.shareware);
            AddFile (vcheck.doom1wad);
            return vcheck.doom1wad;
        }

        // MAES: Maybe we should add FreeDoom here later.

        System.out.println("Game mode indeterminate.\n");
        setGameMode(GameMode_t.indetermined);

        return null;
        // We don't abort. Let's see what the PWAD contains.
        //exit(1);
        //I_Error ("Game mode indeterminate\n");
    }




    /**
     * D_DoomMain
     *
     * Completes the job started by Init, which only created the
     * instances of the various stuff and registered the "status aware" 
     * stuff. Here everything priority-critical is 
     * called and created in more detail.
     *
     *
     */

	public void Start() throws IOException
    {
        int             p;
        StringBuffer file=new StringBuffer();

        // TODO: This may modify the command line by appending more stuff
        // from an external file. Since it can affect other stuff too,
        // maybe it should be outside of Start() and into i.Main ?
        CM.FindResponseFile ();

        String iwadfilename=IdentifyVersion ();
        
        // Sets unbuffered output in C. Not needed here. setbuf (stdout, NULL);
        modifiedgame = false;

        nomonsters = eval(CM.CheckParm ("-nomonsters"));
        respawnparm = eval(CM.CheckParm ("-respawn"));
        fastparm = eval(CM.CheckParm ("-fast"));
        devparm = eval(CM.CheckParm ("-devparm"));
        if (eval(CM.CheckParm ("-altdeath")))
            //deathmatch = 2;
            altdeath=true;
        else if (eval(CM.CheckParm ("-deathmatch")))
            deathmatch = true;

        // MAES: Check for Ultimate Doom in "doom.wad" filename.
        WadLoader tmpwad=new WadLoader();
        try {
			tmpwad.InitFile(iwadfilename,true);
		} catch (Exception e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		// Check using a reloadable hack.
        CheckForUltimateDoom(tmpwad);    
       
        // MAES: better extract a method for this.
        GenerateTitle();

        // Print ticker info. It has already been set at Init() though.
        if (eval(CM.CheckParm("-millis"))){
            System.out.println("ITicker: Using millisecond accuracy timer.");
        } else if (eval(CM.CheckParm("-fasttic"))){
                System.out.println("ITicker: Using fastest possible timer.");
            }
        else {
            System.out.println("ITicker: Using nanosecond accuracy timer.");
        }

        System.out.println(title.toString());

        if (devparm)
            System.out.println(D_DEVSTR);

        // Running from CDROM?
        if (eval(CM.CheckParm("-cdrom")))
        {
            System.out.println(D_CDROM);
            //System.get("c:\\doomdata",0);
            System.out.println (Settings.basedefault+"c:/doomdata/default.cfg");
        }	

        // turbo option
        if ( eval(p=CM.CheckParm ("-turbo")) )
        {
            int     scale = 200;
            //int forwardmove[2];
            // int sidemove[2];

            if (p<CM.getArgc()-1)
                scale = Integer.parseInt(CM.getArgv(p+1));
            if (scale < 10)
                scale = 10;
            if (scale > 400)
                scale = 400;
            System.out.println("turbo scale: "+scale);
            forwardmove[0] = forwardmove[0]*scale/100;
            forwardmove[1] = forwardmove[1]*scale/100;
            sidemove[0] = sidemove[0]*scale/100;
            sidemove[1] = sidemove[1]*scale/100;
        }

        // add any files specified on the command line with -file wadfile
        // to the wad list
        //
        // convenience hack to allow -wart e m to add a wad file
        // prepend a tilde to the filename so wadfile will be reloadable
        p = CM.CheckParm ("-wart");
        if (eval(p))
        {
            char[] tmp=CM.getArgv(p).toCharArray();
            tmp[4]= 'p';// big hack, change to -warp
            CM.setArgv(p,new String(tmp));    
            GameMode_t gamemode=getGameMode();
            // Map name handling.
            switch (gamemode )
            {
            case shareware:
            case retail:
            case registered:
                file.append("~");
                file.append(DEVMAPS);
                file.append(String.format("E%cM%c.wad", CM.getArgv(p+1), CM.getArgv(p+2)));
                file.append(String.format("Warping to Episode %s, Map %s.\n",
                    CM.getArgv(p+1),CM.getArgv(p+2)));
                break;

            case commercial:
            default:
                p = Integer.parseInt(CM.getArgv(p+1));
                if (p<10){
                    file.append("~");
                    file.append(DEVMAPS);
                    file.append(String.format("cdata/map0%d.wad", p));
                }
                else
                {
                    file.append("~");
                    file.append(DEVMAPS);
                    file.append(String.format("cdata/map%d.wad", p));
                }
                break;
            }
            AddFile (file.toString());
        }

        p = CM.CheckParm ("-file");
        if (eval(p))
        {
            // the parms after p are wadfile/lump names,
            // until end of parms or another - preceded parm
            modifiedgame = true;            // homebrew levels
            // MAES 1/6/2011: Avoid + to avoid clashing with +map
            while (++p != CM.getArgc() && CM.getArgv(p).charAt(0) != '-' && CM.getArgv(p).charAt(0) != '+')
                AddFile (C2JUtils.unquoteIfQuoted(CM.getArgv(p),'"'));
        }
        
        p = CM.CheckParm ("-playdemo");
                
        if (eval(p)) normaldemo=true;
        else {
            p=CM.CheckParm ("-fastdemo");        
            if (eval(p)){
            System.out.println("Fastdemo mode. Boundless clock!");
            fastdemo=true;
            this.TICK=new FastTicker();            
            }
            else        
                if (!eval(p)) {
                    p = CM.CheckParm ("-timedemo");
                    if (eval(p)) singletics=true;
                }
        }

        // If any of the previous succeeded, try grabbing the filename.
        if ((normaldemo||fastdemo||singletics) && p < CM.getArgc()-1)
        {   
            loaddemo=CM.getArgv(p+1);
            AddFile (loaddemo+".lmp");
            System.out.printf("Playing demo %s.lmp.\n",loaddemo);
            autostart=true;
        }
        
        // Subsequent uses of loaddemo use only the lump name.
        loaddemo=C2JUtils.extractFileBase(loaddemo,0,true);

        // get skill / episode / map from parms
        // FIXME: should get them FROM THE DEMO itself.
        startskill = skill_t.sk_medium;
        startepisode = 1;
        startmap = 1;
        //autostart = false;


        p = CM.CheckParm ("-novert");
        if (eval(p) && p < CM.getArgc()-1)
        {
            novert=!(CM.getArgv(p+1).toLowerCase().compareTo("disable")==0);
            if (!novert) System.out.println("-novert ENABLED (default)");
            else System.out.println("-novert DISABLED. Hope you know what you're doing...");
        }

        p = CM.CheckParm ("-skill");
        if (eval(p) && p < CM.getArgc()-1)
        {
            startskill = skill_t.values()[CM.getArgv(p+1).charAt(0)-'1'];
            autostart = true;
        }

        p = CM.CheckParm ("-episode");
        if (eval(p) && p < CM.getArgc()-1)
        {
            startepisode = CM.getArgv(p+1).charAt(0)-'0';
            startmap = 1;
            autostart = true;
        }

        p = CM.CheckParm ("-timer");
        if (eval(p) && p < CM.getArgc()-1 && deathmatch)
        {
            int     time;
            time = Integer.parseInt(CM.getArgv(p+1));
            System.out.print("Levels will end after "+time+" minute");
            if (time>1)
                System.out.print("s");
            System.out.print(".\n");
        }

        // OK, and exactly how is this enforced?
        p = CM.CheckParm ("-avg");
        if (eval(p) && p < CM.getArgc()-1 && deathmatch)
            System.out.print("Austin Virtual Gaming: Levels will end after 20 minutes\n");

        // MAES 31/5/2011: added support for +map variation.
        p = CM.CheckParm ("-warp");
        if (eval(p) && p < CM.getArgc()-1)
        {
            if (isCommercial())
                startmap = Integer.parseInt(CM.getArgv(p+1));
            else
            {
                int eval=11;
                try {
                    eval=Integer.parseInt(CM.getArgv(p+1));
                } catch (Exception e){
                    // swallow exception. No warp.
                }

                if (eval>99) eval%=100;

                if (eval<10) {
                    startepisode = 1;
                    startmap = 1;
                }
                else {
                    startepisode = eval/10;
                    startmap = eval%10;
                }
            }
            autostart = true;
        }

        // Maes: 1/6/2011 Added +map support
        p = CM.CheckParm ("+map");
        if (eval(p)) 
        {
            if (isCommercial()) {
                startmap = parseAsMapXX(CM.getArgv(p+1));
                if (startmap!=-1){
                	autostart = true;
            	}
            }
            else
            {
                int eval=parseAsExMx(CM.getArgv(p+1));
                if (eval!=-1){

                    startepisode = Math.max(1,eval/10);
                    startmap = Math.max(1,eval%10);
                    autostart = true;
                }
            }
            
        }
        
        // init subsystems
        System.out.print("V_Init: allocate screens.\n");
        V.Init ();
        

        System.out.print("Z_Init: Init zone memory allocation daemon. \n");
        // DUMMY: Z_Init ();

        System.out.print("W_Init: Init WADfiles.\n");
        try {
            W.InitMultipleFiles(wadfiles,true);
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        //
        System.out.print("VI_Init: set colormaps.\n");
        
        // MAES: FIX for incomplete palette lumps such as those in EGADOOM.
        // Generate the palette programmatically _anyway_
        byte[] pal=PaletteGenerator.generatePalette(PaletteGenerator.playpal,
        		256, ColorTint.tints);
        // Copy over the one you read from disk...
        int pallump=W.GetNumForName("PLAYPAL");
        byte[] tmppal=W.CacheLumpNumAsRawBytes(pallump, PU_STATIC);
        if (tmppal!=null)
        System.arraycopy(tmppal, 0, pal, 0, pal.length);
        
        V.createPalettes(pal, GammaTables.gammatables, 14, 256, 3, 5);
        
        W.InjectLumpNum(pallump,new DoomBuffer(ByteBuffer.wrap(pal)));
        // set it, create it, but don't make it visible yet.
        
        VI=selectVideoInterface();

        VI.InitGraphics();

        // MAES: Before we begin calling the various Init() stuff,
        // we need to make sure that objects that support the IVideoScaleAware
        // interface get set and initialized.

        initializeVideoScaleStuff();

        // MAES: The status bar needs this update because it can "force"
        // the video renderer to assign it a scratchpad screen (Screen 4).
        // Since we're at it, let's update everything, it's easy!
        
        this.updateStatusHolders(this);
        
        // Update variables and stuff NOW.
        this.update();

        // Check for -file in shareware
        CheckForPWADSInShareware();
        

        // Iff additonal PWAD files are used, printmodified banner
        
        if (modifiedgame)
        // Generate WAD loading alert. Abort upon denial.
        if (!I.GenerateAlert(Strings.MODIFIED_GAME_TITLE,Strings.MODIFIED_GAME_DIALOG)) {
        	  W.CloseAllHandles();
        	  System.exit(-2);
          }
        
        // Check and printwhich version is executed.
        switch ( getGameMode() )
        {
        case shareware:
        case indetermined:
            System.out.print("===========================================================================\n");
            System.out.print("                                Shareware!\n");
            System.out.print("===========================================================================\n");
            break;
        case registered:
        case retail:
        case commercial:
        case pack_tnt:
        case pack_plut:
        case pack_xbla:
            System.out.print("===========================================================================\n");
            System.out.print("                 Commercial product - do not distribute!\n");
            System.out.print("         Please report software piracy to the SPA: 1-800-388-PIR8\n");
            System.out.print("===========================================================================\n");
            break;

        default:
            // Ouch.
            break;
        }

        System.out.print("Tables.InitTables: Init trigonometric LUTs.\n");
        Tables.InitTables();

        System.out.print("M_Init: Init miscellaneous info.\n");
        M.Init ();

        System.out.print("R_Init: Init DOOM refresh daemon - ");
        R.Init ();
        
        System.out.print("AM_Init: Init Automap colors - ");
        AM.Init (); // Called here to set up configurable colors.

        System.out.print("\nP_Init: Init Playloop state.\n");
        P.Init ();

        System.out.print("I_Init: Setting up machine state.\n");
        I.Init ();

        System.out.print("D_CheckNetGame: Checking network game status.\n");
        CheckNetGame ();

        System.out.print("S_Init: Setting up sound.\n");
        
      // Sound "drivers" before the game sound controller.
        
        if (CM.CheckParmBool("-nomusic") || CM.CheckParmBool("-nosound"))
            this.IMUS=new DummyMusic();
        else
            this.IMUS=new DavidMusicModule();
        
        if (CM.CheckParmBool("-nosfx") ||  CM.CheckParmBool("-nosound"))
            this.ISND=new DummySFX();
        else {
            // Switch between possible sound drivers.
            // Crudish.
            if (CM.CheckParmBool("-audiolines"))
                this.ISND=	new DavidSFXModule(this,numChannels);
            else // PC Speaker emulation 
            if (CM.CheckParmBool("-speakersound"))
                this.ISND=  new SpeakerDoomSoundDriver(this,numChannels);
            else
            if (CM.CheckParmBool("-clipsound"))
                this.ISND=  new ClipSFXModule(this,numChannels);
            else  // This is the default
            if (CM.CheckParmBool("-classicsound"))
                this.ISND=  new ClassicDoomSoundDriver(this,numChannels);
            else  // This is the default
                this.ISND=  new SuperDoomSoundDriver(this,numChannels);

            }
        
        // Check for sound init failure and revert to dummy
        if (!ISND.InitSound()){
        	 System.err.println ("S_InitSound: failed. Reverting to dummy...\n");
        	this.ISND=new DummySFX();
        }
        
        if (!(CM.CheckParmBool("-nosound") || (ISND instanceof DummySFX)))// Obviously, nomusic && nosfx = nosound.
        	this.S=new AbstractDoomAudio(this,numChannels);
        else
        	// Saves a lot of distance calculations, 
        	// if we're not to output any sound at all.
            // TODO: create a Dummy that can handle music alone.
        	this.S=new DummySoundDriver();
        
        IMUS.InitMusic();
        S.Init (snd_SfxVolume *8, snd_MusicVolume *8 );

        // Hook audio to users.
        this.updateStatusHolders(this);
        
        System.out.print("HU_Init: Setting up heads up display.\n");
        HU.Init();

        System.out.print("ST_Init: Init status bar.\n");
        ST.Init ();

        // check for a driver that wants intermission stats
        p = CM.CheckParm ("-statcopy");
        if (eval(p) && p<CM.getArgc()-1)
        {
            // TODO: this should be chained to a logger
            //statcopy = CM.getArgv(p+1);
            System.out.print("External statistics registered.\n");
        }

        // start the apropriate game based on parms
        p = CM.CheckParm ("-record");

        if (eval(p) && p < CM.getArgc()-1)
        {
            RecordDemo (CM.getArgv(p+1));
            autostart = true;
        }
        
        // NOW it's safe to init the disk reader.
        DD.Init();

        // MAES: at this point everything should be set and initialized, so it's
        // time to make the players aware of the general status of Doom.
        //_D_ gonna try to initialize here, because it is needed to play a demo
        for (int i=0;i<MAXPLAYERS;i++){
            players[i].updateStatus(this);
        }

        //p = CM.CheckParm ("-timedemo");
        if (singletics)
        {
            TimeDemo (loaddemo);
            autostart = true;
            DoomLoop ();  // never returns
        }

        p = CM.CheckParm ("-loadgame");
        if (eval(p) && p < CM.getArgc()-1)
        {
            file.delete(0, file.length());
            if (eval(CM.CheckParm("-cdrom"))){
                file.append("c:\\doomdata\\");
                file.append(SAVEGAMENAME);
                file.append("%c.dsg");
                file.append(CM.getArgv(p+1).charAt(0));
            }
            else
            {
                file.append(String.format("%s%c.dsg",SAVEGAMENAME,
                    CM.getArgv(p+1).charAt(0)));

            }
            LoadGame(file.toString());
        }



        if ( gameaction != gameaction_t.ga_loadgame )
        {
            if (autostart || netgame)
                InitNew (startskill, startepisode, startmap);
            else
                StartTitle ();                // start up intro loop

        }


        if (fastdemo||normaldemo)
        {
            singledemo = true;              // quit after one demo
            if (fastdemo) timingdemo=true;
            InitNew (startskill, startepisode, startmap);
            gamestate=gamestate_t.GS_DEMOSCREEN;
            DeferedPlayDemo (loaddemo);
            DoomLoop ();  // never returns
        }

        
        
        DoomLoop ();  // never returns
    }


	protected abstract DoomVideoInterface<V> selectVideoInterface();


    protected int parseAsMapXX(String argv) {
    	
    	if (argv.length()!=5) return -1; // Nah.
    	if (argv.toLowerCase().lastIndexOf("map")!=0) return -1; // Meh.
    	int map;
    	try {
    		map=Integer.parseInt(argv.substring(3));
    	} catch (NumberFormatException e){
    		return -1; // eww
    	}
    	
		return map;
	}

    protected int parseAsExMx(String argv) {
    	
    	if (argv.length()!=4) return -1; // Nah.
    	if (argv.toLowerCase().lastIndexOf("e")!=0) return -1; // Meh.
    	if (argv.toLowerCase().lastIndexOf("m")!=2) return -1; // Meh.
    	int episode,mission;
    	try {
    		episode=Integer.parseInt(argv.substring(1,2));
    		mission=Integer.parseInt(argv.substring(3,4));
    	} catch (NumberFormatException e){
    		return -1; // eww
    	}
    	
		return episode*10+mission;
	}
    
	List<IVideoScaleAware> videoScaleChildren;

    public  void initializeVideoScaleStuff() {

        videoScaleChildren=new ArrayList<IVideoScaleAware>();

        // The automap...
        videoScaleChildren.add(this.AM);
        // The finale...
        videoScaleChildren.add(this.F);
        // The wiper...
        videoScaleChildren.add(this.WIPE);
        // The heads up...
        videoScaleChildren.add(this.HU);
        // The menu...
        videoScaleChildren.add(this.M);
        // The renderer (also has dependent children!)
        videoScaleChildren.add(this.R);
        // The Status Bar
        videoScaleChildren.add(this.ST);
        // Even the video renderer needs some initialization?
        videoScaleChildren.add(this.V);
        // wiper
        videoScaleChildren.add(this.WI);
        // disk drawer
        videoScaleChildren.add(this.DD);
        
        for(IVideoScaleAware i:videoScaleChildren){
            if (i!=null){
                i.setVideoScale(this.vs);
                i.initScaling();
            }
        }


    }


    /**
     * 
     */
    protected void CheckForPWADSInShareware() {
        if (modifiedgame)
        {
            // These are the lumps that will be checked in IWAD,
            // if any one is not present, execution will be aborted.
            String[] name=
            {
                    "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9",
                    "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9",
                    "dphoof","bfgga0","heada1","cybra1","spida1d1"
            };
            int i;

            // Oh yes I can.
            if ( isShareware())
                System.out.println("\nYou cannot -file with the shareware version. Register!");

            // Check for fake IWAD with right name,
            // but w/o all the lumps of the registered version. 
            if (isRegistered())
                for (i = 0;i < 23; i++)
                    if (W.CheckNumForName(name[i].toUpperCase())<0)
                        I.Error("\nThis is not the registered version: "+name[i]);
        }
    }

    /** Check whether the "doom.wad" we actually loaded
     *  is ultimate Doom's, by checking if it contains 
     *  e4m1 - e4m9.
     * 
     */
    protected void CheckForUltimateDoom(WadLoader W) {
        if (isRegistered())
        {
            // These are the lumps that will be checked in IWAD,
            // if any one is not present, execution will be aborted.
            String[] lumps=
            {
                    "e4m1","e4m2","e4m3","e4m4","e4m5","e4m6","e4m7","e4m8","e4m9"
            };

            // Check for fake IWAD with right name,
            // but w/o all the lumps of the registered version. 
            if (!CheckForLumps(lumps,W)) return;
            // Checks passed, so we can set the mode to Ultimate
            setGameMode(GameMode_t.retail);
        }

    }


    /** Check if ALL of the lumps exist.
     * 
     * @param name
     * @return
     */
    protected boolean CheckForLumps(String[] name, WadLoader W) {
        for (int i = 0;i < name.length; i++)
            if (W.CheckNumForName(name[i].toUpperCase())<0) {
                // Even one is missing? Not OK.
                return false; 
            }
        return true;
    }


    /**
     * 
     */
    protected void GenerateTitle() {
        switch ( getGameMode() )
        {
        case retail:
            title.append("                         ");
            title.append("The Ultimate DOOM Startup v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case shareware:
            title.append("                            ");
            title.append("DOOM Shareware Startup v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case registered:
            title.append("                            ");
            title.append("DOOM Registered Startup v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case commercial:
            title.append("                            ");
            title.append("DOOM 2: Hell on Earth v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");

            break;
        case pack_plut:
            title.append("                            ");
            title.append("DOOM 2: Plutonia Experiment v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case pack_tnt:
            title.append("                            ");
            title.append("DOOM 2: TNT - Evilution v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        case pack_xbla:
            title.append("                            ");
            title.append("DOOM 2: No Rest for the Living v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;

        default:
            title.append("                            ");
            title.append("Public DOOM - v");
            title.append(VERSION/100);
            title.append(".");
            title.append(VERSION%100);
            title.append("                           ");
            break;
        }
    }

    // Used in BuildTiccmd.
    protected ticcmd_t   base=new ticcmd_t();

    /**
     * G_BuildTiccmd
     * Builds a ticcmd from all of the available inputs
     * or reads it from the demo buffer. 
     * If recording a demo, write it out .
     * 
     * The CURRENT event to process is written to the various 
     * gamekeydown etc. arrays by the Responder method.
     * So look there for any fuckups in constructing them.
     * 
     */ 

    private void BuildTiccmd (ticcmd_t cmd) 
    { 
        int     i; 
        boolean strafe;
        boolean bstrafe; 
        int     speed, tspeed,lspeed; 
        int     forward;
        int     side;
        int look;

        //base = I_BaseTiccmd ();     // empty, or external driver
        // memcpy (cmd,base,sizeof(*cmd));
        base.copyTo(cmd);

        cmd.consistancy =  consistancy[consoleplayer][maketic%BACKUPTICS]; 

        strafe = gamekeydown[key_strafe] || mousebuttons(mousebstrafe) 
        || joybuttons(joybstrafe); 
        speed = ((gamekeydown[key_speed]^alwaysrun) || joybuttons(joybspeed))?1:0;

        forward = side = look= 0;
        
        // use two stage accelerative turning
        // on the keyboard and joystick
        if (joyxmove < 0 || joyxmove > 0 ||
        	gamekeydown[key_right] || gamekeydown[key_left]) 
            turnheld += ticdup; 
        else 
            turnheld = 0; 

        if (turnheld < SLOWTURNTICS) 
            tspeed = 2;             // slow turn 
        else 
            tspeed = speed;

        if(gamekeydown[key_lookdown] || gamekeydown[key_lookup])
    	{
    		lookheld += ticdup;
    	}
    	else
    	{
    		lookheld = 0;
    	}
    	if(lookheld < SLOWTURNTICS)
    	{
    		lspeed = 1;
    	}
    	else
    	{
    		lspeed = 2;
    	}
        
        // let movement keys cancel each other out
        if (strafe) 
        { 
            if (gamekeydown[key_right]) 
            {
                // fprintf(stderr, "strafe right\n");
                side += sidemove[speed]; 
            }
            if (gamekeydown[key_left]) 
            {
                //  fprintf(stderr, "strafe left\n");
                side -= sidemove[speed]; 
            }
            if (joyxmove > 0) 
                side += sidemove[speed]; 
            if (joyxmove < 0) 
                side -= sidemove[speed]; 

        } 
        else 
        { 
            if (gamekeydown[key_right]) 
                cmd.angleturn -= angleturn[tspeed]; 
            if (gamekeydown[key_left]) 
                cmd.angleturn += angleturn[tspeed]; 
            if (joyxmove > 0) 
                cmd.angleturn -= angleturn[tspeed]; 
            if (joyxmove < 0) 
                cmd.angleturn += angleturn[tspeed]; 
        } 

        if (gamekeydown[key_up]) 
        {
            //System.err.print("up\n");
            forward += forwardmove[speed]; 
        }
        if (gamekeydown[key_down]) 
        {
            //System.err.print("down\n");
            forward -= forwardmove[speed]; 
        }        
        
        if (joyymove < 0) 
            forward += forwardmove[speed]; 
        if (joyymove > 0) 
            forward -= forwardmove[speed]; 
        if (gamekeydown[key_straferight]) 
            side += sidemove[speed]; 
        if (gamekeydown[key_strafeleft]) 
            side -= sidemove[speed];

    	// Look up/down/center keys
    	if(gamekeydown[key_lookup])
    	{
    		System.err.print("Look up\n");
    		look = lspeed;
    	}
    	
    	if(gamekeydown[key_lookdown])
    	{
    		System.err.print("Look down\n");
    		look = -lspeed;
    	}
    	
    	if(gamekeydown[key_lookcenter])
    	{
    		System.err.print("Center look\n");
    		look = TOCENTER;
    	}
    	
        // buttons
        cmd.chatchar = HU.dequeueChatChar(); 

        if (gamekeydown[key_fire] || mousebuttons(mousebfire) 
                || joybuttons(joybfire)) 
            cmd.buttons |= BT_ATTACK; 

        if (gamekeydown[key_use] || joybuttons(joybuse) ) 
        { 
            cmd.buttons |= BT_USE;
            // clear double clicks if hit use button 
            dclicks = 0;                   
        } 

        // chainsaw overrides 
        for (i=0 ; i<NUMWEAPONS-1 ; i++)        
            if (gamekeydown['1'+i]) 
            { 
                //System.out.println("Attempting weapon change (building ticcmd)");
                cmd.buttons |= BT_CHANGE; 
                cmd.buttons |= i<<BT_WEAPONSHIFT; 
                break; 
            }

        // mouse
        if (mousebuttons(mousebforward)) 
            forward += forwardmove[speed];

        // forward double click (operator precedence? && over >
        if (mousebuttons(mousebforward) != (dclickstate!=0) && (dclicktime > 1) ) 
        { 
            dclickstate = mousebuttons(mousebforward)?1:0; 
            if (dclickstate!=0) 
                dclicks++; 
            if (dclicks == 2) 
            { 
                cmd.buttons |= BT_USE; 
                dclicks = 0; 
            } 
            else 
                dclicktime = 0; 
        } 
        else 
        { 
            dclicktime += ticdup; 
            if (dclicktime > 20) 
            { 
                dclicks = 0; 
                dclickstate = 0; 
            } 
        }

        // strafe double click
        bstrafe = mousebuttons(mousebstrafe) || joybuttons(joybstrafe); 
        if ((bstrafe != (dclickstate2!=0)) && dclicktime2 > 1 ) 
        { 
            dclickstate2 = bstrafe?1:0; 
            if (dclickstate2!=0) 
                dclicks2++; 
            if (dclicks2 == 2) 
            { 
                cmd.buttons |= BT_USE; 
                dclicks2 = 0; 
            } 
            else 
                dclicktime2 = 0; 
        } 
        else 
        { 
            dclicktime2 += ticdup; 
            if (dclicktime2 > 20) 
            { 
                dclicks2 = 0; 
                dclickstate2 = 0; 
            } 
        } 

        // By default, no vertical mouse movement
        if (!novert) forward += mousey; 

        if (strafe) 
            side += mousex*2; 
        else 
            cmd.angleturn -= mousex*0x8; 

        mousex = mousey = 0; 

        if (forward > MAXPLMOVE()) 
            forward = MAXPLMOVE(); 
        else if (forward < -MAXPLMOVE()) 
            forward = -MAXPLMOVE(); 
        if (side > MAXPLMOVE()) 
            side = MAXPLMOVE(); 
        else if (side < -MAXPLMOVE()) 
            side = -MAXPLMOVE(); 

        cmd.forwardmove += forward; 
        cmd.sidemove += side;

    	if(players[consoleplayer].playerstate == PST_LIVE)
    	{
    		if(look < 0)
    		{
    			look += 16;
    		}
    		
    		cmd.lookfly = (char) look;
    	}
        
        // special buttons
        if (sendpause) 
        { 
            sendpause = false; 
            cmd.buttons = BT_SPECIAL | BTS_PAUSE; 
        } 

        if (sendsave) 
        { 
            sendsave = false; 
            cmd.buttons = (char) (BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT)); 
        } 
    } 


    //
    // G_DoLoadLevel 
    //
    //extern  gamestate_t     wipegamestate; 

    public boolean DoLoadLevel () 
    { 
        int             i; 

        // Set the sky map.
        // First thing, we have a dummy sky texture name,
        //  a flat. The data is in the WAD only because
        //  we look for an actual index, instead of simply
        //  setting one.
        TM.setSkyFlatNum(TM.FlatNumForName ( SKYFLATNAME ));

        // DOOM determines the sky texture to be used
        // depending on the current episode, and the game version.
        if (isCommercial()
                || ( gamemission == GameMission_t.pack_tnt )
                || ( gamemission == GameMission_t.pack_plut ) )
        {
            TM.setSkyTexture(TM.TextureNumForName ("SKY3"));
            if (gamemap < 12)
                TM.setSkyTexture(TM.TextureNumForName ("SKY1"));
            else
                if (gamemap < 21)
                    TM.setSkyTexture(TM.TextureNumForName ("SKY2"));
        }

        levelstarttic = gametic;        // for time calculation

        if (wipegamestate == gamestate_t.GS_LEVEL) 
            wipegamestate = gamestate_t.GS_MINUS_ONE;             // force a wipe 

        gamestate = gamestate_t.GS_LEVEL; 

        for (i=0 ; i<MAXPLAYERS ; i++) 
        { 
            if (playeringame[i] && players[i].playerstate == PST_DEAD) 
                players[i].playerstate = PST_REBORN; 
            // I don't give a shit if it's not super-duper optimal. 
            Arrays.fill(players[i].frags, 0);

        } 

        try {
        LL.SetupLevel (gameepisode, gamemap, 0, gameskill);
        } catch (Exception e){
        	e.printStackTrace();
        	// Failure loading level.
        	return false;
        }
        
        displayplayer = consoleplayer;      // view the guy you are playing    
        gameaction = gameaction_t.ga_nothing; 
        //Z_CheckHeap ();

        // clear cmd building stuff
        Arrays.fill(gamekeydown, false);
        keysCleared = true;
        joyxmove = joyymove = 0; 
        mousex = mousey = 0; 
        sendpause = sendsave = paused = false; 
        Arrays.fill (mousearray, false);
        Arrays.fill(joyarray, false); 
        
        
        // killough 5/13/98: in case netdemo has consoleplayer other than green
        ST.Start();
        HU.Start();
        
        // killough: make -timedemo work on multilevel demos
        // Move to end of function to minimize noise -- killough 2/22/98:

        if (timingdemo)
          {
            if (first)
              {
                starttime = RealTime.GetTime();
                first=false;
              }
          }
        
        // Try reclaiming some memory from limit-expanded buffers.
        R.resetLimits();
        return true;
    } 
    
    protected boolean first=true;
    
    // Maes: needed because a caps lock down signal is issued
    // as soon as the app gains focus. This causes lock keys to respond.
    // With this hack, they are ignored (?) the first time this happens.
    public boolean justfocused=true;

    /**
     * G_Responder  
     * Get info needed to make ticcmd_ts for the players.
     */

    public boolean Responder (event_t ev) 
    { 
        // allow spy mode changes even during the demo
        if (gamestate == gamestate_t.GS_LEVEL && ev.type == evtype_t.ev_keydown 
                && ev.data1 == KEY_F12 && (singledemo || !deathmatch) )
        {
            // spy mode 
            do 
            { 
                displayplayer++; 
                if (displayplayer == MAXPLAYERS) 
                    displayplayer = 0; 
            } while (!playeringame[displayplayer] && displayplayer != consoleplayer); 
            return true; 
        }

        // any other key pops up menu if in demos
        if (gameaction == gameaction_t.ga_nothing && !singledemo && 
                (demoplayback || gamestate == gamestate_t.GS_DEMOSCREEN) 
        ) 
        { 
            if (ev.type == evtype_t.ev_keydown ||  
                    (ev.type == evtype_t.ev_mouse && ev.data1!=0) || 
                    (ev.type == evtype_t.ev_joystick && ev.data1!=0) ) 
            { 
                M.StartControlPanel (); 
                return true; 
            } 
            return false; 
        } 

        if (gamestate == gamestate_t.GS_LEVEL) 
        { 

            if (devparm && ev.type == evtype_t.ev_keydown && ev.data1 == ';') 
            { 
                DeathMatchSpawnPlayer (0); 
                return true; 
            } 

            //automapactive=true;

            if (HU.Responder (ev)) 
                return true;    // chat ate the event 
            if (ST.Responder (ev)) 
                return true;    // status window ate it
            if (AM.Responder (ev)) 
                return true;    // automap ate it 

        } 

        if (gamestate == gamestate_t.GS_FINALE) 
        { 
            if (F.Responder (ev)) 
                return true;    // finale ate the event 
        } 

        switch (ev.type) 
        {
        case ev_clear:
        	// PAINFULLY and FORCEFULLY clear the buttons.
            Arrays.fill(gamekeydown, false);
            keysCleared = true;
            return false; // Nothing more to do here. 
        case ev_keydown: 
            if (ev.data1 == KEY_PAUSE) 
            { 
                sendpause = true; 
                return true; 
            }
            
            /* CAPS lock will only go through as a keyup event
            if (ev.data1 == KEY_CAPSLOCK) 
            { 
                if (justfocused) justfocused=false;
                    else
                // If caps are turned on, turn autorun on anyway.
                if (!alwaysrun) {
                    alwaysrun=true;
                    players[consoleplayer].message=String.format("Always run: %s",alwaysrun);
                    }
                return true; 
            } */
            
            if (ev.data1 <NUMKEYS) 
                gamekeydown[ev.data1] = true;
            return true;    // eat key down events 

        case ev_keyup:
            if (ev.data1 == KEY_CAPSLOCK) 
            { 
                if (justfocused) justfocused=false;
                    else
                    {
                    // Just toggle it. It's too hard to read the state.
                    alwaysrun=!alwaysrun;
                    players[consoleplayer].message=String.format("Always run: %s",alwaysrun);
                    }
            }
            
            if (ev.data1 <NUMKEYS) 
                gamekeydown[ev.data1] = false; 
            return false;   // always let key up events filter down 

        case ev_mouse:
            // Ignore them at the responder level
            if (use_mouse){
            mousebuttons(0, ev.data1 & 1); 
            mousebuttons(1, ev.data1 & 2); 
            mousebuttons(2, ev.data1 & 4);
            mousex = ev.data2*(mouseSensitivity+5)/10; 
            mousey = ev.data3*(mouseSensitivity+5)/10;
            }
            return true;    // eat events 

        case ev_joystick:
            if (use_joystick){
            joybuttons(0, ev.data1 & 1); 
            joybuttons(1, ev.data1 & 2); 
            joybuttons(2,ev.data1 & 4); 
            joybuttons(3,ev.data1 & 8); 
            joyxmove = ev.data2; 
            joyymove = ev.data3; 
            }
            return true;    // eat events 

        default: 
            break; 
        } 

        return false; 
    } 


    private final String turbomessage="is turbo!"; 

    /**
     * G_Ticker
     * Make ticcmd_ts for the players.
     */

    public void Ticker () 
    { 
        int     i;
        int     buf; 
        ticcmd_t   cmd;

        // do player reborns if needed
        for (i=0 ; i<MAXPLAYERS ; i++) 
            if (playeringame[i] && players[i].playerstate == PST_REBORN) 
                DoReborn (i);

        // do things to change the game state
        while (gameaction != gameaction_t.ga_nothing) 
        { 
            switch (gameaction) 
            { 
            case ga_loadlevel: 
                DoLoadLevel ();
                break; 
            case ga_newgame: 
                DoNewGame (); 
                break; 
            case ga_loadgame:
                DoLoadGame (); 
                break; 
            case ga_savegame: 
                DoSaveGame (); 
                break; 
            case ga_playdemo: 
                DoPlayDemo (); 
                break; 
            case ga_completed: 
                DoCompleted (); 
                break; 
            case ga_victory: 
                F.StartFinale (); 
                break; 
            case ga_worlddone: 
                DoWorldDone (); 
                break; 
            case ga_screenshot: 
                ScreenShot (); 
                gameaction = gameaction_t.ga_nothing; 
                break; 
            case ga_nothing: 
                break; 
            } 
        }

        // get commands, check consistancy,
        // and build new consistancy check
        buf = (gametic/ticdup)%BACKUPTICS; 

        for (i=0 ; i<MAXPLAYERS ; i++)
        {
            if (playeringame[i]) 
            { 
                cmd = players[i].cmd; 
                //System.out.println("Current command:"+cmd);

                //memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
                netcmds[i][buf].copyTo(cmd);

                // MAES: this is where actual demo commands are being issued or created!
                // Essentially, a demo is a sequence of stored ticcmd_t with a header.
                // Knowing that, it's possible to objectify it.
                if (demoplayback) 
                    ReadDemoTiccmd (cmd); 
                if (demorecording) 
                    WriteDemoTiccmd (cmd);

                // check for turbo cheats
                if (cmd.forwardmove > TURBOTHRESHOLD 
                        && ((gametic&31)==0) && ((gametic>>5)&3) == i )
                {

                    //extern char *player_names[4];
                    //sprintf (turbomessage, "%s is turbo!",player_names[i]);
                    players[consoleplayer].message = hu.HU.player_names[i]+turbomessage;
                }

                if (netgame && !netdemo && (gametic%ticdup)==0 ) 
                { 
                    if (gametic > BACKUPTICS 
                            && consistancy[i][buf] != cmd.consistancy) 
                    { 
                        I.Error ("consistency failure (%d should be %d)",
                            cmd.consistancy, consistancy[i][buf]); 
                    } 
                    if (players[i].mo!=null) 
                        consistancy[i][buf] = (short) players[i].mo.x; 
                    else 
                        consistancy[i][buf] = (short) RND.getIndex(); 
                } 
            }
        }

        // check for special buttons
        for (i=0 ; i<MAXPLAYERS ; i++)
        {
            if (playeringame[i]) 
            { 
                if ((players[i].cmd.buttons & BT_SPECIAL)!=0) 
                { 
                    switch (players[i].cmd.buttons & BT_SPECIALMASK) 
                    { 
                    case BTS_PAUSE:
                        // MAES: fixed stupid ^pause bug.
                        paused = !paused; 
                        if (paused)
                            S.PauseSound ();
                        else
                            S.ResumeSound (); 
                        break; 

                    case BTS_SAVEGAME: 
                        if (savedescription==null) 
                            savedescription=new String( "NET GAME"); 
                        savegameslot =  
                            (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; 
        gameaction = gameaction_t.ga_savegame; 
        break; 
                    } 
                } 
            }
        }

        // do main actions
        switch (gamestate) 
        { 
        case GS_LEVEL: 
            P.Ticker (); 
            ST.Ticker (); 
            AM.Ticker (); 
            HU.Ticker ();            
            break; 

        case GS_INTERMISSION: 
            WI.Ticker (); 
            break; 

        case GS_FINALE: 
            F.Ticker (); 
            break; 

        case GS_DEMOSCREEN: 
            PageTicker (); 
            break; 
        }        
    } 


    //
    // PLAYER STRUCTURE FUNCTIONS
    // also see P_SpawnPlayer in P_Things
    //

    /**
     * G_InitPlayer
     * Called at the start.
     * Called by the game initialization functions.
     *
     * MAES: looks like dead code. It's never called.
     *
     */

    protected void InitPlayer (int player) 
    { 
        player_t   p; 

        // set up the saved info         
        p = players[player]; 

        // clear everything else to defaults 
        p.PlayerReborn (); 

    } 
    
    //
    // G_CheckSpot  
    // Returns false if the player cannot be respawned
    // at the given mapthing_t spot  
    // because something is occupying it 
    //
    //void P_SpawnPlayer (mapthing_t* mthing); 

    private boolean
    CheckSpot
    ( int       playernum,
            mapthing_t   mthing ) 
    { 
        int     x,y; // fixed_t 
        subsector_t    ss; 
        int        an; // angle 
        mobj_t     mo; 


        if (players[playernum].mo==null)
        {
            // first spawn of level, before corpses
            for (int i=0 ; i<playernum ; i++)
                if (players[i].mo.x == mthing.x << FRACBITS
                        && players[i].mo.y == mthing.y << FRACBITS)
                    return false;   
            return true;
        }

        x = mthing.x << FRACBITS; 
        y = mthing.y << FRACBITS; 

        if (!P.CheckPosition (players[playernum].mo, x, y) )
            return false;

        // flush an old corpse if needed 
        if (bodyqueslot >= BODYQUESIZE) 
            P.RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]); 
        bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo; 
        bodyqueslot++; 

        // spawn a teleport fog 
        ss = LL.PointInSubsector (x,y); 
        // Angles stored in things are supposed to be "sanitized" against rollovers.
        an = (int) (( ANG45 * (mthing.angle/45) ) >>> ANGLETOFINESHIFT); 

        mo = P.SpawnMobj (x+20*finecosine[an], y+20*finesine[an] 
                                                             , ss.sector.floorheight 
                                                             , mobjtype_t.MT_TFOG); 

        if (players[consoleplayer].viewz != 1) ; 
        S.StartSound (mo, sfxenum_t.sfx_telept);  // don't start sound on first frame 

        return true; 
    } 


    //
    // G_DeathMatchSpawnPlayer 
    // Spawns a player at one of the random death match spots 
    // called at level load and each death 
    //
    @Override
    public void DeathMatchSpawnPlayer (int playernum) 
    { 
        int             i,j; 
        int             selections; 

        selections = deathmatch_p; 
        if (selections < 4) 
            I.Error ("Only %d deathmatch spots, 4 required", selections); 

        for (j=0 ; j<20 ; j++) 
        { 
            i = RND.P_Random() % selections; 
            if (CheckSpot (playernum, deathmatchstarts[i]) ) 
            { 
                deathmatchstarts[i].type = (short) (playernum+1); 
                P.SpawnPlayer (deathmatchstarts[i]); 
                return; 
            } 
        } 

        // no good spot, so the player will probably get stuck
        // MAES: seriously, fuck him.
        P.SpawnPlayer (playerstarts[playernum]); 
    } 

    //
    // G_DoReborn 
    // 

    public void DoReborn (int playernum) 
    { 
        int                             i; 

        if (!netgame)
        {
            // reload the level from scratch
            gameaction = gameaction_t.ga_loadlevel;  
        }
        else 
        {
            // respawn at the start

            // first dissasociate the corpse 
            players[playernum].mo.player = null;   

            // spawn at random spot if in death match 
            if (deathmatch) 
            { 
                DeathMatchSpawnPlayer (playernum); 
                return; 
            } 

            if (CheckSpot (playernum, playerstarts[playernum]) ) 
            { 
                P.SpawnPlayer (playerstarts[playernum]); 
                return; 
            }

            // try to spawn at one of the other players spots 
            for (i=0 ; i<MAXPLAYERS ; i++)
            {
                if (CheckSpot (playernum, playerstarts[i]) ) 
                { 
                    playerstarts[i].type = (short) (playernum+1); // fake as other player 
                    P.SpawnPlayer (playerstarts[i]); 
                    playerstarts[i].type = (short) (i+1);     // restore 
                    return; 
                }       
                // he's going to be inside something.  Too bad.
                // MAES: Yeah, they're like, fuck him.
            }
            P.SpawnPlayer (playerstarts[playernum]); 
        } 
    } 

    /** DOOM Par Times [4][10] */
    final int[][] pars = 
    { 
            {0}, 
            {0,30,75,120,90,165,180,180,30,165}, 
            {0,90,90,90,120,90,360,240,30,170}, 
            {0,90,45,90,150,90,90,165,30,135} 
    }; 

    /** DOOM II Par Times */
    final int[] cpars =
    {
            30,90,120,120,90,150,120,120,270,90,    //  1-10
            210,150,150,150,210,150,420,150,210,150,    // 11-20
            240,150,180,150,150,300,330,420,300,180,    // 21-30
            120,30                  // 31-32
    };


    //
    // G_DoCompleted 
    //
    boolean     secretexit; 

    public final void ExitLevel () 
    { 
        secretexit = false; 
        gameaction = gameaction_t.ga_completed; 
    } 

    // Here's for the german edition.
    public void SecretExitLevel () 
    { 
        // IF NO WOLF3D LEVELS, NO SECRET EXIT!
        if ( isCommercial()
                && (W.CheckNumForName("MAP31")<0))
            secretexit = false;
        else
            secretexit = true; 
        gameaction =  gameaction_t.ga_completed; 
    } 

    protected void DoCompleted () 
    { 
        int             i; 

        gameaction =  gameaction_t.ga_nothing; 

        for (i=0 ; i<MAXPLAYERS ; i++) 
            if (playeringame[i]) 
                players[i].PlayerFinishLevel ();        // take away cards and stuff 

        if (automapactive) 
            AM.Stop (); 

        if ( !isCommercial())
            switch(gamemap)
            {
            case 8:
                // MAES: end of episode
                gameaction =  gameaction_t.ga_victory;
                return;
            case 9:
                // MAES: end of secret level
                for (i=0 ; i<MAXPLAYERS ; i++) 
                    players[i].didsecret = true; 
                break;
            }

        /*  Hmmm - why? MAES: Clearly redundant.
        if ( (gamemap == 8)
                && (!isCommercial()) ) 
        {
            // victory 
            gameaction =  gameaction_t.ga_victory; 
            return; 
        } 

        if ( (gamemap == 9)
                && !isCommercial() ) 
        {
            // exit secret level 
            for (i=0 ; i<MAXPLAYERS ; i++) 
                players[i].didsecret = true; 
        } 
         */


        wminfo.didsecret = players[consoleplayer].didsecret; 
        wminfo.epsd = gameepisode -1; 
        wminfo.last = gamemap -1;

        // wminfo.next is 0 biased, unlike gamemap
        if ( isCommercial())
        {
            if (secretexit)
                switch(gamemap)
                {
                case 15: wminfo.next = 30; break;
                case 31: wminfo.next = 31; break;
                }
            else
                switch(gamemap)
                {
                case 31:
                case 32: wminfo.next = 15; break;
                default: wminfo.next = gamemap;
                }
        }
        else
        {
            if (secretexit) 
                wminfo.next = 8;    // go to secret level 
            else if (gamemap == 9) 
            {
                // returning from secret level 
                switch (gameepisode) 
                { 
                case 1: 
                    wminfo.next = 3; 
                    break; 
                case 2: 
                    wminfo.next = 5; 
                    break; 
                case 3: 
                    wminfo.next = 6; 
                    break; 
                case 4:
                    wminfo.next = 2;
                    break;
                }                
            } 
            else 
                wminfo.next = gamemap;          // go to next level 
        }

        wminfo.maxkills = totalkills; 
        wminfo.maxitems = totalitems; 
        wminfo.maxsecret = totalsecret; 
        wminfo.maxfrags = 0; 
        if ( isCommercial() )
            wminfo.partime = 35*cpars[gamemap-1]; 
        else if (gameepisode >= pars.length)
            wminfo.partime = 0;
        else
            wminfo.partime = 35*pars[gameepisode][gamemap]; 
        wminfo.pnum = consoleplayer; 

        for (i=0 ; i<MAXPLAYERS ; i++) 
        { 
            wminfo.plyr[i].in = playeringame[i]; 
            wminfo.plyr[i].skills = players[i].killcount; 
            wminfo.plyr[i].sitems = players[i].itemcount; 
            wminfo.plyr[i].ssecret = players[i].secretcount; 
            wminfo.plyr[i].stime = leveltime;
            C2JUtils.memcpy (wminfo.plyr[i].frags, players[i].frags 
                , wminfo.plyr[i].frags.length); 
        } 

        gamestate = gamestate_t.GS_INTERMISSION; 
        viewactive = false; 
        automapactive = false; 

        if (statcopy!=null)
            C2JUtils.memcpy (statcopy, wminfo,1);

        WI.Start (wminfo); 
    } 


    /**
     * G_WorldDone 
     */

    public void WorldDone () 
    { 
        gameaction = gameaction_t.ga_worlddone; 

        if (secretexit) 
            players[consoleplayer].didsecret = true; 

        if ( isCommercial() )
        {
            switch (gamemap)
            {
            case 15:
            case 31:
                if (!secretexit)
                    break;
            case 6:
            case 11:
            case 20:
            case 30:
                F.StartFinale ();
                break;
            }
        }
    } 

    public void DoWorldDone () 
    {        
        gamestate = gamestate_t.GS_LEVEL; 
        gamemap = wminfo.next+1; 
        DoLoadLevel (); 
        gameaction = gameaction_t.ga_nothing; 
        viewactive = true; 
    } 



    //
    // G_InitFromSavegame
    // Can be called by the startup code or the menu task. 
    //
    //extern boolean setsizeneeded;
    //void R_ExecuteSetViewSize (void);

    String    savename;

    public void LoadGame (String name) 
    { 
        savename=new String(name); 
        gameaction = gameaction_t.ga_loadgame; 
    } 



    /** This is fugly. Making a "savegame object" will make at least certain comparisons
     *  easier, and avoid writing code twice.
     */

    protected void DoLoadGame () 
    { 
        try{ 
            int     i;  
            StringBuffer vcheck=new StringBuffer();
            VanillaDSGHeader header=new VanillaDSGHeader();
            IDoomSaveGame dsg=new VanillaDSG();
            dsg.updateStatus(this.DM);
            
            gameaction = gameaction_t.ga_nothing; 

            DataInputStream f=new DataInputStream(new BufferedInputStream(new FileInputStream(savename))); 

            header.read(f);
            f.close();
    
            // skip the description field 
            vcheck.append("version ");
            vcheck.append(VERSION);

            if (vcheck.toString().compareTo(header.getVersion())!=0) {
            	f.close();
            	return; // bad version
            }

            // Ok so far, reopen stream.
            f=new DataInputStream(new BufferedInputStream(new FileInputStream(savename)));        
            gameskill = header.getGameskill(); 
            gameepisode = header.getGameepisode(); 
            gamemap = header.getGamemap(); 
            for (i=0 ; i<MAXPLAYERS ; i++) 
                playeringame[i] = header.getPlayeringame()[i]; 

            // load a base level 
            InitNew (gameskill, gameepisode, gamemap); 
            
            if (gameaction == gameaction_t.ga_failure) {
            	// failure to load. Abort.
            	f.close();
            	return;
            }

            gameaction = gameaction_t.ga_nothing;
            
            // get the times 
            leveltime = header.getLeveltime(); 

            // dearchive all the modifications
            boolean ok=dsg.doLoad(f);
            f.close();

            // MAES: this will cause a forced exit.
            // The problem is that the status will have already been altered 
            // (perhaps VERY badly) so it makes no sense to progress.
            // If you want it bullet-proof, you could implement
            // a "tentative loading" subsystem, which will only alter the game
            // if everything works out without errors. But who cares :-p
            if (!ok) 
                I.Error("Bad savegame");

            // done 
            //Z_Free (savebuffer); 

            if (R.getSetSizeNeeded())
                R.ExecuteSetViewSize ();

            // draw the pattern into the back screen
            R.FillBackScreen ();   
        } catch (Exception e){
            e.printStackTrace();
        }
    } 


    //
    // G_SaveGame
    // Called by the menu task.
    // Description is a 24 byte text string 
    //
    public void
    SaveGame
    ( int   slot,
            String description ) 
    { 
        savegameslot = slot; 
        savedescription=new String(description); 
        sendsave = true; 
    } 

    protected void DoSaveGame () 
    { 
     
        try{
     String    name; 
     //char[]    name2=new char[VERSIONSIZE]; 
     String   description;
     StringBuffer build=new StringBuffer();
     IDoomSaveGameHeader header=new VanillaDSGHeader();
     IDoomSaveGame dsg=new VanillaDSG();
     dsg.updateStatus(this.DM);
     
     if (eval(CM.CheckParm("-cdrom"))) {
         build.append("c:\\doomdata\\");
         build.append(SAVEGAMENAME);
         build.append("%d.dsg");
     } else {         
      build.append(SAVEGAMENAME);
      build.append("%d.dsg"); 
     }
     
     name=String.format(build.toString(), savegameslot);
     
     description = savedescription; 

     header.setName(description);
     header.setVersion(String.format("version %d",VERSION));
     header.setGameskill(gameskill);
     header.setGameepisode(gameepisode);
     header.setGamemap(gamemap);
     header.setPlayeringame(playeringame);
     header.setLeveltime(leveltime);
     dsg.setHeader(header);
     
     // Try opening a save file. No intermediate buffer (performance?)
     DataOutputStream f=new DataOutputStream(new FileOutputStream(name));
     boolean ok=dsg.doSave(f);
     f.close();
        } catch (Exception e){
            e.printStackTrace();
        }
     // Saving is not as destructive as loading.
     
     gameaction = gameaction_t.ga_nothing; 
        savedescription = "";      

        players[consoleplayer].message = GGSAVED; 

        // draw the pattern into the back screen
        R.FillBackScreen ();
     

    } 



    skill_t d_skill; 
    int     d_episode; 
    int     d_map; 

    public void
    DeferedInitNew
    ( skill_t   skill,
            int       episode,
            int       map) 
    { 
        d_skill = skill; 
        d_episode = episode; 
        d_map = map; 
        gameaction = gameaction_t.ga_newgame; 
    } 


    public void DoNewGame () 
    {
        demoplayback = false; 
        netdemo = false;
        netgame = false;
        deathmatch = false;
        playeringame[1] = playeringame[2] = playeringame[3] = false;
        respawnparm = false;
        fastparm = false;
        nomonsters = false;
        consoleplayer = 0;
        InitNew (d_skill, d_episode, d_map); 
        gameaction = gameaction_t.ga_nothing; 
    } 


    /**
     * G_InitNew
     * Can be called by the startup code or the menu task,
     * consoleplayer, displayplayer, playeringame[] should be set. 
     */

    public void InitNew
    ( skill_t   skill,
            int       episode,
            int       map ) 
    { 
        int             i; 

        if (paused) 
        { 
            paused = false; 
            S.ResumeSound (); 
        } 


        if (skill.ordinal() > skill_t.sk_nightmare.ordinal()) 
            skill = skill_t.sk_nightmare;


        // This was quite messy with SPECIAL and commented parts.
        // Supposedly hacks to make the latest edition work.
        // It might not work properly.
        if (episode < 1)
            episode = 1; 

        if ( isRetail() )
        {
            if (episode > 4)
                episode = 4;
        }
        else if ( isShareware() )
        {
            if (episode > 1) 
                episode = 1; // only start episode 1 on shareware
        }  
        else
        {
            if (episode > 3)
                episode = 3;
        }



        if (map < 1) 
            map = 1;

        if ( (map > 9)
                && ( !isCommercial()))
            map = 9; 

        RND.ClearRandom (); 

        if (skill == skill_t.sk_nightmare || respawnparm )
            respawnmonsters = true;
        else
            respawnmonsters = false;

        // If on nightmare/fast monsters make everything MOAR pimp.

        if (fastparm || (skill == skill_t.sk_nightmare && gameskill != skill_t.sk_nightmare) )
        { 
            for (i=statenum_t.S_SARG_RUN1.ordinal() ; i<=statenum_t.S_SARG_PAIN2.ordinal() ; i++) 
                states[i].tics >>= 1; 
            mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 20*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 20*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 20*MAPFRACUNIT; 
        } 
        else if (skill != skill_t.sk_nightmare && gameskill == skill_t.sk_nightmare) 
        { 
            for (i=statenum_t.S_SARG_RUN1.ordinal() ; i<=statenum_t.S_SARG_PAIN2.ordinal() ; i++) 
                states[i].tics <<= 1; 
            mobjinfo[mobjtype_t.MT_BRUISERSHOT.ordinal()].speed = 15*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_HEADSHOT.ordinal()].speed = 10*MAPFRACUNIT; 
            mobjinfo[mobjtype_t.MT_TROOPSHOT.ordinal()].speed = 10*MAPFRACUNIT; 
        } 


        // force players to be initialized upon first level load         
        for (i=0 ; i<MAXPLAYERS ; i++) 
            players[i].playerstate = PST_REBORN; 

        usergame = true;                // will be set false if a demo 
        paused = false; 
        demoplayback = false; 
        automapactive = false; 
        viewactive = true; 
        gameepisode = episode; 
        gamemap = map; 
        gameskill = skill; 

        viewactive = true;

        // set the sky map for the episode
        if ( isCommercial())
        {
            TM.setSkyTexture(TM.TextureNumForName ("SKY3"));
            if (gamemap < 12)
                TM.setSkyTexture(TM.TextureNumForName ("SKY1"));
            else
                if (gamemap < 21)
                    TM.setSkyTexture(TM.TextureNumForName ("SKY2"));
        }
        else
            switch (episode) 
            { 
            case 1: 
                TM.setSkyTexture(TM.TextureNumForName ("SKY1")); 
                break; 
            case 2: 
                TM.setSkyTexture(TM.TextureNumForName ("SKY2"));
                break; 
            case 3: 
                TM.setSkyTexture(TM.TextureNumForName ("SKY3")); 
                break; 
            case 4:   // Special Edition sky
                TM.setSkyTexture(TM.TextureNumForName ("SKY4"));
                break;
            } 

        if (!DoLoadLevel ()) levelLoadFailure();
    } 

    protected void levelLoadFailure(){
    	boolean endgame=I.GenerateAlert(Strings.LEVEL_FAILURE_TITLE, Strings.LEVEL_FAILURE_CAUSE);
    	
    	if (endgame){         	// Initiate endgame
    	gameaction=gameaction_t.ga_failure;
    	gamestate = gamestate_t.GS_DEMOSCREEN; 
    	M.ClearMenus();
    	StartTitle();
    	} else {
    	// Shutdown immediately.
    	I.Quit();	
    	}
    }

    //
    // DEMO RECORDING 
    // 
    public  void ReadDemoTiccmd (ticcmd_t cmd) 
    { 
        IDemoTicCmd democmd=demobuffer.getNextTic();
        if (democmd == null) 
        {
            // end of demo data stream 
            CheckDemoStatus ();

            // Force status resetting
            this.demobuffer.resetDemo();
            return; 
        }

        democmd.decode(cmd); 
    } 

    public void WriteDemoTiccmd (ticcmd_t cmd) 
    { 
        if (gamekeydown['q'])           // press q to end demo recording 
            CheckDemoStatus ();
        IDemoTicCmd reccmd=new VanillaTiccmd();
        reccmd.encode(cmd);     
        demobuffer.putTic(reccmd);

        // MAES: Useless, we can't run out of space anymore (at least not in theory).

        /*   demobuffer[demo_p++] = cmd.forwardmove; 
     demobuffer[demo_p++] = cmd.sidemove; 
     demobuffer[demo_p++] = (byte) ((cmd.angleturn+128)>>8); 
     demobuffer[demo_p++] = (byte) cmd.buttons; 
     demo_p -= 4; 
     if (demo_p > demoend - 16)
     {
     // no more space 
     CheckDemoStatus (); 
     return; 
     } */ 

        //ReadDemoTiccmd (cmd);         // make SURE it is exactly the same
        // MAES: this is NOT the way to do in Mocha, because we are not manipulating
        // the demo index directly anymore. Instead, decode what we have just saved.     
        reccmd.decode(cmd);

    } 



    /**
     * G_RecordDemo 
     */ 
    public void RecordDemo (String name) 
    { 

        StringBuffer buf=new StringBuffer();
        usergame = false; 
        buf.append(name); 
        buf.append(".lmp");
        demoname=buf.toString();
        demobuffer = new VanillaDoomDemo(); 
        demorecording = true; 
    } 


    public void BeginRecording () 
    {         
        demobuffer.setVersion(VERSION);
        demobuffer.setSkill(gameskill); 
        demobuffer.setEpisode(gameepisode);
        demobuffer.setMap(gamemap);
        demobuffer.setDeathmatch(deathmatch);
        demobuffer.setRespawnparm(respawnparm);
        demobuffer.setFastparm(fastparm);
        demobuffer.setNomonsters(nomonsters);
        demobuffer.setConsoleplayer(consoleplayer);
        demobuffer.setPlayeringame(playeringame);
    }
    
    String   defdemoname;
    
    /**
     * G_PlayDemo 
     */

    public void DeferedPlayDemo (String name) 
    { 
        defdemoname = name; 
        gameaction = gameaction_t.ga_playdemo; 
    } 

    public void DoPlayDemo () 
    { 

        skill_t skill;
        boolean fail=false;
        int             i, episode, map; 

        gameaction = gameaction_t.ga_nothing;
        // MAES: Yeah, it's OO all the way now, baby ;-)
        try {
        demobuffer = (IDoomDemo) W.CacheLumpName(defdemoname.toUpperCase(), PU_STATIC,VanillaDoomDemo.class);
        } catch (Exception e){
            fail=true;
        }
        
        fail=(demobuffer.getSkill()==null);

        if (fail || demobuffer.getVersion()!= VERSION)
        {
            System.err.println("Demo is from a different game version!\n");
            System.err.println("Version code read: "+demobuffer.getVersion());
            gameaction = gameaction_t.ga_nothing;
            return;
        }

        skill = demobuffer.getSkill(); 
        episode = demobuffer.getEpisode(); 
        map = demobuffer.getMap(); 
        deathmatch = demobuffer.isDeathmatch();
        respawnparm = demobuffer.isRespawnparm();
        fastparm = demobuffer.isFastparm();
        nomonsters = demobuffer.isNomonsters();
        consoleplayer = demobuffer.getConsoleplayer();
        // Do this, otherwise previously loaded demos will be stuck at their end.
        demobuffer.resetDemo();
        
        boolean[] pigs=demobuffer.getPlayeringame();
        for (i=0 ; i<MAXPLAYERS ; i++) 
            playeringame[i] = pigs[i]; 
        if (playeringame[1]) 
        {
            netgame = true; 
            netdemo = true; 
        }

        // don't spend a lot of time in loadlevel 
        precache = false;
        InitNew (skill, episode, map); 
        precache = true;

        usergame = false; 
        demoplayback = true; 

    }

    //
    // G_TimeDemo 
    //
    public void TimeDemo (String name) 
    {    
        nodrawers = CM.CheckParm ("-nodraw")!=0; 
        noblit = CM.CheckParm ("-noblit")!=0; 
        timingdemo = true; 
        singletics = true;
        defdemoname = name;
        gameaction = gameaction_t.ga_playdemo; 
    } 

    
    /** G_CheckDemoStatus
     * 
     * Called after a death or level completion to allow demos to be cleaned up
     * Returns true if a new demo loop action will take place
     *  
     */ 

    public boolean CheckDemoStatus () 
    { 
        int endtime; 
        
        if (timingdemo) 
        {
            endtime = RealTime.GetTime ();
            // killough -- added fps information and made it work for longer demos:
            long realtics=endtime-starttime;    
            
            this.commit();
            VM.SaveDefaults(VM.getDefaultFile());
            I.Error ("timed %d gametics in %d realtics = %f frames per second",gametic 
                , realtics, gametic*(double)(TICRATE)/realtics); 
        }

        if (demoplayback) 
        {
            if (singledemo) 
                I.Quit ();

            // Z_ChangeTag (demobuffer, PU_CACHE); 
            demoplayback = false; 
            netdemo = false;
            netgame = false;
            deathmatch = false;
            playeringame[1] = playeringame[2] = playeringame[3] = false;
            respawnparm = false;
            fastparm = false;
            nomonsters = false;
            consoleplayer = 0;
            AdvanceDemo (); 
            return true; 
        } 

        if (demorecording) 
        { 
            //demobuffer[demo_p++] = (byte) DEMOMARKER; 

            MenuMisc.WriteFile(demoname, demobuffer); 
            //Z_Free (demobuffer); 
            demorecording = false; 
            I.Error ("Demo %s recorded",demoname); 
        } 

        return false; 
    } 

    /** This should always be available for real timing */
    protected ITicker RealTime;
    
    public DoomMain(){
        // Init game status...
        super();
        C2JUtils.initArrayOfObjects(events,event_t.class);
        this.I=new DoomSystem();
        gamestate=gamestate_t.GS_DEMOSCREEN;        
        
        this.RealTime=new MilliTicker();
    }

   
    
    protected void updateStatusHolders(DoomStatus<T,V> DS){
        for (DoomStatusAware dsa : status_holders){
            dsa.updateStatus(DS);
        }
    }

    /** A list with objects that do hold status and need to be aware of 
     *  it.
     */
    protected List<DoomStatusAware> status_holders;

 


    /* Since it's so intimately tied, it's less troublesome to merge the "main" and "network"
     *  code. 
     * 
     */


    /** To be initialized by the DoomNetworkingInterface via a setter */
    //private  doomcom_t   doomcom;   
    //private  doomdata_t  netbuffer;      // points inside doomcom
    protected StringBuilder sb=new StringBuilder();


    //
    // NETWORKING
    //
    // gametic is the tic about to (or currently being) run
    // maketic is the tick that hasn't had control made for it yet
    // nettics[] has the maketics for all players 
    //
    // a gametic cannot be run until nettics[] > gametic for all players
    //

    //ticcmd_t[]  localcmds= new ticcmd_t[BACKUPTICS];

    //ticcmd_t [][]       netcmds=new ticcmd_t [MAXPLAYERS][BACKUPTICS];
    int[]           nettics=new int[MAXNETNODES];
    boolean[]       nodeingame=new boolean[MAXNETNODES];        // set false as nodes leave game
    boolean[]       remoteresend=new boolean[MAXNETNODES];      // set when local needs tics
    int[]       resendto=new int[MAXNETNODES];          // set when remote needs tics
    int[]       resendcount=new int[MAXNETNODES];

    int[]       nodeforplayer=new int[MAXPLAYERS];

    int             maketic;
    int     lastnettic;
    int     skiptics;
    protected int     ticdup;


    public int getTicdup() {
        return ticdup;
    }


    public void setTicdup(int ticdup) {
        this.ticdup = ticdup;
    }






    int     maxsend; // BACKUPTICS/(2*ticdup)-1;


    //void D_ProcessEvents (void); 
    //void G_BuildTiccmd (ticcmd_t *cmd); 
    //void D_DoAdvanceDemo (void);

    // _D_
    boolean     reboundpacket = false;
    doomdata_t  reboundstore = new doomdata_t();


    // 
    //
    //123

    /** MAES: interesting. After testing it was found to return the following size:
     *  (8*(netbuffer.numtics+1));
     */

    int NetbufferSize ()
    {
        //    return (int)(((doomdata_t)0).cmds[netbuffer.numtics]);
        return (8*(netbuffer.numtics+1));
    }


    protected long NetbufferChecksum ()
    {
        long        c;
        int     i,l;

        c = 0x1234567L;

        // FIXME -endianess?
        if (NORMALUNIX)
            return 0;           // byte order problems


        /* Here it was trying to get the length of a doomdata_t struct up to retransmit from.
         * l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
         * (int)&(((doomdata_t *)0)->retransmitfrom) evaluates to "4"
         * Therefore, l= (netbuffersize - 4)/4
         * 
         */
        l = (NetbufferSize () - 4)/4;
        for (i=0 ; i<l ; i++)
            // TODO: checksum would be better computer in the netbuffer itself.
            // The C code actually takes all fields into account.
            c += 0;// TODO: (netbuffer->retransmitfrom)[i] * (i+1);

        return c & NCMD_CHECKSUM;
    }
    //
    //
    //
    protected int ExpandTics (int low)
    {
        int delta;

        delta = low - (maketic&0xff);

        if (delta >= -64 && delta <= 64)
            return (maketic&~0xff) + low;
        if (delta > 64)
            return (maketic&~0xff) - 256 + low;
        if (delta < -64)
            return (maketic&~0xff) + 256 + low;

        I.Error ("ExpandTics: strange value %d at maketic %d",low,maketic);
        return 0;
    }



    /**
     * HSendPacket
     *
     * Will send out a packet to all involved parties. A special
     * case is the rebound storage, which acts as a local "echo"
     * which is then picked up by the host itself. This is
     * necessary to simulate a 1-node network.
     * @throws IOException 
     *
     */
    void
    HSendPacket
    (int   node,
            int   flags )
    {
        netbuffer.checksum = (int) (NetbufferChecksum () | flags);

        // Local node's comms are sent to rebound packet, which is 
        // then picked up again. THIS IS VITAL FOR SINGLE-PLAYER
        // SPEED THROTTLING TOO, AS IT RELIES ON NETWORK ACKS/BUSY
        // WAITING.
        if (node==0)
        {
            // _D_
            reboundstore.copyFrom(netbuffer);
            reboundpacket = true;
            return;
        }

        if (DM.demoplayback)
            return;

        if (!DM.netgame)
            I.Error ("Tried to transmit to another node");

        doomcom.command = CMD_SEND;
        doomcom.remotenode = (short) node;
        doomcom.datalength = (short) NetbufferSize ();

        if (DM.debugfile!=null)
        {
            int     i;
            int     realretrans;
            if (flags(netbuffer.checksum , NCMD_RETRANSMIT))
                realretrans = ExpandTics (netbuffer.retransmitfrom);
            else
                realretrans = -1;

            logger(debugfile,"send ("+ExpandTics(netbuffer.starttic)+", "+netbuffer.numtics + ", R "+
                realretrans+ "["+ doomcom.datalength+"]");

            for (i=0 ; i<doomcom.datalength ; i++)

                // TODO: get a serialized string representation.
            	logger(debugfile,netbuffer.toString()+"\n");
        }

        // This should execute a "send" command for the current stuff in doomcom.
        DNI.NetCmd ();
    }

    //
    // HGetPacket
    // Returns false if no packet is waiting
    //
    private boolean HGetPacket () 
    {   
        // Fugly way of "clearing" the buffer.
        sb.setLength(0);
        if (reboundpacket)
        {
            // FIXME: MAES: this looks like a struct copy 
            netbuffer.copyFrom(reboundstore);
            doomcom.remotenode = 0;
            reboundpacket = false;
            return true;
        }

        // If not actually a netgame (e.g. single player, demo) return.
        if (!DM.netgame)
            return false;

        if (DM.demoplayback)
            return false;

        doomcom.command = CMD_GET;
        DNI.NetCmd ();

        // Invalid node?
        if (doomcom.remotenode == -1)
            return false;

        if (doomcom.datalength != NetbufferSize ())
        {
            if (eval(debugfile))
            	logger(debugfile,"bad packet length "+doomcom.datalength+"\n");
            return false;
        }

        if (NetbufferChecksum () != (netbuffer.checksum&NCMD_CHECKSUM) )
        {
            if (eval(debugfile))
            	logger(debugfile,"bad packet checksum\n");
            return false;
        }

        if (eval(debugfile))
        {
            int     realretrans;
            int i;

            if (flags(netbuffer.checksum , NCMD_SETUP))
            	logger(debugfile,"setup packet\n");
            else
            {
                if (flags(netbuffer.checksum , NCMD_RETRANSMIT))
                    realretrans = ExpandTics (netbuffer.retransmitfrom);
                else
                    realretrans = -1;

                sb.append("get ");
                sb.append(doomcom.remotenode);
                sb.append(" = (");
                sb.append(ExpandTics(netbuffer.starttic));
                sb.append(" + ");
                sb.append(netbuffer.numtics);
                sb.append(", R ");
                sb.append(realretrans);
                sb.append(")[");
                sb.append(doomcom.datalength);
                sb.append("]");

                logger(debugfile,sb.toString());

                // Trick: force update of internal buffer.
                netbuffer.pack();

                /* TODO: Could it be actually writing stuff beyond the boundaries of a single doomdata object?
                 * A doomcom object has a lot of header info, and a single "raw" data placeholder, which by now
                 * should be inside netbuffer....right?
                 * 
                 * 
                 */

                try{
                    for (i=0 ; i<doomcom.datalength ; i++) {
                    	debugfile.write(Integer.toHexString(netbuffer.cached()[i]));
                    	debugfile.write('\n');
                    }
                }
                catch( IOException e){
                    // "Drown" IOExceptions here.
                }
            }
        }
        return true;    
    }


    ////    GetPackets

    StringBuilder exitmsg=new StringBuilder(80);

    public void GetPackets () 
    {
        int     netconsole;
        int     netnode;
        ticcmd_t    src, dest;
        int     realend;
        int     realstart;

        while ( HGetPacket() )
        {
            if (flags(netbuffer.checksum , NCMD_SETUP))
                continue;       // extra setup packet

            netconsole = netbuffer.player & ~PL_DRONE;
            netnode = doomcom.remotenode;

            // to save bytes, only the low byte of tic numbers are sent
            // Figure out what the rest of the bytes are
            realstart = ExpandTics (netbuffer.starttic);        
            realend = (realstart+netbuffer.numtics);

            // check for exiting the game
            if (flags(netbuffer.checksum , NCMD_EXIT))
            {
                if (!nodeingame[netnode])
                    continue;
                nodeingame[netnode] = false;
                playeringame[netconsole] = false;
                exitmsg.insert(0, "Player 1 left the game");
                exitmsg.setCharAt(7,(char) (exitmsg.charAt(7)+netconsole));
                players[consoleplayer].message = exitmsg.toString();
                if (demorecording)
                    DM.CheckDemoStatus ();
                continue;
            }

            // check for a remote game kill
            if (flags(netbuffer.checksum , NCMD_KILL))
                I.Error ("Killed by network driver");

            nodeforplayer[netconsole] = netnode;

            // check for retransmit request
            if ( resendcount[netnode] <= 0 
                    && flags(netbuffer.checksum , NCMD_RETRANSMIT) )
            {
                resendto[netnode] = ExpandTics(netbuffer.retransmitfrom);
                if (eval(debugfile)){
                    sb.setLength(0);
                    sb.append("retransmit from ");
                    sb.append(resendto[netnode]);
                    sb.append('\n');
                    logger(debugfile,sb.toString());
                    resendcount[netnode] = RESENDCOUNT;
                }
            }
            else
                resendcount[netnode]--;

            // check for out of order / duplicated packet       
            if (realend == nettics[netnode])
                continue;

            if (realend < nettics[netnode])
            {
                if (eval(debugfile)){
                    sb.setLength(0);
                    sb.append("out of order packet (");
                    sb.append(realstart);
                    sb.append(" + ");
                    sb.append(netbuffer.numtics);
                    sb.append(")\n");
                    logger(debugfile,sb.toString());
                }
                continue;
            }

            // check for a missed packet
            if (realstart > nettics[netnode])
            {
                // stop processing until the other system resends the missed tics
                if (eval(debugfile)) {
                    sb.setLength(0);
                    sb.append("missed tics from ");
                    sb.append(netnode);
                    sb.append(" (");
                    sb.append(realstart);
                    sb.append(" - ");
                    sb.append(nettics[netnode]);
                    sb.append(")\n");
                    logger(debugfile,sb.toString());
                }
                remoteresend[netnode] = true;
                continue;
            }

            // update command store from the packet
            {
                int     start;

                remoteresend[netnode] = false;

                start = nettics[netnode] - realstart;       
                src = netbuffer.cmds[start];

                while (nettics[netnode] < realend)
                {
                    dest = netcmds[netconsole][nettics[netnode]%BACKUPTICS];
                    nettics[netnode]++;
                    // MAES: this is a struct copy.
                    src.copyTo(dest);
                    // Advance src
                    start++;

                    //_D_: had to add this (see linuxdoom source). That fixed that damn consistency failure!!!
                    if (start < netbuffer.cmds.length)
                        src = netbuffer.cmds[start];

                }
            }
        }
    }

    protected void logger(OutputStreamWriter debugfile, String string) {
    		try {
				debugfile.write(string);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
	}

	int      gametime;

    @Override
    public void NetUpdate ()
    {
        int             nowtime;
        int             newtics;
        int             i,j;
        int             realstart;
        int             gameticdiv;

        // check time
        nowtime = TICK.GetTime ()/ticdup;
        newtics = nowtime - gametime;
        gametime = nowtime;

        if (newtics <= 0)   // nothing new to update
        {
            // listen for other packets
            GetPackets ();
            return;
        } else {


            if (skiptics <= newtics)
            {
                newtics -= skiptics;
                skiptics = 0;
            }
            else
            {
                skiptics -= newtics;
                newtics = 0;
            }

            netbuffer.player = (byte) consoleplayer;

            // build new ticcmds for console player
            gameticdiv = gametic/ticdup;
            for (i=0 ; i<newtics ; i++)
            {
                VI.StartTic ();
                ProcessEvents ();
                if (maketic - gameticdiv >= BACKUPTICS/2-1)
                    break;          // can't hold any more

                //System.out.printf ("mk:%d ",maketic);
                BuildTiccmd (localcmds[maketic%BACKUPTICS]);
                maketic++;
            }


            if (singletics)
                return;         // singletic update is syncronous

            // send the packet to the other nodes
            for (i=0 ; i<doomcom.numnodes ; i++)
                if (nodeingame[i])
                {
                    netbuffer.starttic = (byte) (realstart = resendto[i]);
                    netbuffer.numtics = (byte) (maketic - realstart);
                    if (netbuffer.numtics > BACKUPTICS)
                        I.Error ("NetUpdate: netbuffer.numtics > BACKUPTICS");

                    resendto[i] = maketic - doomcom.extratics;

                    for (j=0 ; j< netbuffer.numtics ; j++)
                        localcmds[(realstart+j)%BACKUPTICS].copyTo(netbuffer.cmds[j]);
                    // MAES: one of _D_ fixes.
                    //netbuffer.cmds[j] = localcmds[(realstart+j)%BACKUPTICS];

                    if (remoteresend[i])
                    {
                        netbuffer.retransmitfrom = (byte) nettics[i];
                        HSendPacket (i, NCMD_RETRANSMIT);
                    }
                    else
                    {
                        netbuffer.retransmitfrom = 0;
                        HSendPacket (i, 0);
                    }
                }
            GetPackets();
        }

    }



    //
    // CheckAbort
    //
    private void CheckAbort ()
    {
        event_t ev;
        int     stoptic;

        stoptic = TICK.GetTime () + 2; 
        while (TICK.GetTime() < stoptic) 
            VI.StartTic (); 

        VI.StartTic ();
        for ( ; eventtail != eventhead 
        ; eventtail = (++eventtail)&(MAXEVENTS-1) ) 
        { 
            ev = events[eventtail]; 
            if (ev.type == evtype_t.ev_keydown && ev.data1 == KEY_ESCAPE)
                I.Error ("Network game synchronization aborted.");
        } 
    }

    boolean[] gotinfo=new boolean[MAXNETNODES];

    /**
     * D_ArbitrateNetStart
     * @throws IOException 
     *
     * 
     */
    public void ArbitrateNetStart () throws IOException
    {
        int     i;
        autostart = true;

        // Clear it up...
        Arrays.fill(gotinfo,false);

        if (doomcom.consoleplayer!=0)
        {
            // listen for setup info from key player
            System.out.println("listening for network start info...\n");
            while (true)
            {
                CheckAbort ();
                if (!HGetPacket ())
                    continue;
                if (flags(netbuffer.checksum , NCMD_SETUP))
                {
                    if (netbuffer.player != VERSION)
                        I.Error ("Different DOOM versions cannot play a net game!");
                    startskill = skill_t.values()[netbuffer.retransmitfrom & 15];

                    // Deathmatch
                    if (((netbuffer.retransmitfrom & 0xc0) >> 6)==1) 
                        deathmatch = true;
                    else
                        // Cooperative
                        if (((netbuffer.retransmitfrom & 0xc0) >> 6)==2) 
                            altdeath = true;

                    nomonsters = (netbuffer.retransmitfrom & 0x20) > 0;
                    respawnparm = (netbuffer.retransmitfrom & 0x10) > 0;
                    startmap = netbuffer.starttic & 0x3f;
                    startepisode = netbuffer.starttic >> 6;
            return;
                }
            }
        }
        else
        {
            // key player, send the setup info
            System.out.println("sending network start info...\n");
            do
            {
                CheckAbort ();
                for (i=0 ; i<doomcom.numnodes ; i++)
                {
                    netbuffer.retransmitfrom = (byte) startskill.ordinal();
                    if (deathmatch)
                        netbuffer.retransmitfrom |= (1<<6);
                    else
                        if (altdeath)
                            netbuffer.retransmitfrom |= (2<<6);
                    if (nomonsters)
                        netbuffer.retransmitfrom |= 0x20;
                    if (respawnparm)
                        netbuffer.retransmitfrom |= 0x10;
                    netbuffer.starttic = (byte) (startepisode * 64 + startmap);
                    netbuffer.player = VERSION;
                    netbuffer.numtics = 0;
                    HSendPacket (i, NCMD_SETUP);
                }

                //#if 1
                for(i = 10 ; (i>0)  &&  HGetPacket(); --i)
                {
                    if((netbuffer.player&0x7f) < MAXNETNODES)
                        gotinfo[netbuffer.player&0x7f] = true;
                }
                /*
        while (HGetPacket ())
        {
        gotinfo[netbuffer.player&0x7f] = true;
        }
                 */

                for (i=1 ; i<doomcom.numnodes ; i++)
                    if (!gotinfo[i])
                        break;
            } while (i < doomcom.numnodes);
        }
    }

    //
    // D_CheckNetGame
    // Works out player numbers among the net participants
    //

    private void CheckNetGame () throws IOException
    {
        int             i;

        for (i=0 ; i<MAXNETNODES ; i++)
        {
            nodeingame[i] = false;
            nettics[i] = 0;
            remoteresend[i] = false;    // set when local needs tics
            resendto[i] = 0;        // which tic to start sending
        }

        // I_InitNetwork sets doomcom and netgame
        DNI.InitNetwork ();
        if (doomcom.id != DOOMCOM_ID)
            I.Error ("Doomcom buffer invalid!");

        // Maes: This is the only place where netbuffer is definitively set to something
        netbuffer = doomcom.data;
        consoleplayer = displayplayer = doomcom.consoleplayer;
        if (netgame)
            ArbitrateNetStart ();

        System.out.printf ("startskill %s  deathmatch: %s  startmap: %d  startepisode: %d\n",
            startskill.toString(), Boolean.toString(deathmatch), startmap, startepisode);

        // read values out of doomcom
        ticdup = doomcom.ticdup;
        // MAES: ticdup must not be zero at this point. Obvious, no?
        maxsend = BACKUPTICS/(2*ticdup)-1;
        if (maxsend<1)
            maxsend = 1;

        for (i=0 ; i<doomcom.numplayers ; i++)
            playeringame[i] = true;
        for (i=0 ; i<doomcom.numnodes ; i++)
            nodeingame[i] = true;

        System.out.printf ("player %d of %d (%d nodes)\n",(consoleplayer+1), doomcom.numplayers, doomcom.numnodes);

    }


    //
    // D_QuitNetGame
    // Called before quitting to leave a net game
    // without hanging the other players
    //
    @Override
    public void QuitNetGame () throws IOException
    {
        int             i, j;

        if (eval(debugfile))
            try {
                debugfile.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
                return;

            // send a bunch of packets for security
            netbuffer.player = (byte) consoleplayer;
            netbuffer.numtics = 0;
            for (i=0 ; i<4 ; i++)
            {
                for (j=1 ; j<doomcom.numnodes ; j++)
                    if (nodeingame[j])
                        HSendPacket (j, NCMD_EXIT);
                I.WaitVBL (1);
            }
    }



    //
    // TryRunTics
    //
    int[] frametics=new int[4];
    int frameon;
    boolean[] frameskip=new boolean[4];
    int oldnettics;
    int  oldentertics;


    @Override
    public void TryRunTics () throws IOException
    {
        int     i;
        int     lowtic;
        int     entertic;

        int     realtics;
        int     availabletics;
        int     counts;
        int     numplaying;


        // get real tics        
        entertic = TICK.GetTime ()/ticdup;
        realtics = entertic - oldentertics;
        oldentertics = entertic;

        //System.out.printf("Entertic %d, realtics %d, oldentertics %d\n",entertic,realtics,oldentertics);

        // get available tics
        NetUpdate ();

        lowtic = MAXINT;
        numplaying = 0;
        for (i=0 ; i<doomcom.numnodes ; i++)
        {
            if (nodeingame[i])
            {
                numplaying++;
                if (nettics[i] < lowtic)
                    lowtic = nettics[i];
            }
        }
        availabletics = lowtic - gametic/ticdup;

        // decide how many tics to run
        if (realtics < availabletics-1)
            counts = realtics+1;
        else if (realtics < availabletics)
            counts = realtics;
        else
            counts = availabletics;

        if (counts < 1)
            counts = 1;

        frameon++;

        if (eval(debugfile)){
            sb.setLength(0);
            sb.append( "=======real: ");
            sb.append(realtics);
            sb.append("  avail: ");
            sb.append(availabletics);
            sb.append("  game: ");
            sb.append(counts);
            sb.append("\n");
            debugfile.write(sb.toString());
        }

        if (!demoplayback)
        {   
            // ideally nettics[0] should be 1 - 3 tics above lowtic
            // if we are consistantly slower, speed up time
            for (i=0 ; i<MAXPLAYERS ; i++)
                if (playeringame[i])
                    break;
            if (consoleplayer == i)
            {
                // the key player does not adapt
            }
            else
            {
                if (nettics[0] <= nettics[nodeforplayer[i]])
                {
                    gametime--;
                    System.out.print("-");
                }
                frameskip[frameon&3] = oldnettics > nettics[nodeforplayer[i]];
                oldnettics = nettics[0];
                if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
                {
                    skiptics = 1;
                    System.out.print("+");
                }
            }
        }// demoplayback

        // wait for new tics if needed
        while (lowtic < gametic/ticdup + counts)    
        {
            NetUpdate ();   
            lowtic = MAXINT;

            // Finds the node with the lowest number of tics.
            for (i=0 ; i<doomcom.numnodes ; i++)
                if (nodeingame[i] && nettics[i] < lowtic)
                    lowtic = nettics[i];

            if (lowtic < gametic/ticdup)
                I.Error ("TryRunTics: lowtic < gametic");

            // don't stay in here forever -- give the menu a chance to work
            int time=TICK.GetTime();
            if (time/ticdup - entertic >= 20)
            {
                M.Ticker ();
                return;
            }
        }

        // run the count * ticdup dics
        while (counts-->0)
        {
            for (i=0 ; i<ticdup ; i++)
            {
                if (gametic/ticdup > lowtic)
                    I.Error ("gametic>lowtic");
                if (advancedemo)
                    DM.DoAdvanceDemo ();
                M.Ticker ();
                Ticker ();
                gametic++;

                // modify command for duplicated tics
                if (i != ticdup-1)
                {
                    ticcmd_t    cmd;
                    int         buf;
                    int         j;

                    buf = (gametic/ticdup)%BACKUPTICS; 
                    for (j=0 ; j<MAXPLAYERS ; j++)
                    {
                        cmd = netcmds[j][buf];
                        cmd.chatchar = 0;
                        if (flags(cmd.buttons , BT_SPECIAL))
                            cmd.buttons = 0;
                    }
                }
            }
            NetUpdate ();   // check for new console commands
        }
    }


    @Override
    public doomcom_t getDoomCom() {
        return this.doomcom;
    }


    @Override
    public void setDoomCom(doomcom_t doomcom) {
        this.doomcom=doomcom;
    }


    @Override
    public void setGameAction(gameaction_t action) {
        this.gameaction=action;
    }


    @Override
    public gameaction_t getGameAction() {       
        return this.gameaction;
    }

    ////////////////////////////VIDEO SCALE STUFF ////////////////////////////////

    protected int SCREENWIDTH;
    protected int SCREENHEIGHT;
    protected int SAFE_SCALE;
    protected IVideoScale vs;



    @Override
    public void setVideoScale(IVideoScale vs) {
        this.vs=vs;
    }

    @Override
    public void initScaling() {
        this.SCREENHEIGHT=vs.getScreenHeight();
        this.SCREENWIDTH=vs.getScreenWidth();
        this.SAFE_SCALE=vs.getSafeScaling();
    }


    public void setCommandLineArgs(ICommandLineManager cM) {
        this.CM=cM;

    }


    public boolean shouldPollLockingKeys() {
        if (keysCleared) {
            keysCleared = false;
            return true;
        }
        return false;
    }
    
    /**
     * Since this is a fully OO implementation, we need a way to create
     * the instances of the Refresh daemon, the Playloop, the Wadloader 
     * etc. which however are now completely independent of each other
     * (well, ALMOST), and are typically only passed context when 
     * instantiated.
     * 
     *  If you instantiate one too early, it will have null context.
     *  
     *  The trick is to construct objects in the correct order. Some of
     *  them have Init() methods which are NOT yet safe to call.
     * 
     */

    public void Init(){
       
        // The various objects that need to "sense" the global status
        // end up here. This allows one-call updates.
        status_holders=new ArrayList<DoomStatusAware>();
        
        // Doommain is both "main" and handles most of the game status.
        this.DM=this;
        this.DG = this;
        this.DGN=this; // DoomMain also handles its own Game Networking.
             
        // Set ticker. It is a shared status object, but not a holder itself.
        if (eval(CM.CheckParm("-millis"))){

            TICK=new MilliTicker();
        }
        else if (eval(CM.CheckParm("-fasttic"))){
            TICK=new FastTicker();
        } else {
            TICK=new NanoTicker();
        }
        
        // Network "driver"
        status_holders.add((DoomStatusAware) (this.DNI=new DummyNetworkDriver(this)));
        
        // Random number generator, but we can have others too.
        this.RND=new DoomRandom();
        
        // Sound can be left until later, in Start

        this.W=new WadLoader(this.I); // The wadloader is a "weak" status holder.
        status_holders.add(this.WIPE=selectWiper());   

        // Then the menu...
        status_holders.add(this.HU=new HU(this));
        status_holders.add(this.M=new Menu(this));
        status_holders.add(this.LL=new BoomLevelLoader(this));
        
        // This will set R.
        this.R= selectRenderer();
        status_holders.add(this.R);
        status_holders.add((this.P=new Actions(this)));
        status_holders.add(this.ST=new StatusBar(this));
        status_holders.add(this.AM=selectAutoMap()); // Call Init later.
        // Let the renderer pick its own. It makes linking easier.
        this.TM=R.getTextureManager();
        this.SM=R.getSpriteManager();        

        //_D_: well, for EndLevel and Finale to work, they need to be instanciated somewhere!
        // it seems to fit perfectly here
        status_holders.add(this.WI = new EndLevel(this));    
        status_holders.add(this.F = selectFinale());
        
        // TODO: find out if we have requests for a specific resolution,
        // and try honouring them as closely as possible.       

        // 23/5/2011: Experimental dynamic resolution subsystem
        vs=VisualSettings.parse(CM);
        
        // Initializing actually sets drawing positions, constants,
        // etc. for main. Children will be set later in Start().
        this.initScaling();
        
        this.V=selectVideoRenderer();

        status_holders.add((DoomStatusAware) this.I);
        status_holders.add((DoomStatusAware) this.V);
        
        // Disk access visualizer
        
        status_holders.add((DoomStatusAware) (this.DD=new DiskDrawer(this,DiskDrawer.STDISK)));
        
        updateStatusHolders(this);


    }
    


    public static final class HiColor extends DoomMain<byte[], short[]>{
    
        public HiColor(){
            super();
        }
        
        protected IAutoMap<byte[], short[]> selectAutoMap() {
            return new Map.HiColor(this);
        }
        
        protected final Finale<byte[]> selectFinale(){
            return new Finale<byte[]>(this);
        }
        
        protected final DoomVideoRenderer<byte[],short[]> selectVideoRenderer(){
            return new BufferedRenderer16(SCREENWIDTH,SCREENHEIGHT);
        }
        
        protected final DoomVideoInterface<short[]> selectVideoInterface(){
            return new AWTDoom.HiColor(this,V);
        }
        
        protected final Wiper<byte[],short[]> selectWiper(){
            return new Wiper.HiColor(this);
        }
        
        protected final Renderer<byte[],short[]> selectRenderer() {
            // Serial or parallel renderer (serial is default, but can be forced)
            if (eval(CM.CheckParm("-serialrenderer"))){
                return new UnifiedRenderer.HiColor(this);    
            } else 
                // Parallel. Either with default values (2,1) or user-specified.
                if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){        
                    int p = CM.CheckParm ("-parallelrenderer");
                    if (p<1) p=CM.CheckParm("-parallelrenderer2");
                    
                    if (p < CM.getArgc()-1)
                    {
                        // Next THREE args must be numbers.
                        int walls=1, floors=1,masked=2;
                        startmap = Integer.parseInt(CM.getArgv(p+1));
                        // Try parsing walls.
                        try {
                            walls=Integer.parseInt(CM.getArgv(p+1));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // Try parsing floors. If wall succeeded, but floors
                        // not, it will default to 1.
                        try {
                            floors=Integer.parseInt(CM.getArgv(p+2));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }
                        
                        try {
                            masked=Integer.parseInt(CM.getArgv(p+3));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // In the worst case, we will use the defaults.
                        if  (CM.CheckParmBool("-parallelrenderer"))
                            // TODO: temporarily disabled
                        	return new ParallelRenderer.HiColor(this,walls,floors,masked);  
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer(this,walls,floors,masked);
                       // else
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer2(this,walls,floors,masked);
                    }
                } else {
                    // Force serial
                    return new UnifiedRenderer.HiColor(this);   
                }
            
            return null;
        }

        /**
        *  M_Screenshot
        *  
        *  Currently saves PCX screenshots, and only in devparm.
        *  Very oldschool ;-)
        *  
        *  TODO: add non-devparm hotkey for screenshots, sequential screenshot
        *  messages, option to save as either PCX or PNG. Also, request
        *  current palette from VI (otherwise gamma settings and palette effects
        *  don't show up).
        *  
        */

       public void ScreenShot ()
       {
           int     i;
           short[]  linear;
           String format=new String("DOOM%d%d%d%d.png");
           String lbmname = null;
           
           // munge planar buffer to linear
           linear = (short[]) V.getScreen(DoomVideoRenderer.SCREEN_WS);
           VI.ReadScreen (linear);

           // find a file name to save it to
           
           int[] digit=new int[4];
           
           for (i=0 ; i<=9999 ; i++)
           {
           digit[0] = ((i/1000 )%10);
           digit[1] =  ((i/100)%10);
           digit[2] =  ((i/10)%10);
           digit[3] =  (i%10);
           lbmname=String.format(format, digit[0],digit[1],digit[2],digit[3]);
           if (!C2JUtils.testReadAccess(lbmname))
               break;  // file doesn't exist
           }
           if (i==10000)
           I.Error ("M_ScreenShot: Couldn't create a PNG");

           // save the pcx file
           MenuMisc.WritePNGfile (lbmname, linear,
                 SCREENWIDTH, SCREENHEIGHT);

           players[consoleplayer].message = "screen shot";
       }

    }
    
    public static final class Indexed extends DoomMain<byte[], byte[]>{
        
        public Indexed(){
            super();
        }
        
        protected IAutoMap<byte[], byte[]> selectAutoMap() {
            return new Map.Indexed(this);
        }
        
        protected final Finale<byte[]> selectFinale(){
            return new Finale<byte[]>(this);
        }
        
        protected final DoomVideoRenderer<byte[],byte[]> selectVideoRenderer(){
            return new BufferedRenderer(SCREENWIDTH,SCREENHEIGHT);
        }
        
        protected final DoomVideoInterface<byte[]> selectVideoInterface(){
            return new AWTDoom.Indexed(this,V);
        }
        
        protected final Wiper<byte[],byte[]> selectWiper(){
            return new Wiper.Indexed(this);
        }        
        protected final Renderer<byte[],byte[]> selectRenderer() {
            // Serial or parallel renderer (serial is default, but can be forced)
            if (eval(CM.CheckParm("-serialrenderer"))){
                return new UnifiedRenderer.Indexed(this);    
            } else 
                // Parallel. Either with default values (2,1) or user-specified.
                if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){        
                    int p = CM.CheckParm ("-parallelrenderer");
                    if (p<1) p=CM.CheckParm("-parallelrenderer2");
                    
                    if (p < CM.getArgc()-1)
                    {
                        // Next THREE args must be numbers.
                        int walls=1, floors=1,masked=2;
                        startmap = Integer.parseInt(CM.getArgv(p+1));
                        // Try parsing walls.
                        try {
                            walls=Integer.parseInt(CM.getArgv(p+1));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // Try parsing floors. If wall succeeded, but floors
                        // not, it will default to 1.
                        try {
                            floors=Integer.parseInt(CM.getArgv(p+2));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }
                        
                        try {
                            masked=Integer.parseInt(CM.getArgv(p+3));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // In the worst case, we will use the defaults.
                        if  (CM.CheckParmBool("-parallelrenderer"))                            
                            // TODO: temporarily disabled
                            //return new UnifiedRenderer.Indexed(this);   
                            return new ParallelRenderer.Indexed(this,walls,floors,masked);
                        //else
                          //  return new ParallelRenderer2(this,walls,floors,masked);
                    }
                } else {
                    // Force serial
                    return new UnifiedRenderer.Indexed(this);   
                }
            
            return null;
        }

        /**
        *  M_Screenshot
        *  
        *  Currently saves PCX screenshots, and only in devparm.
        *  Very oldschool ;-)
        *  
        *  TODO: add non-devparm hotkey for screenshots, sequential screenshot
        *  messages, option to save as either PCX or PNG. Also, request
        *  current palette from VI (otherwise gamma settings and palette effects
        *  don't show up).
        *  
        */

       public void ScreenShot ()
       {
           int     i;
           byte[]  linear;
           String format=new String("DOOM%d%d%d%d.png");
           String lbmname = null;
           
           // munge planar buffer to linear
           linear = (byte[]) V.getScreen(DoomVideoRenderer.SCREEN_WS);
           VI.ReadScreen (linear);

           // find a file name to save it to
           
           int[] digit=new int[4];
           
           for (i=0 ; i<=9999 ; i++)
           {
           digit[0] = ((i/1000 )%10);
           digit[1] =  ((i/100)%10);
           digit[2] =  ((i/10)%10);
           digit[3] =  (i%10);
           lbmname=String.format(format, digit[0],digit[1],digit[2],digit[3]);
           if (!C2JUtils.testReadAccess(lbmname))
               break;  // file doesn't exist
           }
           if (i==10000)
           I.Error ("M_ScreenShot: Couldn't create a PNG");

           // save the pcx file
           MenuMisc.WritePNGfile(lbmname, linear,
                 SCREENWIDTH, SCREENHEIGHT,V.getPalette());

           players[consoleplayer].message = "screen shot";
       }
        
    }
 
    public static final class TrueColor extends DoomMain<byte[], int[]>{
        
        public TrueColor(){
            super();
        }
        
        protected IAutoMap<byte[], int[]> selectAutoMap() {
            return new Map.TrueColor(this);
        }
        
        protected final Finale<byte[]> selectFinale(){
            return new Finale<byte[]>(this);
        }
        
        protected final DoomVideoRenderer<byte[],int[]> selectVideoRenderer(){
            return new BufferedRenderer32(SCREENWIDTH,SCREENHEIGHT);
        }
        
        protected final DoomVideoInterface<int[]> selectVideoInterface(){
            return new AWTDoom.TrueColor(this,V);
        }
        
        protected final Wiper<byte[],int[]> selectWiper(){
            return new Wiper.TrueColor(this);
        }
        
        protected final Renderer<byte[],int[]> selectRenderer() {
            // Serial or parallel renderer (serial is default, but can be forced)
            if (eval(CM.CheckParm("-serialrenderer"))){
                return new UnifiedRenderer.TrueColor(this);    
            } else 
                // Parallel. Either with default values (2,1) or user-specified.
                if (CM.CheckParmBool("-parallelrenderer")||CM.CheckParmBool("-parallelrenderer2")){        
                    int p = CM.CheckParm ("-parallelrenderer");
                    if (p<1) p=CM.CheckParm("-parallelrenderer2");
                    
                    if (p < CM.getArgc()-1)
                    {
                        // Next THREE args must be numbers.
                        int walls=1, floors=1,masked=2;
                        startmap = Integer.parseInt(CM.getArgv(p+1));
                        // Try parsing walls.
                        try {
                            walls=Integer.parseInt(CM.getArgv(p+1));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // Try parsing floors. If wall succeeded, but floors
                        // not, it will default to 1.
                        try {
                            floors=Integer.parseInt(CM.getArgv(p+2));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }
                        
                        try {
                            masked=Integer.parseInt(CM.getArgv(p+3));
                        } catch (Exception e){
                            // OK, move on anyway.
                        }

                        // In the worst case, we will use the defaults.
                        if  (CM.CheckParmBool("-parallelrenderer"))
                            // TODO: temporarily disabled
                        	return new ParallelRenderer.TrueColor(this,walls,floors,masked);   
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer(this,walls,floors,masked);
                       // else
                       //     return (Renderer<byte[], short[]>) new ParallelRenderer2(this,walls,floors,masked);
                    }
                } else {
                    // Force serial
                    return new UnifiedRenderer.TrueColor(this);   
                }
            
            return null;
        }

        /**
        *  M_Screenshot
        *  
        *  Currently saves PCX screenshots, and only in devparm.
        *  Very oldschool ;-)
        *  
        *  TODO: add non-devparm hotkey for screenshots, sequential screenshot
        *  messages, option to save as either PCX or PNG. Also, request
        *  current palette from VI (otherwise gamma settings and palette effects
        *  don't show up).
        *  
        */

       public void ScreenShot ()
       {
           int     i;
           int[]  linear;
           String format=new String("DOOM%d%d%d%d.png");
           String lbmname = null;
           
           // munge planar buffer to linear
           linear = (int[]) V.getScreen(DoomVideoRenderer.SCREEN_WS);
           VI.ReadScreen (linear);

           // find a file name to save it to
           
    
[...]

full source  download  show line numbers  debug dex  old transpilations   

Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1005016
Snippet name: Mocha Doom Single Source (include)
Eternal ID of this version: #1005016/1
Text MD5: dc89d3a9538a824122a98f8aabc8c0fe
Transpilation MD5: 73ca03f98e9862a606ae662c56a197f1
Author: stefan
Category: javax / games
Type: JavaX source code
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2016-09-28 22:00:15
Source code size: 3225853 bytes / 104211 lines
Pitched / IR pitched: No / No
Views / Downloads: 11566 / 21549
Referenced in: [show references]