FileSystem for games

Well… this is my contribution to this forum…
My experience in java is just a week! :-X
So… try to don’t hit me too much! xD


import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.zip.*;

public class FileSystem
{
    /*
     * Constants
     */
    public static final int     INT     = 0;
    public static final double  DOUBLE  = 1;
    public static final long    LONG    = 2;
    public static final float   FLOAT   = 3;
    public static final short   SHORT   = 4;
    public static final char    CHAR    = 5;
    public static final byte    BYTE    = 6;

    /*
     * Variables
     */
    private static File file = null;
    private static FileInputStream file_input = null;
    private static DataInputStream data_in = null;
    public static file_t filesystem = null;
    public static file_t work_node = null;
    private static int id_counter = 0;

    /*
     * Start
     */
    public static void Start ()
    {
        id_counter = 0;
        filesystem = null;
        filesystem = Node_Create (filesystem, "<null>", null);

        work_node = filesystem;
    }

    /*
     * -------------------------------------------------------------------------
     * Node implementation
     */
    /*
     * Node_Create
     */
    private static file_t Node_Create (file_t a, String name, ByteBuffer buffer)
    {
        a = new file_t ();
        a.filename = name;
        a.backbuffer = buffer;
        a.next = null;
        return a;
    }

    /*
     * Node_Add
     */
    private static file_t Node_Add (file_t a, String name, ByteBuffer buffer)
    {
        if (a.next == null)
        {
            a.next = Node_Create (a.next, name, buffer);
            work_node = a.next;
        }

        return a;
    }

    /*
     * Node_Find
     */
    private static file_t Node_Find (file_t start, String name)
    {
        file_t a = start;
        file_t tmp = null;

        while (a != null)
        {
            if (a.filename.indexOf (name, 0) != -1)
            {
                if (a.backbuffer != null)
                {
                    return a;
                }
            }

            tmp = a.next;
            a = tmp;
        }

        return null;
    }

    /*
     * Node_ReadInt
     */
    private static int Node_ReadInt (file_t fp)
    {
        if (fp.backbuffer == null)
        {
            return -1;
        }

        return fp.backbuffer.getInt ();
    }

    /*
     * Node_ReadDouble
     */
    private static double Node_ReadDouble (file_t fp)
    {
        if (fp.backbuffer == null)
        {
            return -1;
        }

        return fp.backbuffer.getDouble ();
    }

    /*
     * Node_ReadLong
     */
    private static long Node_ReadLong (file_t fp)
    {
        if (fp.backbuffer == null)
        {
            return -1;
        }

        return fp.backbuffer.getLong ();
    }

    /*
     * Node_ReadFloat
     */
    private static float Node_ReadFloat (file_t fp)
    {
        if (fp.backbuffer == null)
        {
            return -1;
        }

        return fp.backbuffer.getFloat ();
    }

    /*
     * Node_ReadShort
     */
    private static short Node_ReadShort (file_t fp)
    {
        if (fp.backbuffer == null)
        {
            return -1;
        }

        return fp.backbuffer.getShort ();
    }

    /*
     * Node_ReadChar
     */
    private static char Node_ReadChar (file_t fp)
    {
        if (fp.backbuffer == null)
        {
            return 0;
        }

        return fp.backbuffer.getChar ();
    }

    /*
     * Node_ReadByte
     */
    private static byte Node_ReadByte (file_t fp)
    {
        if (fp.backbuffer == null)
        {
            return 0;
        }

        return fp.backbuffer.get ();
    }

    /*
     * -------------------------------------------------------------------------
     * Public interface
     */

