public static CodeInfo DeserializeCodeFromFile(string filename, uint bcv, StringsListBuilder strings, IDictionary <string, uint> objectIndices) { IEnumerable <Instruction> instructions; if (filename.ToLowerInvariant().EndsWith(SR.EXT_GML_ASM)) { instructions = Parser.Parse(Tokenizer.Tokenize(File.ReadAllText(filename))); } else if (filename.ToLowerInvariant().EndsWith(SR.EXT_GML_LSP)) { // TODO throw new NotImplementedException(); } else { throw new InvalidDataException("Unknown code format for '" + filename + "'"); } return(DeserializeAssembly(Path.GetFileNameWithoutExtension(filename), instructions, bcv, strings, objectIndices)); }
public static CodeInfo DeserializeCodeFromFile(string filename, uint bcv, StringsListBuilder strings, IDictionary <string, uint> objectIndices) { IEnumerable <Instruction> instructions; if (filename.EndsWith(SR.EXT_GML_ASM, StringComparison.InvariantCultureIgnoreCase)) { instructions = Parser.Parse(Tokenizer.Tokenize(File.ReadAllText(filename))); } else if (filename.EndsWith(SR.EXT_GML_LSP, StringComparison.InvariantCultureIgnoreCase)) { // TODO throw new NotImplementedException("Compiling LSP not supported; export ASM with --project or -d"); } else { throw new InvalidDataException("Unknown code format for '" + filename + "'"); } return(DeserializeAssembly(Path.GetFileNameWithoutExtension(filename), instructions, bcv, strings, objectIndices)); }
public static GMFile /* errors: different return type? */ ReadFile(string baseDir, JsonData projFile) { var f = new GMFile(); // OBJT: depends on SPRT, obj<->id map // ROOM: depends on OBJT, BGND // SCPT: depends on CODE if (projFile.Has("chunkorder") && projFile["chunkorder"].IsArray) { f.ChunkOrder = DeserializeArray(projFile["chunkorder"], jd => SectionHeadersExtensions.FromChunkName((string)jd)); } else { Console.Error.WriteLine("Warning: Project file doesn't have a chunk order. You should export with a newer Altar.NET version."); f.ChunkOrder = new SectionHeaders[] { SectionHeaders.General, SectionHeaders.Options, SectionHeaders.Language, SectionHeaders.Extensions, SectionHeaders.Sounds, SectionHeaders.AudioGroup, SectionHeaders.Sprites, SectionHeaders.Backgrounds, SectionHeaders.Paths, SectionHeaders.Scripts, SectionHeaders.Globals, SectionHeaders.Shaders, SectionHeaders.Fonts, SectionHeaders.Timelines, SectionHeaders.Objects, SectionHeaders.Rooms, SectionHeaders.DataFiles, SectionHeaders.TexturePage, SectionHeaders.Code, SectionHeaders.Variables, SectionHeaders.Functions, SectionHeaders.Strings, SectionHeaders.Textures, SectionHeaders.Audio, SectionHeaders.EmbedImage, }; } if (projFile.Has("general")) { Console.WriteLine("Loading general..."); try { f.General = DeserializeGeneral(LoadJson(baseDir, (string)(projFile["general"]))); if (f.General.Version >= new Version(2, 0)) { Console.Error.WriteLine("Warning: GM:S 2.0 support is incomplete!"); } } catch (Exception) { Console.Error.WriteLine("Error loading general"); throw; } } if (projFile.Has("options")) { Console.WriteLine("Loading options..."); try { f.Options = DeserializeOptions(LoadJson(baseDir, (string)(projFile["options"]))); } catch (Exception) { Console.Error.WriteLine("Error loading options"); throw; } } if (projFile.Has("strings")) { Console.WriteLine("Loading strings..."); try { f.Strings = DeserializeArray(LoadJson(baseDir, (string)(projFile["strings"])), jd => (string)jd); } catch (Exception) { Console.Error.WriteLine("Error loading strings"); throw; } } var variables = new ReferenceDef[0]; var functions = new ReferenceDef[0]; if (projFile.Has("variables")) { Console.WriteLine("Loading variables..."); try { var vardata = LoadJson(baseDir, (string)(projFile["variables"])); variables = DeserializeArray(vardata.IsArray ? vardata : vardata["variables"], DeserializeReferenceDef); if (vardata.Has("extra")) { f.VariableExtra = DeserializeArray(vardata["extra"], jd => (uint)jd); } } catch (Exception) { Console.Error.WriteLine("Error loading variables"); throw; } } if (projFile.Has("functions")) { Console.WriteLine("Loading functions..."); try { var funcdata = LoadJson(baseDir, (string)(projFile["functions"])); functions = DeserializeArray(funcdata.IsArray ? funcdata : funcdata["functions"], DeserializeReferenceDef); if (funcdata.Has("locals")) { f.FunctionLocals = DeserializeArray(funcdata["locals"], DeserializeFuncLocals); } } catch (Exception) { Console.Error.WriteLine("Error loading functions"); throw; } } f.RefData = new RefData { Variables = variables, Functions = functions }; if (projFile.Has("textures")) { Console.WriteLine("Loading textures..."); var textures = projFile["textures"].ToArray(); var ts = new TextureInfo[textures.Length]; for (int i = 0; i < textures.Length; i++) { try { var texinfo = new TextureInfo { PngData = File.ReadAllBytes(Path.Combine(baseDir, (string)(textures[i]))) }; var bp = new UniquePtr(texinfo.PngData); unsafe { var png = (PngHeader *)bp.BPtr; texinfo.Width = Utils.SwapEnd32(png->IHDR.Width); texinfo.Height = Utils.SwapEnd32(png->IHDR.Height); } ts[i] = texinfo; } catch (Exception) { Console.Error.WriteLine($"Error loading {textures[i]}"); throw; } } f.Textures = ts; } if (projFile.Has("tpags")) { Console.Write("Loading texture pages... "); var cl = Console.CursorLeft; var ct = Console.CursorTop; var tpags = projFile["tpags"].ToArray(); var tps = new TexturePageInfo[tpags.Length]; for (int i = 0; i < tpags.Length; i++) { Console.SetCursorPosition(cl, ct); Console.WriteLine(O_PAREN + (i + 1) + SLASH + tpags.Length + C_PAREN); try { tps[i] = DeserializeTPag(LoadJson(baseDir, (string)(tpags[i]))); } catch (Exception) { Console.Error.WriteLine($"Error loading {tpags[i]}"); throw; } } f.TexturePages = tps; } if (projFile.Has("audio")) { Console.WriteLine("Loading audio..."); var audio = projFile["audio"].ToArray(); var ais = new AudioInfo[audio.Length]; for (int i = 0; i < audio.Length; i++) { try { var audioinfo = new AudioInfo { Wave = File.ReadAllBytes(Path.Combine(baseDir, (string)(audio[i]))) }; ais[i] = audioinfo; } catch (Exception) { Console.Error.WriteLine($"Error loading {audio[i]}"); throw; } } f.Audio = ais; } if (projFile.Has("sprites")) { Console.Write("Loading sprites... "); var cl = Console.CursorLeft; var ct = Console.CursorTop; var sprites = projFile["sprites"].ToArray(); var ss = new SpriteInfo[sprites.Length]; for (int i = 0; i < sprites.Length; i++) { Console.SetCursorPosition(cl, ct); Console.WriteLine(O_PAREN + (i + 1) + SLASH + sprites.Length + C_PAREN); try { ss[i] = DeserializeSprite(LoadJson(baseDir, (string)(sprites[i]))); ss[i].Name = Path.GetFileNameWithoutExtension((string)(sprites[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {sprites[i]}"); throw; } } f.Sprites = ss; } if (projFile.Has("objs")) { Console.Write("Loading objects... "); var cl = Console.CursorLeft; var ct = Console.CursorTop; var objs = projFile["objs"].ToArray(); var objNames = objs.Select(o => Path.GetFileNameWithoutExtension((string)o)).ToArray(); var os = new ObjectInfo[objs.Length]; for (int i = 0; i < objs.Length; i++) { Console.SetCursorPosition(cl, ct); Console.WriteLine(O_PAREN + (i + 1) + SLASH + objs.Length + C_PAREN); try { os[i] = DeserializeObj( LoadJson(baseDir, (string)(objs[i])), f.Sprites, s => (uint)Array.IndexOf(objNames, s)); os[i].Name = objNames[i]; } catch (Exception) { Console.Error.WriteLine($"Error loading {objs[i]}"); throw; } } f.Objects = os; } if (projFile.Has("code")) { Console.WriteLine("Loading code..."); var code = projFile["code"].ToArray(); var cs = new CodeInfo[code.Length]; var strings = new StringsListBuilder(); strings.AddStrings(f.Strings); IDictionary <string, uint> objectIndices = new Dictionary <string, uint>(f.Objects.Length); for (uint i = 0; i < f.Objects.Length; i++) { objectIndices[f.Objects[i].Name] = i; } for (int i = 0; i < code.Length; i++) { Console.WriteLine((string)(code[i])); try { cs[i] = Assembler.DeserializeCodeFromFile(Path.Combine(baseDir, (string)(code[i])), f.General.BytecodeVersion, strings, objectIndices); cs[i].Name = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension((string)(code[i]))); cs[i].ArgumentCount = 1; if (f.FunctionLocals != null) { for (int j = 0; j < f.FunctionLocals.Length; j++) { int fastIndex = (j + i) % f.FunctionLocals.Length; if (f.FunctionLocals[fastIndex].FunctionName == cs[i].Name) { cs[i].ArgumentCount = f.FunctionLocals[fastIndex].LocalNames.Length; break; } } } } catch (Exception) { Console.Error.WriteLine($"Error loading {code[i]}"); throw; } } f.Code = cs; f.Strings = strings.GetStrings(); } if (projFile.Has("sounds")) { Console.WriteLine("Loading sounds..."); var sounds = projFile["sounds"].ToArray(); var ss = new SoundInfo[sounds.Length]; for (int i = 0; i < sounds.Length; i++) { try { ss[i] = DeserializeSound(LoadJson(baseDir, (string)(sounds[i]))); ss[i].Name = Path.GetFileNameWithoutExtension((string)(sounds[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {sounds[i]}"); throw; } } f.Sound = ss; } if (projFile.Has("bg")) { Console.WriteLine("Loading backgrounds..."); var bg = projFile["bg"].ToArray(); var bs = new BackgroundInfo[bg.Length]; for (int i = 0; i < bg.Length; i++) { try { bs[i] = DeserializeBg(LoadJson(baseDir, (string)(bg[i]))); bs[i].Name = Path.GetFileNameWithoutExtension((string)(bg[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {bg[i]}"); throw; } } f.Backgrounds = bs; } if (projFile.Has("paths")) { Console.WriteLine("Loading paths..."); var paths = projFile["paths"].ToArray(); var ps = new PathInfo[paths.Length]; for (int i = 0; i < paths.Length; i++) { try { ps[i] = DeserializePath(LoadJson(baseDir, (string)(paths[i]))); ps[i].Name = Path.GetFileNameWithoutExtension((string)(paths[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {paths[i]}"); throw; } } f.Paths = ps; } if (projFile.Has("scripts")) { Console.WriteLine("Loading scripts..."); var scripts = projFile["scripts"].ToArray(); var ss = new ScriptInfo[scripts.Length]; for (int i = 0; i < scripts.Length; i++) { try { ss[i] = DeserializeScript(LoadJson(baseDir, (string)(scripts[i])), f.Code); ss[i].Name = Path.GetFileNameWithoutExtension((string)(scripts[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {scripts[i]}"); throw; } } f.Scripts = ss; } if (projFile.Has("fonts")) { Console.WriteLine("Loading fonts..."); var fonts = projFile["fonts"].ToArray(); var fs = new FontInfo[fonts.Length]; for (int i = 0; i < fonts.Length; i++) { try { fs[i] = DeserializeFont(LoadJson(baseDir, (string)(fonts[i]))); fs[i].CodeName = Path.GetFileNameWithoutExtension((string)(fonts[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {fonts[i]}"); throw; } } f.Fonts = fs; } if (projFile.Has("rooms")) { Console.Write("Loading rooms... "); var cl = Console.CursorLeft; var ct = Console.CursorTop; var rooms = projFile["rooms"].ToArray(); var rs = new RoomInfo[rooms.Length]; for (int i = 0; i < rooms.Length; i++) { Console.SetCursorPosition(cl, ct); Console.WriteLine(O_PAREN + (i + 1) + SLASH + rooms.Length + C_PAREN); try { rs[i] = DeserializeRoom(LoadJson(baseDir, (string)(rooms[i])), f.Backgrounds, f.Objects); rs[i].Name = Path.GetFileNameWithoutExtension((string)(rooms[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {rooms[i]}"); throw; } } f.Rooms = rs; } if (projFile.Has("audiogroups")) { Console.WriteLine("Loading audio groups..."); try { f.AudioGroups = DeserializeArray(LoadJson(baseDir, (string)(projFile["audiogroups"])), jd => (string)jd); } catch (Exception) { Console.Error.WriteLine("Error loading audio groups"); throw; } } if (projFile.Has("shaders")) { Console.WriteLine("Loading shaders..."); var shaders = projFile["shaders"].ToArray(); var ss = new ShaderInfo[shaders.Length]; for (int i = 0; i < shaders.Length; i++) { try { ss[i] = DeserializeShader(LoadJson(baseDir, (string)(shaders[i]))); ss[i].Name = Path.GetFileNameWithoutExtension((string)(shaders[i])); } catch (Exception) { Console.Error.WriteLine($"Error loading {shaders[i]}"); throw; } } f.Shaders = ss; } return(f); }
private static CodeInfo DeserializeAssembly(string name, IEnumerable <Instruction> instructions, uint bcv, StringsListBuilder strings, IDictionary <string, uint> objectIndices) { uint size = 0; var labels = new Dictionary <string, uint>(); foreach (var inst in instructions) { if (inst is Label labelInst) { if (labelInst.LabelValue is string label) { labels[label] = size * sizeof(int); } } else { size += InstSize(inst, bcv); } } IList <Tuple <ReferenceSignature, uint> > functionReferences = new List <Tuple <ReferenceSignature, uint> >(); IList <Tuple <ReferenceSignature, uint> > variableReferences = new List <Tuple <ReferenceSignature, uint> >(); var binaryInstructions = new List <AnyInstruction>(); size = 0; foreach (var inst in instructions) { if (inst is Label) { continue; } var op = new OpCodes { VersionE = inst.OpCode.VersionE, VersionF = inst.OpCode.VersionF }; var type = DisasmExt.Kind(op, bcv); AnyInstruction bininst = new AnyInstruction(); switch (type) { case InstructionKind.Set: var setinst = (Set)inst; bininst.Set = new SetInstruction { DestVar = new Reference(setinst.VariableType, 0), Instance = GetInstanceType(setinst.InstanceType, setinst.InstanceName, objectIndices), OpCode = op, Types = new TypePair(setinst.Type1, setinst.Type2) }; variableReferences.Add(new Tuple <ReferenceSignature, uint>(new ReferenceSignature { Name = setinst.TargetVariable, InstanceType = setinst.InstanceType, Instance = setinst.InstanceType == InstanceType.Local ? name : null, VariableType = setinst.VariableType, VariableIndex = setinst.VariableIndex }, size)); break; case InstructionKind.Push: var bp = new PushInstruction { OpCode = op, Type = ((Push)inst).Type }; if (bp.Type == DataType.Variable) { var p = (PushVariable)inst; bp.Value = (short)GetInstanceType(p.InstanceType, p.InstanceName, objectIndices); bp.ValueRest = new Reference(p.VariableType, 0).val; variableReferences.Add(new Tuple <ReferenceSignature, uint>(new ReferenceSignature { Name = p.VariableName, InstanceType = p.InstanceType, Instance = p.InstanceType == InstanceType.Local ? name : null, VariableType = p.VariableType, VariableIndex = p.VariableIndex }, size)); } else { var p = (PushConst)inst; switch (p.Type) { case DataType.Int16: bp.Value = (short)(long)p.Value; break; case DataType.Boolean: bp.ValueRest = (uint)(long)p.Value; break; case DataType.Double: case DataType.Single: bp.ValueRest = BitConverter.ToUInt64(BitConverter.GetBytes(Convert.ToDouble(p.Value)), 0); break; case DataType.Int32: case DataType.Int64: bp.ValueRest = BitConverter.ToUInt64(BitConverter.GetBytes(unchecked ((long)(p.Value))), 0); break; case DataType.String: bp.ValueRest = strings.GetIndex((string)p.Value); break; } } bininst.Push = bp; break; case InstructionKind.Call: var callinst = (Call)inst; bininst.Call = new CallInstruction { Arguments = (ushort)callinst.Arguments, Function = new Reference(callinst.FunctionType, 0), OpCode = op, ReturnType = callinst.ReturnType }; functionReferences.Add(new Tuple <ReferenceSignature, uint>(new ReferenceSignature { Name = callinst.FunctionName, InstanceType = InstanceType.StackTopOrGlobal, VariableType = callinst.FunctionType, VariableIndex = -1 }, size)); break; case InstructionKind.Break: var breakinst = (Break)inst; bininst.Break = new BreakInstruction { OpCode = op, Signal = (short)breakinst.Signal, Type = breakinst.Type }; break; case InstructionKind.DoubleType: var doubleinst = (DoubleType)inst; bininst.DoubleType = new DoubleTypeInstruction { OpCode = op, Types = new TypePair(doubleinst.Type1, doubleinst.Type2) }; if (inst is Compare cmpinst) { bininst.DoubleType.ComparisonType = cmpinst.ComparisonType; } break; case InstructionKind.SingleType: var singleinst = (SingleType)inst; bininst.SingleType = new SingleTypeInstruction { OpCode = op, Type = singleinst.Type }; if (inst is Dup dupinst) { bininst.SingleType.DupExtra = dupinst.Extra; } break; case InstructionKind.Goto: var gotoinst = (Branch)inst; uint absTarget = 0; if (gotoinst.Label is long) { absTarget = (uint)(long)(gotoinst.Label); } else if (gotoinst.Label is string) { absTarget = labels[(string)gotoinst.Label]; } else if (gotoinst.Label == null) { bininst.Goto = new BranchInstruction { Offset = new Int24(0xF00000), OpCode = op }; break; } else { Console.Error.WriteLine($"Error in {name}: Can't use label {gotoinst.Label}"); break; } var relTarget = (int)absTarget - (int)size; uint offset = unchecked ((uint)relTarget); if (relTarget < 0) { offset &= 0xFFFFFF; offset += 0x1000000; } offset /= 4; bininst.Goto = new BranchInstruction { Offset = new Int24(offset), OpCode = op }; break; default: Console.Error.WriteLine($"Error in {name}: Unknown instruction type {type}!"); continue; } binaryInstructions.Add(bininst); unsafe { size += DisasmExt.Size(&bininst, bcv) * 4; } } return(new CodeInfo { Size = (int)size, InstructionsCopy = binaryInstructions.ToArray(), functionReferences = functionReferences, variableReferences = variableReferences }); }