/* * Copyright (C) 2006-2011 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2006, 2007, 2008 Clam <clamisgood@gmail.com> * Copyright (C) 2007, 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 static org.lateralgm.main.Util.deRef; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; import java.io.OutputStream; import java.util.Enumeration; import java.util.List; import javax.swing.JProgressBar; 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.Constants; import org.lateralgm.resources.Extension; 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.InstantiableResource; 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.Shader; 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.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.PSprite; import org.lateralgm.resources.library.LibAction; 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.CharacterRange.PCharacterRange; import org.lateralgm.resources.sub.CharacterRange; 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 GmFileWriter { private GmFileWriter() { } public static void writeProjectFile(OutputStream os, ProjectFile f, ResNode root, int ver) throws IOException { f.format = ProjectFile.FormatFlavor.getVersionFlavor(ver); long savetime = System.currentTimeMillis(); GmStreamEncoder out = new GmStreamEncoder(os); JProgressBar progressBar = LGM.getProgressDialogBar(); progressBar.setMaximum(200); LGM.setProgressTitle(Messages.getString("ProgressDialog.GMK_SAVING")); GameSettings gs = f.gameSettings.get(0); LGM.setProgress(0,Messages.getString("ProgressDialog.SETTINGS")); if (ver >= 810) out.setCharset(Charset.forName("UTF-8")); else out.setCharset(Charset.defaultCharset()); out.write4(1234321); out.write4(ver); if (ver == 530) out.write4(0); int gameId = gs.get(PGameSettings.GAME_ID); if (ver == 701) { out.write4(0); //bob out.write4(0); //fred out.write4(248); //seed out.write(gameId & 0xFF); out.setSeed(248); out.write3(gameId >>> 8); } else out.write4(gameId); out.write((byte[]) gs.get(PGameSettings.GAME_GUID)); //16 bytes writeSettings(f,out,ver,savetime,gs); if (ver >= 800) { LGM.setProgress(10,Messages.getString("ProgressDialog.TRIGGERS")); writeTriggers(f,out,ver,gs); LGM.setProgress(20,Messages.getString("ProgressDialog.CONSTANTS")); writeConstants(f,out,ver,gs); } LGM.setProgress(30,Messages.getString("ProgressDialog.SOUNDS")); writeSounds(f,out,ver,gs); LGM.setProgress(40,Messages.getString("ProgressDialog.SPRITES")); writeSprites(f,out,ver,gs); LGM.setProgress(50,Messages.getString("ProgressDialog.BACKGROUNDS")); writeBackgrounds(f,out,ver,gs); LGM.setProgress(60,Messages.getString("ProgressDialog.PATHS")); writePaths(f,out,ver,gs); LGM.setProgress(70,Messages.getString("ProgressDialog.SCRIPTS")); writeScripts(f,out,ver,gs); LGM.setProgress(80,Messages.getString("ProgressDialog.FONTS")); writeFonts(f,out,ver,gs); LGM.setProgress(90,Messages.getString("ProgressDialog.TIMELINES")); writeTimelines(f,out,ver,gs); LGM.setProgress(100,Messages.getString("ProgressDialog.OBJECTS")); writeGmObjects(f,out,ver,gs); LGM.setProgress(110,Messages.getString("ProgressDialog.ROOMS")); writeRooms(f,out,ver,gs); out.write4(f.lastInstanceId); out.write4(f.lastTileId); if (ver >= 700) { LGM.setProgress(120,Messages.getString("ProgressDialog.INCLUDEFILES")); writeIncludedFiles(f,out,ver,gs); LGM.setProgress(130,Messages.getString("ProgressDialog.PACKAGES")); writePackages(f,out,ver); } LGM.setProgress(140,Messages.getString("ProgressDialog.GAMEINFORMATION")); writeGameInformation(f,out,ver,gs); LGM.setProgress(150,Messages.getString("ProgressDialog.LIBRARYCREATION")); //Library Creation Code out.write4(500); out.write4(0); LGM.setProgress(160,Messages.getString("ProgressDialog.ROOMEXECUTION")); //Room Execution Order out.write4(ver >= 700 ? 700 : 540); out.write4(0); LGM.setProgress(170,Messages.getString("ProgressDialog.FILETREE")); writeTree(out,root); out.close(); LGM.setProgress(200,Messages.getString("ProgressDialog.FINISHED")); } public static void writeSettings(ProjectFile f, GmStreamEncoder out, int ver, long savetime, GameSettings g) throws IOException { ver = ver >= 810 ? 810 : ver >= 800 ? 800 : ver >= 701 ? 702 : ver; out.write4(ver >= 800 ? 800 : ver); if (ver >= 800) out.beginDeflate(); PropertyMap<PGameSettings> p = g.properties; out.writeBool(p,PGameSettings.START_FULLSCREEN); if (ver >= 600) out.writeBool(p,PGameSettings.INTERPOLATE); out.writeBool(p,PGameSettings.DONT_DRAW_BORDER,PGameSettings.DISPLAY_CURSOR); out.write4(p,PGameSettings.SCALING); out.writeBool(p,PGameSettings.ALLOW_WINDOW_RESIZE,PGameSettings.ALWAYS_ON_TOP); out.write4(Util.getGmColor((Color) p.get(PGameSettings.COLOR_OUTSIDE_ROOM))); out.writeBool(p,PGameSettings.SET_RESOLUTION); out.write4(ProjectFile.GS_DEPTH_CODE.get(p.get(PGameSettings.COLOR_DEPTH))); out.write4(ProjectFile.GS_RESOL_CODE.get(p.get(PGameSettings.RESOLUTION))); out.write4(ProjectFile.GS_FREQ_CODE.get(p.get(PGameSettings.FREQUENCY))); out.writeBool(p,PGameSettings.DONT_SHOW_BUTTONS,PGameSettings.USE_SYNCHRONIZATION); if (ver >= 800) out.writeBool(p,PGameSettings.DISABLE_SCREENSAVERS); out.writeBool(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 >= 702) out.writeBool(p,PGameSettings.LET_F9_SCREENSHOT,PGameSettings.TREAT_CLOSE_AS_ESCAPE); out.write4(ProjectFile.GS_PRIORITY_CODE.get(p.get(PGameSettings.GAME_PRIORITY))); out.writeBool(p,PGameSettings.FREEZE_ON_LOSE_FOCUS); out.write4(ProjectFile.GS_PROGBAR_CODE.get(p.get(PGameSettings.LOAD_BAR_MODE))); if (p.get(PGameSettings.LOAD_BAR_MODE) == ProgressBar.CUSTOM) { if (p.get(PGameSettings.BACK_LOAD_BAR) != null) { out.write4(ver < 800 ? 10 : 1); out.writeZlibImage((BufferedImage) p.get(PGameSettings.BACK_LOAD_BAR)); } else out.write4(ver < 800 ? -1 : 0); if (p.get(PGameSettings.FRONT_LOAD_BAR) != null) { out.write4(ver < 800 ? 10 : 1); out.writeZlibImage((BufferedImage) p.get(PGameSettings.FRONT_LOAD_BAR)); } else out.write4(ver < 800 ? -1 : 0); } out.writeBool(p,PGameSettings.SHOW_CUSTOM_LOAD_IMAGE); if (p.get(PGameSettings.SHOW_CUSTOM_LOAD_IMAGE)) { if (p.get(PGameSettings.LOADING_IMAGE) != null) { out.write4(ver < 800 ? 10 : 1); out.writeZlibImage((BufferedImage) p.get(PGameSettings.LOADING_IMAGE)); } else out.write4(ver < 800 ? -1 : 0); } out.writeBool(p,PGameSettings.IMAGE_PARTIALLY_TRANSPARENTY); out.write4(p,PGameSettings.LOAD_IMAGE_ALPHA); out.writeBool(p,PGameSettings.SCALE_PROGRESS_BAR); //FIXME: GM8 icons Util.fixIcon((ICOFile) g.get(PGameSettings.GAME_ICON),ver); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ((ICOFile) g.get(PGameSettings.GAME_ICON)).write(baos); out.write4(baos.size()); baos.writeTo(out); out.writeBool(p,PGameSettings.DISPLAY_ERRORS,PGameSettings.WRITE_TO_LOG, PGameSettings.ABORT_ON_ERROR); out.write4((p.get(PGameSettings.TREAT_UNINIT_AS_0) ? 1 : 0) | ((Boolean) p.get(PGameSettings.ERROR_ON_ARGS) && ver >= 810 ? 2 : 0)); out.writeStr(p,PGameSettings.AUTHOR); if (ver <= 600) { try { out.write4(Integer.parseInt((String) p.get(PGameSettings.VERSION))); } catch (NumberFormatException e) { out.write4(100); } } else out.writeStr(p,PGameSettings.VERSION); p.put(PGameSettings.LAST_CHANGED,ProjectFile.longTimeToGmTime(savetime)); out.writeD(p,PGameSettings.LAST_CHANGED); out.writeStr(p,PGameSettings.INFORMATION); if (ver < 800) { out.write4(g.constants.constants.size()); for (Constant con : g.constants.constants) { out.writeStr(con.name); out.writeStr(con.value); } if (ver == 542 || ver == 600) { ResourceList<Include> includes = f.resMap.getList(Include.class); out.write4(includes.size()); for (Include inc : includes) out.writeStr(inc.filepath); out.write4(ProjectFile.GS_INCFOLDER_CODE.get(p.get(PGameSettings.INCLUDE_FOLDER))); out.writeBool(p,PGameSettings.OVERWRITE_EXISTING,PGameSettings.REMOVE_AT_GAME_END); } } if (ver >= 702) { out.write4(p,PGameSettings.VERSION_MAJOR,PGameSettings.VERSION_MINOR, PGameSettings.VERSION_RELEASE,PGameSettings.VERSION_BUILD); out.writeStr(p,PGameSettings.COMPANY,PGameSettings.PRODUCT,PGameSettings.COPYRIGHT, PGameSettings.DESCRIPTION); if (ver >= 800) out.writeD(g.getLastChanged()); } out.endDeflate(); } public static void writeTriggers(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { if (ver < 800) return; out.write4(800); int no = f.triggers.isEmpty() ? 0 : (f.triggers.lastKey() + 1); out.write4(no); for (Integer i = 0; i < no; i++) { out.beginDeflate(); Trigger t = f.triggers.get(i); if (t == null) { out.writeBool(false); // Trigger does not exist } else { out.writeBool(true); // Trigger exists out.write4(800); out.writeStr(t.name); out.writeStr(t.condition); out.write4(t.checkStep); out.writeStr(t.constant); } out.endDeflate(); } out.writeD(gs.getLastChanged()); } public static void writeConstants(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { if (ver < 800) return; out.write4(800); out.write4(gs.constants.constants.size()); for (Constant c : gs.constants.constants) { out.writeStr(c.name); out.writeStr(c.value); } out.writeD(gs.getLastChanged()); } public static void writeSounds(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { ver = ver >= 800 ? 800 : ver >= 600 ? 600 : 440; out.write4(ver == 800 ? 800 : 400); out.write4(f.resMap.getList(Sound.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Sound.class).lastId; i++) { if (ver == 800) out.beginDeflate(); Sound snd = f.resMap.getList(Sound.class).getUnsafe(i); out.writeBool(snd != null); if (snd != null) { out.writeStr(snd.getName()); if (ver == 800) out.writeD(gs.getLastChanged()); out.write4(ver); out.write4(ProjectFile.SOUND_KIND_CODE.get(snd.get(PSound.KIND))); out.writeStr(snd.properties,PSound.FILE_TYPE,PSound.FILE_NAME); if (snd.data != null) { out.writeBool(true); if (ver == 800) { out.write4(snd.data.length); out.write(snd.data); } else out.compress(snd.data); } else out.writeBool(false); int effects = 0; int n = 1; for (PSound k : ProjectFile.SOUND_FX_FLAGS) { if (snd.get(k)) effects |= n; n <<= 1; } out.write4(effects); out.writeD(snd.properties,PSound.VOLUME,PSound.PAN); out.writeBool(snd.properties,PSound.PRELOAD); } out.endDeflate(); } } public static void writeSprites(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { ver = ver >= 800 ? 800 : ver >= 542 ? 542 : 400; out.write4(ver == 800 ? 800 : 400); out.write4(f.resMap.getList(Sprite.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Sprite.class).lastId; i++) { if (ver == 800) out.beginDeflate(); Sprite spr = f.resMap.getList(Sprite.class).getUnsafe(i); out.writeBool(spr != null); if (spr != null) { out.writeStr(spr.getName()); if (ver == 800) out.writeD(gs.getLastChanged()); out.write4(ver); if (ver < 800) { out.write4(spr.subImages.getWidth()); out.write4(spr.subImages.getHeight()); out.write4(spr.properties,PSprite.BB_LEFT,PSprite.BB_RIGHT,PSprite.BB_BOTTOM, PSprite.BB_TOP); out.writeBool(spr.properties,PSprite.TRANSPARENT,PSprite.SMOOTH_EDGES,PSprite.PRELOAD); out.write4(ProjectFile.SPRITE_BB_CODE.get(spr.get(PSprite.BB_MODE))); out.writeBool(spr.get(PSprite.SHAPE) == Sprite.MaskShape.PRECISE); } out.write4(spr.properties,PSprite.ORIGIN_X,PSprite.ORIGIN_Y); out.write4(spr.subImages.size()); for (int j = 0; j < spr.subImages.size(); j++) { BufferedImage sub = spr.subImages.get(j); if (ver == 800) { out.write4(800); int w = sub.getWidth(); int h = sub.getHeight(); out.write4(w); out.write4(h); if (w != 0 && h != 0) out.writeBGRAImage(sub,(Boolean) spr.get(PSprite.TRANSPARENT)); } else { out.write4(10); out.writeZlibImage(sub); } } if (ver >= 800) { out.write4(ProjectFile.SPRITE_MASK_CODE.get(spr.get(PSprite.SHAPE))); out.write4(spr.properties,PSprite.ALPHA_TOLERANCE); out.writeBool(spr.properties,PSprite.SEPARATE_MASK); out.write4(ProjectFile.SPRITE_BB_CODE.get(spr.get(PSprite.BB_MODE))); out.write4(spr.properties,PSprite.BB_LEFT,PSprite.BB_RIGHT,PSprite.BB_BOTTOM, PSprite.BB_TOP); } } out.endDeflate(); } } public static void writeBackgrounds(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { ver = ver >= 710 ? 710 : ver >= 543 ? 543 : 400; out.write4(ver == 710 ? 800 : 400); out.write4(f.resMap.getList(Background.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Background.class).lastId; i++) { if (ver == 710) out.beginDeflate(); Background back = f.resMap.getList(Background.class).getUnsafe(i); out.writeBool(back != null); if (back != null) { out.writeStr(back.getName()); if (ver == 710) out.writeD(gs.getLastChanged()); out.write4(ver); if (ver < 710) { out.write4(back.getWidth()); out.write4(back.getHeight()); out.writeBool(back.properties,PBackground.TRANSPARENT,PBackground.SMOOTH_EDGES, PBackground.PRELOAD,PBackground.USE_AS_TILESET); } else out.writeBool(back.properties,PBackground.USE_AS_TILESET); out.write4(back.properties,PBackground.TILE_WIDTH,PBackground.TILE_HEIGHT, PBackground.H_OFFSET,PBackground.V_OFFSET,PBackground.H_SEP,PBackground.V_SEP); BufferedImage bi = back.getBackgroundImage(); if (ver < 710) { if (bi != null) { out.writeBool(true); out.write4(10); out.writeZlibImage(bi); } else out.writeBool(false); } else { out.write4(800); int w = bi == null ? 0 : bi.getWidth(); int h = bi == null ? 0 : bi.getHeight(); out.write4(w); out.write4(h); if (w != 0 && h != 0) out.writeBGRAImage(bi,(Boolean) back.get(PBackground.TRANSPARENT)); } } out.endDeflate(); } } public static void writePaths(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { if (ver > 800) ver = 800; out.write4(ver == 800 ? 800 : 420); out.write4(f.resMap.getList(Path.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Path.class).lastId; i++) { if (ver == 800) out.beginDeflate(); Path path = f.resMap.getList(Path.class).getUnsafe(i); out.writeBool(path != null); if (path != null) { out.writeStr(path.getName()); if (ver == 800) out.writeD(gs.getLastChanged()); out.write4(530); out.writeBool(path.properties,PPath.SMOOTH,PPath.CLOSED); out.write4(path.properties,PPath.PRECISION); out.writeId((ResourceReference<?>) path.get(PPath.BACKGROUND_ROOM)); out.write4(path.properties,PPath.SNAP_X,PPath.SNAP_Y); out.write4(path.points.size()); for (PathPoint p : path.points) { out.writeD(p.getX()); out.writeD(p.getY()); out.writeD(p.getSpeed()); } } out.endDeflate(); } } public static void writeScripts(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { ver = ver >= 800 ? 800 : 400; out.write4(ver); out.write4(f.resMap.getList(Script.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Script.class).lastId; i++) { if (ver == 800) out.beginDeflate(); Script scr = f.resMap.getList(Script.class).getUnsafe(i); out.writeBool(scr != null); if (scr != null) { out.writeStr(scr.getName()); if (ver == 800) out.writeD(gs.getLastChanged()); out.write4(ver); out.writeStr(scr.properties,PScript.CODE); } out.endDeflate(); } } public static void writeFonts(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { out.write4(ver >= 800 ? 800 : 540); out.write4(f.resMap.getList(Font.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Font.class).lastId; i++) { if (ver >= 800) out.beginDeflate(); Font font = f.resMap.getList(Font.class).getUnsafe(i); out.writeBool(font != null); if (font != null) { out.writeStr(font.getName()); if (ver >= 800) out.writeD(gs.getLastChanged()); out.write4(ver >= 800 ? 800 : 540); out.writeStr(font.properties,PFont.FONT_NAME); out.write4(font.properties,PFont.SIZE); out.writeBool(font.properties,PFont.BOLD,PFont.ITALIC); int rangemin = 0, rangemax = 0; if (font.characterRanges.size() > 0) { CharacterRange cr = font.characterRanges.get(0); if (cr != null) { rangemin = cr.properties.get(PCharacterRange.RANGE_MIN); rangemax = cr.properties.get(PCharacterRange.RANGE_MAX); } } if (ver >= 810) { out.write2(rangemin); out.write((Integer) font.get(PFont.CHARSET)); out.write((Integer) font.get(PFont.ANTIALIAS)); } else out.write4(rangemin); out.write4(rangemax); } out.endDeflate(); } } public static void writeTimelines(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { if (ver > 800) ver = 800; out.write4(ver == 800 ? 800 : 500); out.write4(f.resMap.getList(Timeline.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Timeline.class).lastId; i++) { if (ver == 800) out.beginDeflate(); Timeline time = f.resMap.getList(Timeline.class).getUnsafe(i); out.writeBool(time != null); if (time != null) { out.writeStr(time.getName()); if (ver == 800) out.writeD(gs.getLastChanged()); out.write4(500); out.write4(time.moments.size()); for (Moment mom : time.moments) { out.write4(mom.stepNo); writeActions(out,mom); } } out.endDeflate(); } } public static void writeGmObjects(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { if (ver > 800) ver = 800; out.write4(ver == 800 ? 800 : 400); out.write4(f.resMap.getList(GmObject.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(GmObject.class).lastId; i++) { if (ver == 800) out.beginDeflate(); GmObject obj = f.resMap.getList(GmObject.class).getUnsafe(i); out.writeBool(obj != null); if (obj != null) { out.writeStr(obj.getName()); if (ver == 800) out.writeD(gs.getLastChanged()); out.write4(430); out.writeId((ResourceReference<?>) obj.get(PGmObject.SPRITE)); out.writeBool(obj.properties,PGmObject.SOLID,PGmObject.VISIBLE); out.write4(obj.properties,PGmObject.DEPTH); out.writeBool(obj.properties,PGmObject.PERSISTENT); out.writeId((ResourceReference<?>) obj.get(PGmObject.PARENT),-100); out.writeId((ResourceReference<?>) obj.get(PGmObject.MASK)); int numMainEvents = ver == 800 ? 12 : 11; out.write4(numMainEvents - 1); for (int j = 0; j < numMainEvents; j++) { MainEvent me = obj.mainEvents.get(j); for (int k = me.events.size(); k > 0; k--) { Event ev = me.events.get(k - 1); if (j == MainEvent.EV_COLLISION) out.writeId(ev.other); else out.write4(ev.id); writeActions(out,ev); } out.write4(-1); } } out.endDeflate(); } } public static void writeRooms(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { if (ver > 800) ver = 800; out.write4(ver == 800 ? 800 : 420); out.write4(f.resMap.getList(Room.class).lastId + 1); for (int i = 0; i <= f.resMap.getList(Room.class).lastId; i++) { if (ver == 800) out.beginDeflate(); Room rm = f.resMap.getList(Room.class).getUnsafe(i); out.writeBool(rm != null); if (rm != null) { out.writeStr(rm.getName()); if (ver == 800) out.writeD(gs.getLastChanged()); out.write4(541); out.writeStr(rm.properties,PRoom.CAPTION); out.write4(rm.properties,PRoom.WIDTH,PRoom.HEIGHT,PRoom.SNAP_Y,PRoom.SNAP_X); out.writeBool(rm.properties,PRoom.ISOMETRIC); out.write4(rm.properties,PRoom.SPEED); out.writeBool(rm.properties,PRoom.PERSISTENT); out.write4(Util.getGmColor((Color) rm.get(PRoom.BACKGROUND_COLOR))); out.writeBool(rm.properties,PRoom.DRAW_BACKGROUND_COLOR); out.writeStr(rm.properties,PRoom.CREATION_CODE); out.write4(rm.backgroundDefs.size()); for (BackgroundDef back : rm.backgroundDefs) { out.writeBool(back.properties,PBackgroundDef.VISIBLE,PBackgroundDef.FOREGROUND); out.writeId((ResourceReference<?>) back.properties.get(PBackgroundDef.BACKGROUND)); out.write4(back.properties,PBackgroundDef.X,PBackgroundDef.Y); out.writeBool(back.properties,PBackgroundDef.TILE_HORIZ,PBackgroundDef.TILE_VERT); out.write4(back.properties,PBackgroundDef.H_SPEED,PBackgroundDef.V_SPEED); out.writeBool(back.properties,PBackgroundDef.STRETCH); } out.writeBool(rm.properties,PRoom.VIEWS_ENABLED); out.write4(rm.views.size()); for (View view : rm.views) { out.writeBool(view.properties,PView.VISIBLE); out.write4(view.properties,PView.VIEW_X,PView.VIEW_Y,PView.VIEW_W,PView.VIEW_H, PView.PORT_X,PView.PORT_Y,PView.PORT_W,PView.PORT_H,PView.BORDER_H,PView.BORDER_V, PView.SPEED_H,PView.SPEED_V); out.writeId((ResourceReference<?>) view.properties.get(PView.OBJECT)); } out.write4(rm.instances.size()); for (Instance in : rm.instances) { out.write4(in.getPosition().x); out.write4(in.getPosition().y); ResourceReference<GmObject> or = in.properties.get(PInstance.OBJECT); out.writeId(or); out.write4((Integer) in.properties.get(PInstance.ID)); out.writeStr(in.getCreationCode()); out.writeBool(in.isLocked()); } out.write4(rm.tiles.size()); for (Tile tile : rm.tiles) { out.write4(tile.getPosition().x); out.write4(tile.getPosition().y); ResourceReference<Background> rb = tile.properties.get(PTile.BACKGROUND); out.writeId(rb); out.write4(tile.getBackgroundPosition().x); out.write4(tile.getBackgroundPosition().y); out.write4(tile.getSize().width); out.write4(tile.getSize().height); out.write4(tile.getDepth()); out.write4((Integer) tile.properties.get(PTile.ID)); out.writeBool(tile.isLocked()); } out.writeBool(rm.properties,PRoom.REMEMBER_WINDOW_SIZE); out.write4(rm.properties,PRoom.EDITOR_WIDTH,PRoom.EDITOR_HEIGHT); out.writeBool(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); out.write4(rm.properties,PRoom.CURRENT_TAB,PRoom.SCROLL_BAR_X,PRoom.SCROLL_BAR_Y); } out.endDeflate(); } } public static void writeIncludedFiles(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { ver = ver >= 800 ? 800 : ver >= 620 ? 620 : 0; if (ver < 620) return; out.write4(ver); ResourceList<Include> includes = f.resMap.getList(Include.class); out.write4(includes.size()); for (Include i : includes) { if (ver >= 800) { out.beginDeflate(); out.writeD(gs.getLastChanged()); } out.write4(ver); out.writeStr(i.filename); out.writeStr(i.filepath); out.writeBool(i.isOriginal); out.write4(i.size); if (i.data != null) { out.writeBool(true); out.write4(i.data.length); out.write(i.data); } else out.writeBool(false); out.write4(i.export); out.writeStr(i.exportFolder); out.writeBool(i.overwriteExisting); out.writeBool(i.freeMemAfterExport); out.writeBool(i.removeAtGameEnd); out.endDeflate(); } } public static void writePackages(ProjectFile f, GmStreamEncoder out, int ver) throws IOException { if (ver < 700) return; out.write4(700); out.write4(f.packages.size()); for (String s : f.packages) out.writeStr(s); } public static void writeGameInformation(ProjectFile f, GmStreamEncoder out, int ver, GameSettings gs) throws IOException { ver = ver >= 800 ? 800 : /*ver >= 620 ? 620 : */ver >= 600 ? 600 : 430; out.write4(ver); if (ver == 800) out.beginDeflate(); GameInformation g = f.gameInfo; PropertyMap<PGameInformation> p = g.properties; out.write4(Util.getGmColor((Color) p.get(PGameInformation.BACKGROUND_COLOR))); if (ver < 800) out.writeBool(p,PGameInformation.EMBED_GAME_WINDOW); else out.writeBool(!(Boolean) p.get(PGameInformation.EMBED_GAME_WINDOW)); out.writeStr(p,PGameInformation.FORM_CAPTION); out.write4(p,PGameInformation.LEFT,PGameInformation.TOP,PGameInformation.WIDTH, PGameInformation.HEIGHT); out.writeBool(p,PGameInformation.SHOW_BORDER,PGameInformation.ALLOW_RESIZE, PGameInformation.STAY_ON_TOP,PGameInformation.PAUSE_GAME); if (ver >= 800) out.writeD(gs.getLastChanged()); out.writeStr(p,PGameInformation.TEXT); out.endDeflate(); } public static void writeTree(GmStreamEncoder out, ResNode root) throws IOException { Enumeration<?> e = root.preorderEnumeration(); e.nextElement(); while (e.hasMoreElements()) { ResNode node = (ResNode) e.nextElement(); if (node.kind == Shader.class || node.kind == Include.class || node.kind == Extension.class || node.kind == Constants.class) { continue; } out.write4(node.status); if (ProjectFile.RESOURCE_CODE.containsKey(node.kind)) out.write4(ProjectFile.RESOURCE_CODE.get(node.kind)); else out.write4(0); Resource<?,?> res = deRef((ResourceReference<?>) node.getRes()); if (res != null && res instanceof InstantiableResource<?,?>) out.write4(((InstantiableResource<?,?>) res).getId()); else out.write4(0); out.writeStr((String) node.getUserObject()); out.write4(node.getChildCount()); } } public static void writeActions(GmStreamEncoder out, ActionContainer container) throws IOException { out.write4(400); out.write4(container.actions.size()); for (Action act : container.actions) { LibAction la = act.getLibAction(); out.write4(440); out.write4(la.parent != null ? la.parent.id : la.parentId); out.write4(la.id); out.write4(la.actionKind); out.writeBool(la.allowRelative); out.writeBool(la.question); out.writeBool(la.canApplyTo); out.write4(la.execType); if (la.execType == Action.EXEC_FUNCTION) out.writeStr(la.execInfo); else out.write4(0); if (la.execType == Action.EXEC_CODE) out.writeStr(la.execInfo); else out.write4(0); List<Argument> args = act.getArguments(); out.write4(args.size()); out.write4(args.size()); for (Argument arg : args) out.write4(arg.kind); ResourceReference<GmObject> at = act.getAppliesTo(); if (at != null) { if (at == GmObject.OBJECT_OTHER) out.write4(-2); else if (at == GmObject.OBJECT_SELF) out.write4(-1); else out.writeId(at,-100); } else out.write4(-100); out.writeBool(act.isRelative()); out.write4(args.size()); for (Argument arg : args) { Class<? extends Resource<?,?>> kind = Argument.getResourceKind(arg.kind); if (kind != null && InstantiableResource.class.isAssignableFrom(kind)) { Resource<?,?> r = deRef((ResourceReference<?>) arg.getRes()); if (r != null && r instanceof InstantiableResource<?,?>) out.writeStr(Integer.toString(((InstantiableResource<?,?>) r).getId())); else out.writeStr("-1"); } else out.writeStr(arg.getVal()); } out.writeBool(act.isNot()); } } }