    /*
     * fadd
     */
    public static boolean fadd (String name)
    {
        File file = null;

        int fileLength = 0;
        FileChannel channel = null;
        FileInputStream input = null;
        ByteBuffer buffer = null;
        boolean ispak = false;

        if (name.indexOf (Global.FS_PAK_EXTENSION, 0) != -1)
        {
            ispak = true;
        }

        try
        {
            if (!ispak)
            {
                file = new File(name);
                if (file.canRead())
                {
                    input = new FileInputStream(file);
                    channel = input.getChannel();
                    fileLength = (int) channel.size();
                    buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
                                        fileLength);
                    input.close();

                    buffer.order (ByteOrder.LITTLE_ENDIAN);
                    work_node = Node_Add (work_node, name, buffer);
                    work_node = work_node.next;

                    System.out.println ("loaded >> " + name);
                    return true;
                }
            }
            else
            {
                ZipInputStream zip_in_stream;
                file = new File(name);

                if (file.canRead())
                {
                    FileInputStream in = new FileInputStream (file);
                    BufferedInputStream source = new BufferedInputStream (in);
                    zip_in_stream = new ZipInputStream (source);

                    while (true)
                    {
                        ZipEntry zip_entry = zip_in_stream.getNextEntry ();

                        if (zip_entry == null)
                        {
                            break;
                        }

                        int size = (int)zip_entry.getSize ();
                        byte[] input_buffer = new byte[size];
                        int len = 0;
                        String filename = zip_entry.getName ();

                        len = zip_in_stream.read (input_buffer, 0, size);
                        buffer = ByteBuffer.wrap (input_buffer);

                        buffer.order (ByteOrder.LITTLE_ENDIAN);
                        work_node = Node_Add (work_node, filename, buffer);
                        work_node = work_node.next;

                        System.out.println ("loaded >> " + zip_entry.getName ());
                    }

                    zip_in_stream.close ();
                    return true;
                }
            }
        }
        catch (Exception e)
        {
            System.out.println ("exception >> " + e);
        }


        return false;
    }


    /*
     * fopen
     */
    public static file_t fopen (String name)
    {
        // file_t ret = Node_Search (name, filesystem);
        file_t ret = Node_Find (filesystem, name);

        if (ret != null)
        {
            ret.backbuffer.position (0);
            return ret;
        }

        return null;
    }

    /*
     * fclose
     */
    public static void fclose (file_t fp)
    {
    }

    /*
     * -------------------------------------------------------------------------
     * fread
     */
    /*
     * int
     */
    public static int fread (file_t fp, int a)
    {
        return Node_ReadInt (fp);
    }

    /*
     * double
     */
    public static double fread (file_t fp, double a)
    {
        return Node_ReadDouble (fp);
    }

    /*
     * long
     */
    public static long fread (file_t fp, long a)
    {
        return Node_ReadLong (fp);
    }

    /*
     * float
     */
    public static float fread (file_t fp, float a)
    {
        return Node_ReadFloat (fp);
    }

    /*
     * short
     */
    public static short fread (file_t fp, short a)
    {
        return Node_ReadShort (fp);
    }

    /*
     * char
     */
    public static char fread (file_t fp, char a)
    {
        return Node_ReadChar (fp);
    }

    /*
     * byte
     */
    public static byte fread (file_t fp, byte a)
    {
        return Node_ReadByte (fp);
    }
}

Any comments or something like that will be apreciated. :slight_smile:

Trying to code C in Java rarely gives good results…

Uhm… whats the purpose of all that? I mean you can just mount any jars/zip files into the classpath (even during runtime) and simply access those files directly (getResourceAsStream)… or load files from zip files etc.

I don’t quite see the purpose either ??? How about a simple example on what you can do with it?

Hey! it’s just a code of a week…
The basic idea of this is:

FileSystem.Start ();
FileSystem.fadd (“base/valkyrie.mdl”);
FileSystem.fadd (“base/mytextfile.txt”);
FileSystem.fadd (“base/test.zip”);
FileSystem.fadd (“base/test.tga”);

Filesystem creates nodes with a byte buffer in memory with all the above files (in zip or jar files filesystem will add all the files in the zip or jar file).
As you can see… im a C programmer… xD
I really miss the functions fopen, fread and fclose.

Well… you wanna read 3 bytes of a file?


FileSystem.Start ();
FileSystem.fadd ("base/valkyrie.mdl");

file_t a = FileSystem.fopen ("base/valkyrie.mdl");
if (a == null)
{
       return;
}

byte[] a = new byte[3];
byte[0] = FileSystem.fread (a, FileSystem.BYTE);
byte[1] = FileSystem.fread (a, FileSystem.BYTE);
byte[2] = FileSystem.fread (a, FileSystem.BYTE);

FileSystem.fclose (a);

I put FileSystem here to see another alternatives so, if im wrong, please, tell me… im just wanna learn.:slight_smile:
(in fact, just point me at the right direction if this is wrong).

Generally you’d just use the classpath as your filesystem. Stick resources in your jars, or in directories in your classpath. Then to do what you’ve just done you could do:


try {
    InputStream in = ThisClass.class.getClassLoader().getResourceAsStream("base/valkyrie.mdl");

    byte[] data = new byte[3];
    in.read(data);
    in.close();
} catch (IOException e) {
    e.printStackTrace();
}

where the MDL could be in any of the jars or directories in your class path. This helps later when you start using other distribution mediums - like for instance webstart. Also means you can play around with your classpath, put test resources etc in the way of the real ones to try things out.

