Skip to content

Commit

Permalink
Included File Editing Support (#434)
Browse files Browse the repository at this point in the history
Completely, brand new, included file editor. The basic decision made here is that we are going with a revamped GameMaker 5 version of data files. This is like GMSv1.4 in that the included file hierarchy is present in the main tree. It differs in that I decided not to open up LGM's main tree to having file extensions in resource names for fear of regression. However, since the new editor, like GM5's, allows you to edit the file name property separately, it should still remain GMSv1.4 compatible.

I had to make a lot of fixes to the file readers and writers. We were ignoring that the data files in GMD are zlib compressed. We were ignoring the store option for GMK 800 and 810 include files. We were throwing out the data files hierarchy in the GMD, which we now reuse for includes. In GMX, we needed an enum mapping for the export action in order to use the property link factory.
  • Loading branch information
RobertBColton authored May 23, 2019
1 parent dd75299 commit c8f395b
Show file tree
Hide file tree
Showing 11 changed files with 367 additions and 186 deletions.
24 changes: 14 additions & 10 deletions org/lateralgm/file/GMXFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import javax.imageio.ImageIO;
import javax.swing.JProgressBar;
import javax.xml.parsers.DocumentBuilder;
Expand Down Expand Up @@ -106,10 +107,10 @@
import org.lateralgm.resources.sub.View.PView;
import org.lateralgm.util.PropertyMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
Expand Down Expand Up @@ -337,7 +338,7 @@ private static void readGroup(ProjectFileContext c, ResNode root, Class<?> kind)
Document in = c.in;
ResNode node = new ResNode(Resource.kindNamesPlural.get(kind),ResNode.STATUS_PRIMARY,kind,null);
root.add(node);

String kindTagName = GMXFileWriter.tagNames.get(kind);
if (kindTagName == null) return;
NodeList list = in.getElementsByTagName(kindTagName);
Expand Down Expand Up @@ -865,7 +866,7 @@ private static void readScript(ProjectFileContext c, ResNode node, Node cNode)
node.add(rnode);
String path = f.getDirectory() + '/' + Util.getPOSIXPath(cNode.getTextContent());

try (BufferedReader reader = new BufferedReader(new FileReader(path)))
try (BufferedReader reader = new BufferedReader(new FileReader(path)))
{
String code = ""; //$NON-NLS-1$
String line = reader.readLine();
Expand All @@ -875,7 +876,7 @@ private static void readScript(ProjectFileContext c, ResNode node, Node cNode)
line = reader.readLine();
if (line == null) return;
}
do
do
{
if (line.startsWith("#define")) //$NON-NLS-1$
{
Expand Down Expand Up @@ -917,7 +918,7 @@ private static void readShader(ProjectFileContext c, ResNode node, Node cNode)
String code = ""; //$NON-NLS-1$
String path = f.getDirectory() + '/' + Util.getPOSIXPath(cNode.getTextContent());

try (BufferedReader reader = new BufferedReader(new FileReader(path)))
try (BufferedReader reader = new BufferedReader(new FileReader(path)))
{
String line = ""; //$NON-NLS-1$
while ((line = reader.readLine()) != null)
Expand Down Expand Up @@ -1587,7 +1588,9 @@ private static void readInclude(ProjectFileContext c, ResNode node, Node cNode)

final Include inc = f.resMap.getList(Include.class).add();
String name = el.getElementsByTagName("name").item(0).getTextContent(); //$NON-NLS-1$
inc.setName(name);
//NOTE: we don't yet allow file extensions in the resource tree at all
//and it really might not be a good idea to allow that anyway
inc.setName(Util.fileNameWithoutExtension(name));
ResNode rnode = new ResNode(inc.getName(),ResNode.STATUS_SECONDARY,Include.class,inc.reference);
node.add(rnode);

Expand All @@ -1604,17 +1607,18 @@ private static void readInclude(ProjectFileContext c, ResNode node, Node cNode)
String exportFolder = el.getElementsByTagName("exportDir").item(0).getTextContent(); //$NON-NLS-1$
inc.put(PInclude.EXPORTFOLDER,exportFolder);
int exportAction = Integer.parseInt(el.getElementsByTagName("exportAction").item(0).getTextContent()); //$NON-NLS-1$
inc.put(PInclude.EXPORTACTION,exportAction);
inc.put(PInclude.EXPORTACTION,ProjectFile.INCLUDE_EXPORT_ACTION[exportAction]);
String filename = el.getElementsByTagName("filename").item(0).getTextContent(); //$NON-NLS-1$
inc.put(PInclude.FILENAME,filename);

String filePath = filename;
ResNode parent = node;
while (parent != null && parent.status == ResNode.STATUS_GROUP) {
filePath = parent.toString() + '/' + filePath;
filePath = parent.toString() + File.separatorChar + filePath;
parent = (ResNode) parent.getParent();
}
filePath = f.getDirectory() + "/datafiles/" + filePath; //$NON-NLS-1$
filePath = f.getDirectory() + File.separatorChar + "datafiles" + File.separatorChar + filePath; //$NON-NLS-1$
inc.put(PInclude.FILEPATH,filePath);
File dataFile = new File(filePath);
try
{
Expand Down Expand Up @@ -1699,7 +1703,7 @@ private static void readGameInformation(ProjectFileContext c, ResNode root)

String text = ""; //$NON-NLS-1$

try (BufferedReader reader = new BufferedReader(new FileReader(path)))
try (BufferedReader reader = new BufferedReader(new FileReader(path)))
{
String line = ""; //$NON-NLS-1$
while ((line = reader.readLine()) != null)
Expand Down
5 changes: 3 additions & 2 deletions org/lateralgm/file/GMXFileWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,7 @@ private static void writeRoom(ProjectFileContext c, ResNode resNode, Element dom
// Write Backgrounds
Element backroot = doc.createElement("backgrounds"); //$NON-NLS-1$
roomroot.appendChild(backroot);
for (BackgroundDef back : room.backgroundDefs)
for (BackgroundDef back : room.backgroundDefs)
{
PropertyMap<PBackgroundDef> props = back.properties;
Element bckelement = doc.createElement("background"); //$NON-NLS-1$
Expand Down Expand Up @@ -1298,8 +1298,9 @@ private static void writeInclude(ProjectFileContext c, ResNode resNode, Element
include.get(PInclude.SIZE).toString()));
incRoot.appendChild(createElement(dom,"exportDir", //$NON-NLS-1$
include.get(PInclude.EXPORTFOLDER).toString()));
int exportCode = ProjectFile.INCLUDE_EXPORT_CODE.get(include.get(PInclude.EXPORTACTION));
incRoot.appendChild(createElement(dom,"exportAction", //$NON-NLS-1$
include.get(PInclude.EXPORTACTION).toString()));
Integer.toString(exportCode)));
incRoot.appendChild(createElement(dom,"overwrite", //$NON-NLS-1$
boolToString((Boolean)include.get(PInclude.OVERWRITE))));
incRoot.appendChild(createElement(dom,"store", //$NON-NLS-1$
Expand Down
124 changes: 84 additions & 40 deletions org/lateralgm/file/GmFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.zip.DataFormatException;

import javax.swing.JProgressBar;
import javax.swing.tree.TreeNode;

import org.lateralgm.components.impl.ResNode;
import org.lateralgm.file.ProjectFile.ResourceHolder;
Expand All @@ -47,16 +48,18 @@
import org.lateralgm.resources.GmObject;
import org.lateralgm.resources.GmObject.PGmObject;
import org.lateralgm.resources.Include;
import org.lateralgm.resources.Include.ExportAction;
import org.lateralgm.resources.Include.PInclude;
import org.lateralgm.resources.InstantiableResource;
import org.lateralgm.resources.Path;
import org.lateralgm.resources.Shader;
import org.lateralgm.resources.Path.PPath;
import org.lateralgm.resources.Resource;
import org.lateralgm.resources.ResourceReference;
import org.lateralgm.resources.Room;
import org.lateralgm.resources.Room.PRoom;
import org.lateralgm.resources.Script;
import org.lateralgm.resources.Script.PScript;
import org.lateralgm.resources.Shader;
import org.lateralgm.resources.Sound;
import org.lateralgm.resources.Sound.PSound;
import org.lateralgm.resources.Sprite;
Expand Down Expand Up @@ -478,18 +481,23 @@ private static void readSettingsIncludes(ProjectFile f, GmStreamDecoder in, Game
for (int i = 0; i < no; i++)
{
Include inc = f.resMap.getList(Include.class).add();
inc.filepath = in.readStr();
inc.filename = new File(inc.filepath).getName();
String filepath = in.readStr();
String filename = new File(filepath).getName();
inc.put(PInclude.FILEPATH,filepath);
inc.put(PInclude.FILENAME,filename);
}
gs.put(PGameSettings.INCLUDE_FOLDER,ProjectFile.GS_INCFOLDERS[in.read4()]);
// f.gameSettings.includeFolder = in.read4(); //0 = main, 1 = temp
in.readBool(gs.properties,PGameSettings.OVERWRITE_EXISTING,
PGameSettings.REMOVE_AT_GAME_END);
//1 = temp, 2 = main
ExportAction exportAction = gs.get(PGameSettings.INCLUDE_FOLDER) == IncludeFolder.TEMP
? ExportAction.TEMP_DIRECTORY : ExportAction.SAME_FOLDER;
for (Include inc : f.resMap.getList(Include.class))
{
inc.export = gs.get(PGameSettings.INCLUDE_FOLDER) == IncludeFolder.TEMP ? 1 : 2; //1 = temp, 2 = main
inc.overwriteExisting = gs.get(PGameSettings.OVERWRITE_EXISTING);
inc.removeAtGameEnd = gs.get(PGameSettings.REMOVE_AT_GAME_END);
inc.put(PInclude.EXPORTACTION,exportAction);
inc.put(PInclude.OVERWRITE,gs.get(PGameSettings.OVERWRITE_EXISTING));
inc.put(PInclude.REMOVEATGAMEEND,gs.get(PGameSettings.REMOVE_AT_GAME_END));
}
}

Expand Down Expand Up @@ -819,7 +827,7 @@ private static void readScripts(ProjectFileContext c) throws IOException,GmForma
}
}

private static void readFonts(ProjectFileContext c, int ver) throws IOException,GmFormatException
private static void readFonts(ProjectFileContext c, int ver) throws IOException,GmFormatException,DataFormatException
{
ProjectFile f = c.f;
GmStreamDecoder in = c.in;
Expand All @@ -838,20 +846,22 @@ private static void readFonts(ProjectFileContext c, int ver) throws IOException,
throw new GmFormatException(f,Messages.format("ProjectFileReader.ERROR_UNSUPPORTED", //$NON-NLS-1$
Messages.getString("ProjectFileReader.INDATAFILES"),ver)); //$NON-NLS-1$
Include inc = f.resMap.getList(Include.class).add();
inc.filepath = in.readStr();
inc.filename = new File(inc.filepath).getName();
String filepath = in.readStr();
String filename = new File(filepath).getName();
inc.put(PInclude.FILEPATH,filepath);
inc.put(PInclude.FILENAME,filename);
if (in.readBool()) //file data exists?
{
inc.size = in.read4();
inc.data = new byte[inc.size];
in.read(inc.data,0,inc.size);
inc.data = in.decompress(in.read4());
if (inc.data != null)
inc.put(PInclude.SIZE,inc.data.length);
}
inc.export = in.read4();
inc.put(PInclude.EXPORTACTION,ProjectFile.INCLUDE_EXPORT_ACTION[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();
inc.put(PInclude.OVERWRITE,in.readBool());
inc.put(PInclude.FREEMEMORY,in.readBool());
inc.put(PInclude.REMOVEATGAMEEND,in.readBool());
}
return;
}
Expand Down Expand Up @@ -1113,21 +1123,24 @@ private static void readIncludedFiles(ProjectFileContext c) throws IOException,G
throw new GmFormatException(f,Messages.format("ProjectFileReader.ERROR_UNSUPPORTED", //$NON-NLS-1$
Messages.getString("ProjectFileReader.ININCLUDEDFILES"),ver)); //$NON-NLS-1$
Include inc = f.resMap.getList(Include.class).add();
inc.filename = in.readStr();
inc.filepath = in.readStr();
inc.isOriginal = in.readBool();
inc.size = in.read4();
if (in.readBool()) //store in editable?
inc.put(PInclude.FILENAME,in.readStr());
inc.put(PInclude.FILEPATH,in.readStr());
inc.put(PInclude.ORIGINAL,in.readBool());
int size = in.read4();
inc.put(PInclude.SIZE,size);
boolean store = in.readBool();
inc.put(PInclude.STORE,store);
if (store)
{
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();
inc.put(PInclude.EXPORTACTION,ProjectFile.INCLUDE_EXPORT_ACTION[in.read4()]);
inc.put(PInclude.EXPORTFOLDER,in.readStr());
inc.put(PInclude.OVERWRITE,in.readBool());
inc.put(PInclude.FREEMEMORY,in.readBool());
inc.put(PInclude.REMOVEATGAMEEND,in.readBool());
in.endInflate();
}
}
Expand Down Expand Up @@ -1189,6 +1202,9 @@ private static void readTree(ProjectFileContext c, ResNode root, int ver) throws
{
byte status = (byte) in.read4();
Class<?> type = ProjectFile.RESOURCE_KIND[in.read4()];
// It's "Data Files" in GM5, some of which are fonts.
if (ver == 500 && type == Font.class)
type = Include.class;
int ind = in.read4();
String name = in.readStr();
boolean hasRef;
Expand All @@ -1199,11 +1215,18 @@ private static void readTree(ProjectFileContext c, ResNode root, int ver) throws
hasRef = false;
ResourceList<?> rl = hasRef ? (ResourceList<?>) f.resMap.get(type) : null;
ResNode node = new ResNode(name,status,type,hasRef ? rl.getUnsafe(ind).reference : null);
if (ver == 500 && type == Include.class)
{
// Included files don't redundantly store the name with the metadata
// so we need to sync the resource name with the tree name.
if (hasRef) rl.getUnsafe(ind).setName(name);

if (ver == 500 && status == ResNode.STATUS_PRIMARY && type == Font.class)
path.peek().addChild(Messages.getString("LGM.FNT"),status,type); //$NON-NLS-1$
else
path.peek().add(node);
// GameMaker 5 did not have a dedicated primary fonts group, let's add one.
if (status == ResNode.STATUS_PRIMARY)
path.peek().addChild(Messages.getString("LGM.FNT"),status,Font.class); //$NON-NLS-1$
}

path.peek().add(node);
int contents = in.read4();
if (contents > 0)
{
Expand All @@ -1216,25 +1239,46 @@ private static void readTree(ProjectFileContext c, ResNode root, int ver) throws
rootnodes = left.pop().intValue();
path.pop();
}
}

ResNode incRoot = null;
if (ver == 500)
{
// For GameMaker 5 we need to move the "Data Files" folder
// to the place of the "Includes" folder in LGM's default tree
TreeNode dataFileNode = root.getChildAt(6);
if (dataFileNode instanceof ResNode)
{
incRoot = (ResNode)dataFileNode;
incRoot.setUserObject("Includes");
root.remove(incRoot);
}
}
if (ver <= 540) root.addChild(Messages.getString("LGM.EXT"), //$NON-NLS-1$
else
{
// All newer GameMaker versions don't have a primary node for included files.
// Therefore we need to create a primary node for them and construct a tree.
incRoot = new ResNode("Includes",ResNode.STATUS_PRIMARY,Include.class);
for (Include inc : f.resMap.getList(Include.class))
{
String filename = inc.get(PInclude.FILENAME).toString();
if (!filename.isEmpty())
inc.setName(Util.fileNameWithoutExtension(filename));
incRoot.add(new ResNode(inc.getName(),ResNode.STATUS_SECONDARY,Include.class,inc.reference));
}
}
root.insert(incRoot,9);

if (ver <= 540) root.addChild("Extension Packages",
ResNode.STATUS_SECONDARY,ExtensionPackages.class);

//TODO: This just makes the GMK arrange to the modern version of the IDE
//This just makes the GMK arrange to the modern version of the IDE
ResNode node = new ResNode("Shaders",ResNode.STATUS_PRIMARY,Shader.class);
root.insert(node,5);
node = new ResNode("Extensions",ResNode.STATUS_PRIMARY,Extension.class);
root.insert(node,10);
node = new ResNode("Includes",ResNode.STATUS_PRIMARY,Include.class);
root.insert(node,10);
for (Include inc : f.resMap.getList(Include.class))
{
node.add(new ResNode(inc.getName(),ResNode.STATUS_SECONDARY,Include.class,inc.reference));
}
root.insert(node,11);
node = new ResNode("Constants",ResNode.STATUS_SECONDARY,Constants.class);
root.insert(node,12);

}

private static void readActions(ProjectFileContext c, ActionContainer container, String errorKey,
Expand Down
Loading

0 comments on commit c8f395b

Please sign in to comment.