/* * Copyright (C) 2006-2011 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2006, 2007, 2008 Clam <clamisgood@gmail.com> * Copyright (C) 2007, 2008, 2009 Quadduc <quadduc@gmail.com> * * This file is part of LateralGM. * LateralGM is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.file; import java.awt.Dimension; import java.awt.Point; import java.io.File; import java.io.IOException; import java.util.Stack; import java.util.zip.DataFormatException; import org.lateralgm.components.impl.ResNode; import org.lateralgm.file.iconio.ICOFile; import org.lateralgm.main.LGM; import org.lateralgm.main.Util; import org.lateralgm.messages.Messages; import org.lateralgm.resources.Background; import org.lateralgm.resources.Font; import org.lateralgm.resources.GameInformation; import org.lateralgm.resources.GameSettings; import org.lateralgm.resources.GmObject; import org.lateralgm.resources.Include; import org.lateralgm.resources.Path; import org.lateralgm.resources.Resource; import org.lateralgm.resources.ResourceReference; import org.lateralgm.resources.Room; import org.lateralgm.resources.Script; import org.lateralgm.resources.Sound; import org.lateralgm.resources.Sprite; import org.lateralgm.resources.Timeline; import org.lateralgm.resources.Background.PBackground; import org.lateralgm.resources.Font.PFont; import org.lateralgm.resources.GameInformation.PGameInformation; import org.lateralgm.resources.GameSettings.IncludeFolder; import org.lateralgm.resources.GameSettings.PGameSettings; import org.lateralgm.resources.GameSettings.ProgressBar; import org.lateralgm.resources.GmObject.PGmObject; import org.lateralgm.resources.Path.PPath; import org.lateralgm.resources.Room.PRoom; import org.lateralgm.resources.Script.PScript; import org.lateralgm.resources.Sound.PSound; import org.lateralgm.resources.Sprite.BBMode; import org.lateralgm.resources.Sprite.PSprite; import org.lateralgm.resources.library.LibAction; import org.lateralgm.resources.library.LibArgument; import org.lateralgm.resources.library.LibManager; import org.lateralgm.resources.sub.Action; import org.lateralgm.resources.sub.ActionContainer; import org.lateralgm.resources.sub.Argument; import org.lateralgm.resources.sub.BackgroundDef; import org.lateralgm.resources.sub.Constant; import org.lateralgm.resources.sub.Event; import org.lateralgm.resources.sub.Instance; import org.lateralgm.resources.sub.MainEvent; import org.lateralgm.resources.sub.Moment; import org.lateralgm.resources.sub.PathPoint; import org.lateralgm.resources.sub.Tile; import org.lateralgm.resources.sub.Trigger; import org.lateralgm.resources.sub.View; import org.lateralgm.resources.sub.BackgroundDef.PBackgroundDef; import org.lateralgm.resources.sub.Instance.PInstance; import org.lateralgm.resources.sub.Tile.PTile; import org.lateralgm.resources.sub.View.PView; import org.lateralgm.util.PropertyMap; public final class GmFileReader { private GmFileReader() { } //Workaround for Parameter limit private static class GmFileContext { GmFile f; GmStreamDecoder in; RefList<Timeline> timeids; RefList<GmObject> objids; RefList<Room> rmids; public GmFileContext(GmFile f, GmStreamDecoder in, RefList<Timeline> timeids, RefList<GmObject> objids, RefList<Room> rmids) { this.f = f; this.in = in; this.timeids = timeids; this.objids = objids; this.rmids = rmids; } public GmFileContext copy() { return new GmFileContext(f,in,timeids,objids,rmids); } } private static GmFormatException versionError(GmFile f, String error, String res, int ver) { return versionError(f,error,res,0,ver); } private static GmFormatException versionError(GmFile f, String error, String res, int i, int ver) { return new GmFormatException(f,Messages.format( "GmFileReader.ERROR_UNSUPPORTED",Messages.format(//$NON-NLS-1$ "GmFileReader." + error,Messages.getString("LGM." + res),i),ver)); //$NON-NLS-1$ //$NON-NLS-2$ } public static GmFile readGmFile(File file, ResNode root) throws GmFormatException { GmFile f = new GmFile(); f.filename = file.getPath(); GmStreamDecoder in = null; RefList<Timeline> timeids = new RefList<Timeline>(Timeline.class); // timeline ids RefList<GmObject> objids = new RefList<GmObject>(GmObject.class); // object ids RefList<Room> rmids = new RefList<Room>(Room.class); // room id try { long startTime = System.currentTimeMillis(); in = new GmStreamDecoder(file); GmFileContext c = new GmFileContext(f,in,timeids,objids,rmids); int identifier = in.read4(); if (identifier != 1234321) throw new GmFormatException(f,Messages.format("GmFileReader.ERROR_INVALID",file.getPath(), //$NON-NLS-1$ identifier)); int ver = in.read4(); f.fileVersion = ver; if (ver != 530 && ver != 600 && ver != 701 && ver != 800 && ver != 810) { String msg = Messages.format("GmFileReader.ERROR_UNSUPPORTED","",ver); //$NON-NLS-1$ //$NON-NLS-2$ throw new GmFormatException(f,msg); } if (ver == 530) in.skip(4); //reserved 0 if (ver == 701) { int s1 = in.read4(); int s2 = in.read4(); in.skip(s1 * 4); //since only the first byte of the game id isn't encrypted, we have to do some acrobatics here int seed = in.read4(); in.skip(s2 * 4); int b1 = in.read(); in.setSeed(seed); f.gameSettings.put(PGameSettings.GAME_ID,b1 | in.read3() << 8); } else f.gameSettings.put(PGameSettings.GAME_ID,in.read4()); in.read((byte[]) f.gameSettings.get(PGameSettings.DPLAY_GUID)); //16 bytes readSettings(c); if (ver >= 800) { readTriggers(c); readConstants(c); } readSounds(c); readSprites(c); readBackgrounds(c); readPaths(c); readScripts(c); readFonts(c); readTimelines(c); readGmObjects(c); readRooms(c); f.lastInstanceId = in.read4(); f.lastTileId = in.read4(); if (ver >= 700) { readIncludedFiles(c); readPackages(c); } readGameInformation(c); //Library Creation Code ver = in.read4(); if (ver != 500) throw new GmFormatException(f,Messages.format("GmFileReader.ERROR_UNSUPPORTED", //$NON-NLS-1$ Messages.getString("GmFileReader.AFTERINFO"),ver)); //$NON-NLS-1$ int no = in.read4(); for (int j = 0; j < no; j++) in.skip(in.read4()); //Room Execution Order ver = in.read4(); if (ver != 500 && ver != 540 && ver != 700) throw new GmFormatException(f,Messages.format("GmFileReader.ERROR_UNSUPPORTED", //$NON-NLS-1$ Messages.getString("GmFileReader.AFTERINFO2"),ver)); //$NON-NLS-1$ in.skip(in.read4() * 4); readTree(c,root,ver); System.out.println(Messages.format("GmFileReader.LOADTIME",System.currentTimeMillis() //$NON-NLS-1$ - startTime)); } catch (Exception e) { if ((e instanceof GmFormatException)) throw (GmFormatException) e; throw new GmFormatException(f,e); } finally { try { if (in != null) { in.close(); in = null; } } catch (IOException ex) { String key = Messages.getString("GmFileReader.ERROR_CLOSEFAILED"); //$NON-NLS-1$ throw new GmFormatException(f,key); } } return f; } private static void readSettings(GmFileContext c) throws IOException,GmFormatException, DataFormatException { GmStreamDecoder in = c.in; GameSettings g = c.f.gameSettings; PropertyMap<PGameSettings> p = g.properties; int ver = in.read4(); if (ver != 530 && ver != 542 && ver != 600 && ver != 702 && ver != 800) { String msg = Messages.format("GmFileReader.ERROR_UNSUPPORTED","",ver); //$NON-NLS-1$ //$NON-NLS-2$ throw new GmFormatException(c.f,msg); } if (ver == 800) in.beginInflate(); in.readBool(p,PGameSettings.START_FULLSCREEN); if (ver >= 600) in.readBool(p,PGameSettings.INTERPOLATE); in.readBool(p,PGameSettings.DONT_DRAW_BORDER,PGameSettings.DISPLAY_CURSOR); in.read4(p,PGameSettings.SCALING); if (ver == 530) in.skip(8); //"fullscreen scale" & "only scale w/ hardware support" else { in.readBool(p,PGameSettings.ALLOW_WINDOW_RESIZE,PGameSettings.ALWAYS_ON_TOP); p.put(PGameSettings.COLOR_OUTSIDE_ROOM,Util.convertGmColor(in.read4())); } in.readBool(p,PGameSettings.SET_RESOLUTION); int colorDepth = 0, frequency; if (ver == 530) { in.skip(8); //Color Depth, Exclusive Graphics p.put(PGameSettings.RESOLUTION,GmFile.GS5_RESOLS[in.read4()]); byte b = (byte) in.read4(); frequency = (b == 4) ? 0 : (byte) (b + 1); in.skip(8); //vertical blank, caption in fullscreen } else { colorDepth = (byte) in.read4(); p.put(PGameSettings.RESOLUTION,GmFile.GS_RESOLS[in.read4()]); frequency = (byte) in.read4(); } p.put(PGameSettings.COLOR_DEPTH,GmFile.GS_DEPTHS[colorDepth]); p.put(PGameSettings.FREQUENCY,GmFile.GS_FREQS[frequency]); in.readBool(p,PGameSettings.DONT_SHOW_BUTTONS); if (ver > 530) in.readBool(p,PGameSettings.USE_SYNCHRONIZATION); if (ver >= 800) in.readBool(p,PGameSettings.DISABLE_SCREENSAVERS); in.readBool(p,PGameSettings.LET_F4_SWITCH_FULLSCREEN,PGameSettings.LET_F1_SHOW_GAME_INFO, PGameSettings.LET_ESC_END_GAME,PGameSettings.LET_F5_SAVE_F6_LOAD); if (ver == 530) in.skip(8); //unknown bytes, both 0 if (ver > 600) in.readBool(p,PGameSettings.LET_F9_SCREENSHOT,PGameSettings.TREAT_CLOSE_AS_ESCAPE); p.put(PGameSettings.GAME_PRIORITY,GmFile.GS_PRIORITIES[in.read4()]); in.readBool(p,PGameSettings.FREEZE_ON_LOSE_FOCUS); p.put(PGameSettings.LOAD_BAR_MODE,GmFile.GS_PROGBARS[in.read4()]); if (p.get(PGameSettings.LOAD_BAR_MODE) == ProgressBar.CUSTOM) { if (ver < 800) { if (in.read4() != -1) p.put(PGameSettings.BACK_LOAD_BAR,in.readZlibImage()); if (in.read4() != -1) p.put(PGameSettings.FRONT_LOAD_BAR,in.readZlibImage()); } //ver >= 800 else { if (in.readBool()) p.put(PGameSettings.BACK_LOAD_BAR,in.readZlibImage()); if (in.readBool()) p.put(PGameSettings.FRONT_LOAD_BAR,in.readZlibImage()); } } in.readBool(p,PGameSettings.SHOW_CUSTOM_LOAD_IMAGE); if (p.get(PGameSettings.SHOW_CUSTOM_LOAD_IMAGE)) { if (ver < 800) { if (in.read4() != -1) p.put(PGameSettings.LOADING_IMAGE,in.readZlibImage()); } else if (in.readBool()) p.put(PGameSettings.LOADING_IMAGE,in.readZlibImage()); } in.readBool(p,PGameSettings.IMAGE_PARTIALLY_TRANSPARENTY); in.read4(p,PGameSettings.LOAD_IMAGE_ALPHA); in.readBool(p,PGameSettings.SCALE_PROGRESS_BAR); int length = in.read4(); byte[] data = new byte[length]; in.read(data,0,length); try { g.put(PGameSettings.GAME_ICON,new ICOFile(data)); } catch (Exception e) { // hopefully this won't happen e.printStackTrace(); } in.readBool(p,PGameSettings.DISPLAY_ERRORS,PGameSettings.WRITE_TO_LOG, PGameSettings.ABORT_ON_ERROR); int errors = in.read4(); p.put(PGameSettings.TREAT_UNINIT_AS_0,((errors & 0x01) != 0)); p.put(PGameSettings.ERROR_ON_ARGS,((errors & 0x02) != 0)); in.readStr(p,PGameSettings.AUTHOR); if (ver > 600) in.readStr(p,PGameSettings.VERSION); else p.put(PGameSettings.VERSION,Integer.toString(in.read4())); in.readD(p,PGameSettings.LAST_CHANGED); in.readStr(p,PGameSettings.INFORMATION); if (ver < 800) { int no = in.read4(); for (int i = 0; i < no; i++) { Constant con = new Constant(); c.f.constants.add(con); con.name = in.readStr(); con.value = in.readStr(); } } if (ver > 600) { in.read4(p,PGameSettings.VERSION_MAJOR,PGameSettings.VERSION_MINOR, PGameSettings.VERSION_RELEASE,PGameSettings.VERSION_BUILD); in.readStr(p,PGameSettings.COMPANY,PGameSettings.PRODUCT,PGameSettings.COPYRIGHT, PGameSettings.DESCRIPTION); if (ver >= 800) in.skip(8); //last changed } else if (ver > 530) readSettingsIncludes(c.f,in); in.endInflate(); } private static void readSettingsIncludes(GmFile f, GmStreamDecoder in) throws IOException { int no = in.read4(); for (int i = 0; i < no; i++) { Include inc = new Include(); f.includes.add(inc); inc.filepath = in.readStr(); inc.filename = new File(inc.filepath).getName(); } f.gameSettings.put(PGameSettings.INCLUDE_FOLDER,GmFile.GS_INCFOLDERS[in.read4()]); // f.gameSettings.includeFolder = in.read4(); //0 = main, 1 = temp in.readBool(f.gameSettings.properties,PGameSettings.OVERWRITE_EXISTING, PGameSettings.REMOVE_AT_GAME_END); for (Include inc : f.includes) { inc.export = f.gameSettings.get(PGameSettings.INCLUDE_FOLDER) == IncludeFolder.TEMP ? 1 : 2; //1 = temp, 2 = main inc.overwriteExisting = f.gameSettings.get(PGameSettings.OVERWRITE_EXISTING); inc.removeAtGameEnd = f.gameSettings.get(PGameSettings.REMOVE_AT_GAME_END); } } private static void readTriggers(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 800) throw versionError(f,"BEFORE","SOUNDS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int no = in.read4(); for (int i = 0; i < no; i++) { in.beginInflate(); if (!in.readBool()) { in.endInflate(); continue; } ver = in.read4(); if (ver != 800) throw versionError(f,"BEFORE","SOUNDS",ver); //$NON-NLS-1$ //$NON-NLS-2$ Trigger trig = new Trigger(); f.triggers.add(trig); trig.name = in.readStr(); trig.condition = in.readStr(); trig.checkStep = in.read4(); trig.constant = in.readStr(); in.endInflate(); } in.skip(8); //last changed } private static void readConstants(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 800) throw versionError(f,"BEFORE","SOUNDS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int no = in.read4(); for (int i = 0; i < no; i++) { Constant con = new Constant(); f.constants.add(con); con.name = in.readStr(); con.value = in.readStr(); } in.skip(8); //last changed } private static void readSounds(GmFileContext c) throws IOException,GmFormatException, DataFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 400 && ver != 800) throw versionError(f,"BEFORE","SOUNDS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noSounds = in.read4(); for (int i = 0; i < noSounds; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { f.sounds.lastId++; in.endInflate(); continue; } Sound snd = f.sounds.add(); snd.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed ver = in.read4(); if (ver != 440 && ver != 600 && ver != 800) throw versionError(f,"IN","SOUNDS",i,ver); //$NON-NLS-1$ //$NON-NLS-2$ int kind53 = -1; if (ver == 440) kind53 = in.read4(); //kind (wav, mp3, etc) else snd.put(PSound.KIND,GmFile.SOUND_KIND[in.read4()]); //normal, background, etc in.readStr(snd.properties,PSound.FILE_TYPE); if (ver == 440) { //-1 = no sound if (kind53 != -1) snd.data = in.decompress(in.read4()); in.skip(8); snd.put(PSound.PRELOAD,!in.readBool()); } else { snd.put(PSound.FILE_NAME,in.readStr()); if (in.readBool()) { if (ver == 600) snd.data = in.decompress(in.read4()); else { int s = in.read4(); snd.data = new byte[s]; in.read(snd.data); } } int effects = in.read4(); for (PSound k : GmFile.SOUND_FX_FLAGS) { snd.put(k,(effects & 1) != 0); effects >>= 1; } in.readD(snd.properties,PSound.VOLUME,PSound.PAN); snd.put(PSound.PRELOAD,in.readBool()); } in.endInflate(); } } private static void readSprites(GmFileContext c) throws IOException,GmFormatException, DataFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 400 && ver != 800) throw versionError(f,"BEFORE","SPRITES",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noSprites = in.read4(); for (int i = 0; i < noSprites; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { f.sprites.lastId++; in.endInflate(); continue; } Sprite spr = f.sprites.add(); spr.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed ver = in.read4(); if (ver != 400 && ver != 542 && ver != 800) throw versionError(f,"IN","SPRITES",i,ver); //$NON-NLS-1$ //$NON-NLS-2$ int w = 0, h = 0; if (ver < 800) { w = in.read4(); h = in.read4(); //temporarily set bbmode to manual so bbox doesn't get recalculated until bbmode is ready spr.put(PSprite.BB_MODE,BBMode.MANUAL); in.read4(spr.properties,PSprite.BB_LEFT,PSprite.BB_RIGHT,PSprite.BB_BOTTOM,PSprite.BB_TOP); spr.put(PSprite.TRANSPARENT,in.readBool()); //XXX: tends to cause an update... if (ver > 400) { in.readBool(spr.properties,PSprite.SMOOTH_EDGES,PSprite.PRELOAD); } spr.put(PSprite.BB_MODE,GmFile.SPRITE_BB_MODE[in.read4()]); //now bbmode is ready boolean precise = in.readBool(); spr.put(PSprite.SHAPE,precise ? Sprite.MaskShape.PRECISE : Sprite.MaskShape.RECTANGLE); if (ver == 400) { in.skip(4); //use video memory spr.put(PSprite.PRELOAD,!in.readBool()); } } else spr.put(PSprite.TRANSPARENT,false); in.read4(spr.properties,PSprite.ORIGIN_X,PSprite.ORIGIN_Y); int nosub = in.read4(); for (int j = 0; j < nosub; j++) { if (ver >= 800) { ver = in.read4(); if (ver != 800) throw versionError(f,"IN","SPRITES",i,ver); //$NON-NLS-1$ //$NON-NLS-2$ w = in.read4(); h = in.read4(); if (w != 0 && h != 0) spr.subImages.add(in.readBGRAImage(w,h)); } else { if (in.read4() == -1) continue; spr.subImages.add(in.readZlibImage(w,h)); } } if (ver >= 800) { spr.put(PSprite.SHAPE,GmFile.SPRITE_MASK_SHAPE[in.read4()]); spr.put(PSprite.ALPHA_TOLERANCE,in.read4()); spr.put(PSprite.SEPARATE_MASK,in.readBool()); spr.put(PSprite.BB_MODE,GmFile.SPRITE_BB_MODE[in.read4()]); in.read4(spr.properties,PSprite.BB_LEFT,PSprite.BB_RIGHT,PSprite.BB_BOTTOM,PSprite.BB_TOP); } in.endInflate(); } } private static void readBackgrounds(GmFileContext c) throws IOException,GmFormatException, DataFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 400 && ver != 800) throw versionError(f,"BEFORE","BACKGROUNDS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noBackgrounds = in.read4(); for (int i = 0; i < noBackgrounds; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { f.backgrounds.lastId++; in.endInflate(); continue; } Background back = f.backgrounds.add(); back.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed ver = in.read4(); if (ver != 400 && ver != 543 && ver != 710) throw versionError(f,"IN","BACKGROUNDS",i,ver); //$NON-NLS-1$ //$NON-NLS-2$ if (ver < 710) { int w = in.read4(); int h = in.read4(); back.put(PBackground.TRANSPARENT,in.readBool()); if (ver > 400) { in.readBool(back.properties,PBackground.SMOOTH_EDGES,PBackground.PRELOAD, PBackground.USE_AS_TILESET); in.read4(back.properties,PBackground.TILE_WIDTH,PBackground.TILE_HEIGHT, PBackground.H_OFFSET,PBackground.V_OFFSET,PBackground.H_SEP,PBackground.V_SEP); } else { in.skip(4); //use video memory back.put(PBackground.PRELOAD,!in.readBool()); } if (in.readBool()) { if (in.read4() == -1) continue; back.setBackgroundImage(in.readZlibImage(w,h)); } } //ver >= 710 else { back.put(PBackground.USE_AS_TILESET,in.readBool()); in.read4(back.properties,PBackground.TILE_WIDTH,PBackground.TILE_HEIGHT, PBackground.H_OFFSET,PBackground.V_OFFSET,PBackground.H_SEP,PBackground.V_SEP); ver = in.read4(); if (ver != 800) throw versionError(f,"IN","BACKGROUNDS",i,ver); //$NON-NLS-1$ //$NON-NLS-2$ int w = in.read4(); int h = in.read4(); if (w != 0 && h != 0) back.setBackgroundImage(in.readBGRAImage(w,h)); } in.endInflate(); } } private static void readPaths(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 420 && ver != 800) throw versionError(f,"BEFORE","PATHS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noPaths = in.read4(); for (int i = 0; i < noPaths; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { f.paths.lastId++; in.endInflate(); continue; } Path path = f.paths.add(); path.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed int ver2 = in.read4(); if (ver2 != 530) throw versionError(f,"IN","PATHS",i,ver2); //$NON-NLS-1$ //$NON-NLS-2$ in.readBool(path.properties,PPath.SMOOTH,PPath.CLOSED); path.put(PPath.PRECISION,in.read4()); path.put(PPath.BACKGROUND_ROOM,c.rmids.get(in.read4())); in.read4(path.properties,PPath.SNAP_X,PPath.SNAP_Y); int nopoints = in.read4(); for (int j = 0; j < nopoints; j++) { path.points.add(new PathPoint((int) in.readD(),(int) in.readD(),(int) in.readD())); } in.endInflate(); } } private static void readScripts(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 400 && ver != 800) throw versionError(f,"BEFORE","SCRIPTS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noScripts = in.read4(); for (int i = 0; i < noScripts; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { f.scripts.lastId++; in.endInflate(); continue; } Script scr = f.scripts.add(); scr.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed ver = in.read4(); if (ver != 400 && ver != 800) throw versionError(f,"IN","SCRIPTS",i,ver); //$NON-NLS-1$ //$NON-NLS-2$ String code = in.readStr(); scr.put(PScript.CODE,code); in.endInflate(); } } private static void readFonts(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 440 && ver != 540 && ver != 800) throw versionError(f,"BEFORE","FONTS",(int) in.getPos()); //$NON-NLS-1$ //$NON-NLS-2$ if (ver == 440) //data files { int noDataFiles = in.read4(); for (int i = 0; i < noDataFiles; i++) { if (!in.readBool()) continue; in.skip(in.read4()); if (in.read4() != 440) throw new GmFormatException(f,Messages.format("GmFileReader.ERROR_UNSUPPORTED", //$NON-NLS-1$ Messages.getString("GmFileReader.INDATAFILES"),ver)); //$NON-NLS-1$ Include inc = new Include(); f.includes.add(inc); inc.filepath = in.readStr(); inc.filename = new File(inc.filepath).getName(); if (in.readBool()) //file data exists? { inc.size = in.read4(); inc.data = new byte[inc.size]; in.read(inc.data,0,inc.size); } inc.export = in.read4(); //FIXME: Deal with Font Includes //if (inc.export == 3) inc.exportFolder = Font Folder? inc.overwriteExisting = in.readBool(); inc.freeMemAfterExport = in.readBool(); inc.removeAtGameEnd = in.readBool(); } return; } int noFonts = in.read4(); for (int i = 0; i < noFonts; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { f.fonts.lastId++; in.endInflate(); continue; } Font font = f.fonts.add(); font.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed ver = in.read4(); if (ver != 540 && ver != 800) throw versionError(f,"IN","FONTS",i,ver); //$NON-NLS-1$ //$NON-NLS-2$ font.put(PFont.FONT_NAME,in.readStr()); font.put(PFont.SIZE,in.read4()); in.readBool(font.properties,PFont.BOLD,PFont.ITALIC); font.put(PFont.RANGE_MIN,in.read2()); font.put(PFont.CHARSET,in.read()); int aa = in.read(); if (aa == 0 && LGM.currentFile.fileVersion < 810) aa = 3; font.put(PFont.ANTIALIAS,aa); font.put(PFont.RANGE_MAX,in.read4()); in.endInflate(); } } private static void readTimelines(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 500 && ver != 800) throw versionError(f,"BEFORE","TIMELINES",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noTimelines = in.read4(); for (int i = 0; i < noTimelines; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { in.endInflate(); continue; } ResourceReference<Timeline> r = c.timeids.get(i); //includes ID Timeline time = r.get(); f.timelines.add(time); time.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed int ver2 = in.read4(); if (ver2 != 500) throw versionError(f,"IN","TIMELINES",i,ver2); //$NON-NLS-1$ //$NON-NLS-2$ int nomoms = in.read4(); for (int j = 0; j < nomoms; j++) { Moment mom = time.addMoment(); mom.stepNo = in.read4(); GmFileContext fc = c.copy(); fc.in = in; readActions(fc,mom,"INTIMELINEACTION",i,mom.stepNo); //$NON-NLS-1$ } in.endInflate(); } f.timelines.lastId = noTimelines - 1; } private static void readGmObjects(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 400 && ver != 800) throw versionError(f,"BEFORE","OBJECTS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noGmObjects = in.read4(); for (int i = 0; i < noGmObjects; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { in.endInflate(); continue; } ResourceReference<GmObject> r = c.objids.get(i); //includes ID GmObject obj = r.get(); f.gmObjects.add(obj); obj.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed int ver2 = in.read4(); if (ver2 != 430) throw versionError(f,"IN","OBJECTS",i,ver2); //$NON-NLS-1$ //$NON-NLS-2$ Sprite temp = f.sprites.getUnsafe(in.read4()); if (temp != null) obj.put(PGmObject.SPRITE,temp.reference); in.readBool(obj.properties,PGmObject.SOLID,PGmObject.VISIBLE); obj.put(PGmObject.DEPTH,in.read4()); obj.put(PGmObject.PERSISTENT,in.readBool()); obj.put(PGmObject.PARENT,c.objids.get(in.read4())); temp = f.sprites.getUnsafe(in.read4()); if (temp != null) obj.put(PGmObject.MASK,temp.reference); int noEvents = in.read4() + 1; for (int j = 0; j < noEvents; j++) { MainEvent me = obj.mainEvents.get(j); boolean done = false; while (!done) { int first = in.read4(); if (first != -1) { Event ev = new Event(); me.events.add(0,ev); if (j == MainEvent.EV_COLLISION) ev.other = c.objids.get(first); else ev.id = first; ev.mainId = j; GmFileContext fc = c.copy(); fc.in = in; readActions(fc,ev,"INOBJECTACTION",i,j * 1000 + ev.id); //$NON-NLS-1$ } else done = true; } } in.endInflate(); } f.gmObjects.lastId = noGmObjects - 1; } private static void readRooms(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 420 && ver != 800) throw versionError(f,"BEFORE","ROOMS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noRooms = in.read4(); for (int i = 0; i < noRooms; i++) { if (ver == 800) in.beginInflate(); if (!in.readBool()) { in.endInflate(); continue; } ResourceReference<Room> r = c.rmids.get(i); //includes ID Room rm = r.get(); f.rooms.add(rm); rm.setName(in.readStr()); if (ver == 800) in.skip(8); //last changed int ver2 = in.read4(); if (ver2 != 520 && ver2 != 541) throw versionError(f,"IN","ROOMS",i,ver2); //$NON-NLS-1$ //$NON-NLS-2$ rm.put(PRoom.CAPTION,in.readStr()); in.read4(rm.properties,PRoom.WIDTH,PRoom.HEIGHT,PRoom.SNAP_Y,PRoom.SNAP_X); rm.put(PRoom.ISOMETRIC,in.readBool()); rm.put(PRoom.SPEED,in.read4()); rm.put(PRoom.PERSISTENT,in.readBool()); rm.put(PRoom.BACKGROUND_COLOR,Util.convertGmColor(in.read4())); rm.put(PRoom.DRAW_BACKGROUND_COLOR,in.readBool()); rm.put(PRoom.CREATION_CODE,in.readStr()); int nobackgrounds = in.read4(); for (int j = 0; j < nobackgrounds; j++) { BackgroundDef bk = rm.backgroundDefs.get(j); in.readBool(bk.properties,PBackgroundDef.VISIBLE,PBackgroundDef.FOREGROUND); Background temp = f.backgrounds.getUnsafe(in.read4()); if (temp != null) bk.properties.put(PBackgroundDef.BACKGROUND,temp.reference); in.read4(bk.properties,PBackgroundDef.X,PBackgroundDef.Y); in.readBool(bk.properties,PBackgroundDef.TILE_HORIZ,PBackgroundDef.TILE_VERT); in.read4(bk.properties,PBackgroundDef.H_SPEED,PBackgroundDef.V_SPEED); bk.properties.put(PBackgroundDef.STRETCH,in.readBool()); } rm.put(PRoom.ENABLE_VIEWS,in.readBool()); int noviews = in.read4(); for (int j = 0; j < noviews; j++) { View vw = rm.views.get(j); in.readBool(vw.properties,PView.VISIBLE); //vw.properties.put(PView.VISIBLE,in.readBool()); in.read4(vw.properties,PView.VIEW_X,PView.VIEW_Y,PView.VIEW_W,PView.VIEW_H,PView.PORT_X, PView.PORT_Y); if (ver2 > 520) in.read4(vw.properties,PView.PORT_W,PView.PORT_H); in.read4(vw.properties,PView.BORDER_H,PView.BORDER_V,PView.SPEED_H,PView.SPEED_V); GmObject temp = f.gmObjects.getUnsafe(in.read4()); if (temp != null) vw.properties.put(PView.OBJECT,temp.reference); } int noinstances = in.read4(); for (int j = 0; j < noinstances; j++) { Instance inst = rm.addInstance(); inst.setPosition(new Point(in.read4(),in.read4())); GmObject temp = f.gmObjects.getUnsafe(in.read4()); if (temp != null) inst.properties.put(PInstance.OBJECT,temp.reference); inst.properties.put(PInstance.ID,in.read4()); inst.setCreationCode(in.readStr()); inst.setLocked(in.readBool()); } int notiles = in.read4(); for (int j = 0; j < notiles; j++) { Tile t = new Tile(rm); t.setRoomPosition(new Point(in.read4(),in.read4())); Background temp = f.backgrounds.getUnsafe(in.read4()); ResourceReference<Background> bkg = null; if (temp != null) bkg = temp.reference; t.properties.put(PTile.BACKGROUND,bkg); t.setBackgroundPosition(new Point(in.read4(),in.read4())); t.setSize(new Dimension(in.read4(),in.read4())); t.setDepth(in.read4()); t.properties.put(PTile.ID,in.read4()); t.setLocked(in.readBool()); rm.tiles.add(t); } rm.put(PRoom.REMEMBER_WINDOW_SIZE,in.readBool()); in.read4(rm.properties,PRoom.EDITOR_WIDTH,PRoom.EDITOR_HEIGHT); in.readBool(rm.properties,PRoom.SHOW_GRID,PRoom.SHOW_OBJECTS,PRoom.SHOW_TILES, PRoom.SHOW_BACKGROUNDS,PRoom.SHOW_FOREGROUNDS,PRoom.SHOW_VIEWS, PRoom.DELETE_UNDERLYING_OBJECTS,PRoom.DELETE_UNDERLYING_TILES); if (ver2 == 520) in.skip(6 * 4); //tile info in.read4(rm.properties,PRoom.CURRENT_TAB,PRoom.SCROLL_BAR_X,PRoom.SCROLL_BAR_Y); in.endInflate(); } f.rooms.lastId = noRooms - 1; } private static void readIncludedFiles(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 430 && ver != 600 && ver != 620 && ver != 800) throw versionError(f,"BEFORE","GAMEINFO",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noIncludes = in.read4(); for (int i = 0; i < noIncludes; i++) { if (ver == 800) { in.beginInflate(); in.skip(8); //last changed } ver = in.read4(); if (ver != 620 && ver != 800) throw new GmFormatException(f,Messages.format("GmFileReader.ERROR_UNSUPPORTED", //$NON-NLS-1$ Messages.getString("GmFileReader.ININCLUDEDFILES"),ver)); //$NON-NLS-1$ Include inc = new Include(); f.includes.add(inc); inc.filename = in.readStr(); inc.filepath = in.readStr(); inc.isOriginal = in.readBool(); inc.size = in.read4(); if (in.readBool()) //store in editable? { int s = in.read4(); inc.data = new byte[s]; in.read(inc.data,0,s); } inc.export = in.read4(); inc.exportFolder = in.readStr(); inc.overwriteExisting = in.readBool(); inc.freeMemAfterExport = in.readBool(); inc.removeAtGameEnd = in.readBool(); in.endInflate(); } } private static void readPackages(GmFileContext c) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; int ver = in.read4(); if (ver != 700) throw versionError(f,"BEFORE","EXTENSIONS",ver); //$NON-NLS-1$ //$NON-NLS-2$ int noPackages = in.read4(); for (int i = 0; i < noPackages; i++) f.packages.add(in.readStr()); //Package name } private static void readGameInformation(GmFileContext c) throws IOException,GmFormatException { GmStreamDecoder in = c.in; GameInformation gameInfo = c.f.gameInfo; PropertyMap<PGameInformation> p = gameInfo.properties; int ver = in.read4(); if (ver != 430 && ver != 600 && ver != 620 && ver != 800) throw versionError(c.f,"BEFORE","GAMEINFO",ver); //$NON-NLS-1$ //$NON-NLS-2$ if (ver == 800) in.beginInflate(); int bc = in.read4(); if (bc >= 0) p.put(PGameInformation.BACKGROUND_COLOR,Util.convertGmColor(bc)); if (ver < 800) in.readBool(p,PGameInformation.MIMIC_GAME_WINDOW); else p.put(PGameInformation.MIMIC_GAME_WINDOW,!in.readBool()); //Show help in a separate window if (ver > 430) { in.readStr(p,PGameInformation.FORM_CAPTION); in.read4(p,PGameInformation.LEFT,PGameInformation.TOP,PGameInformation.WIDTH, PGameInformation.HEIGHT); in.readBool(p,PGameInformation.SHOW_BORDER,PGameInformation.ALLOW_RESIZE, PGameInformation.STAY_ON_TOP,PGameInformation.PAUSE_GAME); } if (ver == 800) in.skip(8); //last changed in.readStr(p,PGameInformation.TEXT); in.endInflate(); } private static void readTree(GmFileContext c, ResNode root, int ver) throws IOException { GmFile f = c.f; GmStreamDecoder in = c.in; Stack<ResNode> path = new Stack<ResNode>(); Stack<Integer> left = new Stack<Integer>(); path.push(root); int rootnodes = (ver > 540) ? 12 : 11; while (rootnodes-- > 0) { byte status = (byte) in.read4(); Resource.Kind type = GmFile.RESOURCE_KIND[(byte) in.read4()]; int ind = in.read4(); String name = in.readStr(); boolean hasRef; if (status == ResNode.STATUS_SECONDARY) switch (type) { case GAMEINFO: case GAMESETTINGS: case EXTENSIONS: hasRef = false; break; case FONT: hasRef = ver != 500; break; default: hasRef = true; } else hasRef = false; ResourceList<?> rl = hasRef ? f.getList(type) : null; ResNode node = new ResNode(name,status,type,hasRef ? rl.getUnsafe(ind).reference : null); if (ver == 500 && status == ResNode.STATUS_PRIMARY && type == Resource.Kind.FONT) path.peek().addChild(Messages.getString("LGM.FONTS"),status,type); //$NON-NLS-1$ else path.peek().add(node); int contents = in.read4(); if (contents > 0) { left.push(new Integer(rootnodes)); rootnodes = contents; path.push(node); } while (rootnodes == 0 && !left.isEmpty()) { rootnodes = left.pop().intValue(); path.pop(); } } if (ver <= 540) root.addChild(Messages.getString("LGM.EXTENSIONS"), //$NON-NLS-1$ ResNode.STATUS_SECONDARY,Resource.Kind.EXTENSIONS); } private static void readActions(GmFileContext c, ActionContainer container, String errorKey, int format1, int format2) throws IOException,GmFormatException { GmFile f = c.f; GmStreamDecoder in = c.in; Resource<?,?> tag = new Script(); int ver = in.read4(); if (ver != 400) { throw new GmFormatException(f,Messages.format("GmFileReader.ERROR_UNSUPPORTED", //$NON-NLS-1$ Messages.format("GmFileReader." + errorKey,format1,format2),ver)); //$NON-NLS-1$ } int noacts = in.read4(); for (int k = 0; k < noacts; k++) { in.skip(4); int libid = in.read4(); int actid = in.read4(); LibAction la = LibManager.getLibAction(libid,actid); boolean unknownLib = la == null; //The libAction will have a null parent, among other things if (unknownLib) { la = new LibAction(); la.id = actid; la.parentId = libid; la.actionKind = (byte) in.read4(); la.allowRelative = in.readBool(); la.question = in.readBool(); la.canApplyTo = in.readBool(); la.execType = (byte) in.read4(); if (la.execType == Action.EXEC_FUNCTION) la.execInfo = in.readStr(); else in.skip(in.read4()); if (la.execType == Action.EXEC_CODE) la.execInfo = in.readStr(); else in.skip(in.read4()); } else { in.skip(20); in.skip(in.read4()); in.skip(in.read4()); } Argument[] args = new Argument[in.read4()]; byte[] argkinds = new byte[in.read4()]; for (int x = 0; x < argkinds.length; x++) argkinds[x] = (byte) in.read4(); if (unknownLib) { la.libArguments = new LibArgument[argkinds.length]; for (int x = 0; x < argkinds.length; x++) { la.libArguments[x] = new LibArgument(); la.libArguments[x].kind = argkinds[x]; } } Action act = container.addAction(la); int appliesTo = in.read4(); switch (appliesTo) { case -1: act.setAppliesTo(GmObject.OBJECT_SELF); break; case -2: act.setAppliesTo(GmObject.OBJECT_OTHER); break; default: act.setAppliesTo(c.objids.get(appliesTo)); } act.setRelative(in.readBool()); int actualnoargs = in.read4(); for (int l = 0; l < actualnoargs; l++) { if (l >= args.length) { in.skip(in.read4()); continue; } args[l] = new Argument(argkinds[l]); String strval = in.readStr(); Resource<?,?> res = tag; switch (argkinds[l]) { case Argument.ARG_SPRITE: res = f.sprites.getUnsafe(Integer.parseInt(strval)); break; case Argument.ARG_SOUND: res = f.sounds.getUnsafe(Integer.parseInt(strval)); break; case Argument.ARG_BACKGROUND: res = f.backgrounds.getUnsafe(Integer.parseInt(strval)); break; case Argument.ARG_PATH: res = f.paths.getUnsafe(Integer.parseInt(strval)); break; case Argument.ARG_SCRIPT: res = f.scripts.getUnsafe(Integer.parseInt(strval)); break; case Argument.ARG_GMOBJECT: args[l].setRes(c.objids.get(Integer.parseInt(strval))); break; case Argument.ARG_ROOM: args[l].setRes(c.rmids.get(Integer.parseInt(strval))); break; case Argument.ARG_FONT: res = f.fonts.getUnsafe(Integer.parseInt(strval)); break; case Argument.ARG_TIMELINE: args[l].setRes(c.timeids.get(Integer.parseInt(strval))); break; default: args[l].setVal(strval); break; } if (res != null && res != tag) { args[l].setRes(res.reference); } act.setArguments(args); } act.setNot(in.readBool()); } } }