Though I think most people here would sensibly wrap the classload bits and pieces up in an lightweight utility.

Kev

Though I think most people here would sensibly wrap the classload bits and pieces up in an lightweight utility.

Ye… for me its just InputStream=getInputStream(“foo/bar.ext”); or DataInputStream=getDataInputStream(“foo/bar.ext”);

I also dont need an extra try/catch there. If an exception occurs the module gets stopped and I get some stacktrace in the (quake alike) console. It doesnt really get any simpler than that :wink:

Me also, I consider a runtime error if something can’t be loaded at game time. Hence thats a “stop and complain” rather than an possible exception case.

Kev

If you want to write C code thats fine, but don’t try and write C code in Java.

  • Use the classpath
  • Loose the statics
  • Don’t bring your awkward C naming conventions (file_t, Node_Create, a, tmp) with you.
  • Don’t convert exceptions into return codes.
  • Your use of dummy parameters to overloaded functions is hideous
  • You appear to have a whole bunch of unused variables
  • Read this, this and this.

Hey come on OT, go easy, at least this wasn’t spam :slight_smile:

Kev

Actually on re-reading that it sounds a little harsher than I intended. :-X

The first link is still very recommended though, it’s a pretty good read.

Well thanks…

Can you explain this points?

Generally it’s a bit overambitious to get into file I/O first thing. You should make some more useless programs and learn object oriented stuff before you can appreciate the design of the Java I/O classes.

About the naming conventions, in java you don’t use names like do_something, you use doSomething. I’m sure the relevant document was linked above.

Sure. For the first one see the link I posted for naming conventions. The main ones to get are that classes start with a capital, method names start with lower case and underscores aren’t used.

  1. fadd() catches an exception and converts it into a bool return code. It would be better to simply throw the exception, or catch it and re-throw it as a runtime exception.

  2. The syntax of FileSystem.fread(foo, BYTE) is just plain weird. You’re introducing a dummy variable that doesn’t get used just and forcing the caller to do odd things. Why not just try FileSystem.readByte(foo) / readInt(foo) / etc.

  3. file_input, data_in and id_counter all seem to be unused.

Hi Kev,
I was just reading your excellent webstart tutorial http://www.cokeandcode.com/webstarthowto where you recommend resources be accessed the following way:

Thread.currentThread().getContextClassLoader().getClassLoader().getResource(“sprites/mySprite.png”);

In the above posts you prescribed this:

ThisClass.class.getClassLoader().getResource(“base/valkyrie.mdl”);

Is the latter equivalent to the first and will both work with JWS’s special class loader you mentioned in the tutorial?

Thanks

Tremendous question. I’m afraid I was being sloppy. The ThisClass.class version is the way we worked out for ourselfs when webstart first started becoming useful for games. However, there was a post that I’ve been unable to find from one of the guys at Sun that recommended the Thread.getContextClassLoader() method.

I think the correct thing to use the getContextClassLoader() version since you may find the class that use to find the ClassLoader may have been loaded in some special way - where as the getContextClassLoader() will return you the class loader that would be used to load classes at the current point in process execution.

I’ve use both ways lots and lots and never had a problem with either. However this is one of the reasons I wrap my resource access up in little static class - if someone eventually decides that one way is better than the other - or a third version comes up thats now the “right thing ™” - then its easy to change :slight_smile:

For reference my ResourceLoader wrapper is available as part of my FECK code here:

http://www.cokeandcode.com/code/src/util/org/newdawn/util/ResourceLoader.java

This isn’t for review - just for reference :slight_smile: I know lots of people won’t like the System.exit(0) in there - but it works beautifully for my usage :slight_smile:

Kev

Thanks :slight_smile:

That code’s handy.

getContextClassLoader() is annoying to use because its so verbose but also I’m having to give it absolute names for the resource since I can’t tell where its relative position is like SomeClass.class.getClassLoader().

I’m going to use it in the following way to get around this:

Thread.currentThread().getContextClassLoader().getResource(“mypackage.resources.”+resourceName);

Some people also add a file system fallback to the whole thing for testing purposes. Personally I just sort the classpath out earlier but I thought it was worth noting.

Kev

I’ve got an improved one I keep meaning to post :). So …

Code: http://javagamesfactory.org/sourceformatter/sourcecode/8/ResourceLoader.java

Instructions: http://javagamesfactory.org/views/view-sourcesnippet?id=8

Crikey, I can just about remember that :slight_smile:

Kev