private void TestBBEmpty(float[] data, int period, float sigmaWidth) { BBData res = _testObj.Calculate(data, period, sigmaWidth); res.SMA.ShouldBeEmpty(); res.BBL.ShouldBeEmpty(); res.BBH.ShouldBeEmpty(); }
private void TestBB(float[] data, BBData expected, int period, float sigmaWidth) { BBData res = _testObj.Calculate(data, period, sigmaWidth); res.SMA.ShouldBe(expected.SMA, 0.0001f); res.BBL.ShouldBe(expected.BBL, 0.0001f); res.BBH.ShouldBe(expected.BBH, 0.0001f); }
public override void Calculate(StockPricesData data) { BBData res = (new BB()).Calculate(data.C, _statParams.Get(StatBBParams.Period).As <int>(), _statParams.Get(StatBBParams.SigmaWidth).As <float>()); _data[StatBBData.BBH] = res.BBH; _data[StatBBData.SMA] = res.SMA; _data[StatBBData.BBL] = res.BBL; }
private static void WriteCodeInfo(BBData data, CodeInfo ci, StringsChunkBuilder strings, uint bytecodeVersion) { data.Buffer.Write(strings.GetOffset(ci.Name)); // Name data.Buffer.Write(ci.Size); // Length if (bytecodeVersion > 0xE) { data.Buffer.Write(ci.ArgumentCount); // ArgumentCount data.Buffer.Write(0); // BytecodeOffset data.Buffer.Write(0); // pad } else { WriteCodeBlock(data, ci.InstructionsCopy, bytecodeVersion); } }
public static void WriteCodeBlock(BBData data, AnyInstruction[] instructions, uint bytecodeVersion) { foreach (var inst in instructions) { var ins_ = inst; var instdata = new BinBuffer(); instdata.Write(inst); uint size; unsafe { size = DisasmExt.Size(&ins_, bytecodeVersion) * 4; } data.Buffer.Write(instdata, 0, (int)size, 0); } }
public static void ResolveReferenceOffsets(BBData data, IList <Tuple <ReferenceSignature, uint> > references, StringsChunkBuilder strings, bool extended, out IList <ReferenceDef> startOffsetsAndCounts) { startOffsetsAndCounts = new List <ReferenceDef>(); int localCount = 0; int nonLocalCount = 0; for (int i = 0; i < references.Count; i++) { Tuple <ReferenceSignature, uint> last = references[i]; uint diff; uint existing; uint count = 0; var targetRef = references[i].Item1; var start = references[i].Item2; if (targetRef.InstanceType >= InstanceType.StackTopOrGlobal && extended && targetRef.VariableIndex != -1) { for (InstanceType possibleInstanceType = InstanceType.Self; possibleInstanceType >= InstanceType.Local; possibleInstanceType--) { for (int j = i + 1; j < references.Count; j++) { if (references[j].Item1.Name == targetRef.Name && references[j].Item1.InstanceType == possibleInstanceType) { targetRef.InstanceType = references[j].Item1.InstanceType; targetRef.Instance = references[j].Item1.Instance; break; } } if (targetRef.InstanceType < InstanceType.StackTopOrGlobal) { break; } } if (targetRef.InstanceType >= InstanceType.StackTopOrGlobal) { targetRef.InstanceType = InstanceType.Self; // ?? } } if (targetRef.InstanceType == InstanceType.Local && targetRef.Name == "arguments") { //localCount = 0; } if (start != 0xFFFFFFFF) { count = 1; for (int j = i + 1; j < references.Count;) { if (references[j].Item1.Name == targetRef.Name && (!extended || (targetRef.VariableIndex != -1) || (references[j].Item1.InstanceType >= InstanceType.StackTopOrGlobal) || (references[j].Item1.InstanceType == targetRef.InstanceType && //references[j].Item1.VariableType == targetRef.VariableType && (references[j].Item1.InstanceType != InstanceType.Local || references[j].Item1.Instance == targetRef.Instance))) && ((targetRef.VariableIndex == -1) || (targetRef.VariableIndex == references[j].Item1.VariableIndex))) { diff = (references[j].Item2 - last.Item2) & 0xFFFFFF; data.Buffer.Position = (int)last.Item2 + 4; existing = data.Buffer.ReadUInt32(); data.Buffer.Write(diff | existing); last = references[j]; references.RemoveAt(j); count++; } else { j++; } } diff = strings.GetIndex(last.Item1.Name); data.Buffer.Position = (int)last.Item2 + 4; existing = data.Buffer.ReadUInt32(); data.Buffer.Write(diff | existing); } var def = new ReferenceDef { FirstOffset = start, HasExtra = extended, InstanceType = targetRef.InstanceType, Name = targetRef.Name, Occurrences = count, unknown2 = targetRef.InstanceType == InstanceType.Local ? localCount : targetRef.VariableType == VariableType.StackTop ? nonLocalCount : -6, VariableType = targetRef.VariableType }; if (targetRef.VariableIndex == -1) { startOffsetsAndCounts.Add(def); } else { while (startOffsetsAndCounts.Count <= targetRef.VariableIndex) { startOffsetsAndCounts.Add(new ReferenceDef()); } startOffsetsAndCounts[targetRef.VariableIndex] = def; } if (targetRef.InstanceType == InstanceType.Local) { localCount++; } else if (targetRef.VariableType == VariableType.StackTop) { nonLocalCount++; } } }
public static int[] WriteCodes(BBData data, GMFile f, StringsChunkBuilder strings) { int bytecodeSize = 0; foreach (var ci in f.Code) { bytecodeSize += ci.Size; } Console.WriteLine($"Assembling..."); BBData[] datas = new BBData[f.Code.Length]; for (int i = 0; i < f.Code.Length; i++) { BBData codedata = new BBData(new BinBuffer(), new int[0]); WriteCodeInfo(codedata, f.Code[i], strings, f.General.BytecodeVersion); datas[i] = codedata; } data.Buffer.Write(datas.Length); var allOffs = data.OffsetOffsets.ToList(); var offAcc = data.Buffer.Position + datas.Length * sizeof(int); // after all offsets if (f.General.BytecodeVersion > 0xE) { offAcc += bytecodeSize; } int[] offsets = new int[datas.Length]; var stringOffsetOffsets = new int[f.Code.Length]; for (int i = 0; i < datas.Length; i++) { allOffs.Add(data.Buffer.Position); data.Buffer.Write(offAcc); offsets[i] = offAcc; stringOffsetOffsets[i] = offAcc + 8; offAcc += datas[i].Buffer.Size; } Console.WriteLine($"Linking..."); IList <Tuple <ReferenceSignature, uint> > functionReferences = new List <Tuple <ReferenceSignature, uint> >(); IList <Tuple <ReferenceSignature, uint> > variableReferences = new List <Tuple <ReferenceSignature, uint> >(); variableReferences.Add(new Tuple <ReferenceSignature, uint>(new ReferenceSignature { Name = "prototype", InstanceType = InstanceType.Self, VariableType = VariableType.Normal, VariableIndex = 0 }, 0xFFFFFFFF)); variableReferences.Add(new Tuple <ReferenceSignature, uint>(new ReferenceSignature { Name = "@@array@@", InstanceType = InstanceType.Self, VariableType = VariableType.Normal, VariableIndex = 1 }, 0xFFFFFFFF)); int[] bytecodeOffsets = null; if (f.General.BytecodeVersion > 0xE) { // In >=F bytecodes, the code comes before the info data, which // is why this method can't just be a call to WriteList. bytecodeOffsets = new int[f.Code.Length]; for (int i = 0; i < f.Code.Length; i++) { bytecodeOffsets[i] = data.Buffer.Position - 12; AddReferencesOffset(functionReferences, f.Code[i].functionReferences, data.Buffer.Position); if (f.Code[i].variableReferences.Count == 0 || f.Code[i].variableReferences[0].Item1.Name != "arguments") { variableReferences.Add(new Tuple <ReferenceSignature, uint>(new ReferenceSignature { Name = "arguments", InstanceType = InstanceType.Local, Instance = f.Code[i].Name, VariableType = VariableType.Normal, VariableIndex = -1 }, 0xFFFFFFFF)); } AddReferencesOffset(variableReferences, f.Code[i].variableReferences, data.Buffer.Position); WriteCodeBlock(data, f.Code[i].InstructionsCopy, f.General.BytecodeVersion); } } for (int i = 0; i < datas.Length; i++) { if (f.General.BytecodeVersion > 0xE) { datas[i].Buffer.Position = (int)Marshal.OffsetOf(typeof(CodeEntryF), "BytecodeOffset"); datas[i].Buffer.Write(bytecodeOffsets[i] - data.Buffer.Position); } else { AddReferencesOffset(functionReferences, f.Code[i].functionReferences, data.Buffer.Position); if (f.Code[i].variableReferences.Count == 0 || f.Code[i].variableReferences[0].Item1.Name != "arguments") { variableReferences.Add(new Tuple <ReferenceSignature, uint>(new ReferenceSignature { Name = "arguments", InstanceType = InstanceType.Local, Instance = f.Code[i].Name, VariableType = VariableType.Normal, VariableIndex = -1 }, 0xFFFFFFFF)); } AddReferencesOffset(variableReferences, f.Code[i].variableReferences, data.Buffer.Position); } SectionWriter.Write(data.Buffer, datas[i]); allOffs.AddRange(datas[i].OffsetOffsets); // updated by Write } IList <ReferenceDef> functionStartOffsetsAndCounts; ResolveReferenceOffsets(data, functionReferences, strings, false, out functionStartOffsetsAndCounts); IList <ReferenceDef> variableStartOffsetsAndCounts; ResolveReferenceOffsets(data, variableReferences, strings, true, out variableStartOffsetsAndCounts); if (f.RefData.Variables == null || f.RefData.Variables.Length == 0) { Console.Error.WriteLine("Warning: Variable definitions not pre-loaded. Linking may be inaccurate or lose information."); } else { // I tried my best at guessing what these should be, but it wasn't enough. // I suspect it may have to do with variable type, since getting // one wrong resulted in "tried to index something that isn't an // array" (or something to that effect). for (int i = 0; i < variableStartOffsetsAndCounts.Count; i++) { var v = variableStartOffsetsAndCounts[i]; if (i < f.RefData.Variables.Length && v.Name == f.RefData.Variables[i].Name) // && //(v.InstanceType == f.RefData.Variables[i].InstanceType || v.InstanceType >= InstanceType.StackTopOrGlobal)) { v.unknown2 = f.RefData.Variables[i].unknown2; v.InstanceType = f.RefData.Variables[i].InstanceType; variableStartOffsetsAndCounts[i] = v; } } } f.RefData = new RefData { Functions = functionStartOffsetsAndCounts.ToArray(), Variables = variableStartOffsetsAndCounts.ToArray() }; data.OffsetOffsets = allOffs.ToArray(); return(stringOffsetOffsets); }
public static byte[] WriteFile(string baseDir, JsonData projFile, GMFile f) { Console.WriteLine($"Preparing strings..."); var stringsChunkBuilder = new StringsChunkBuilder(); stringsChunkBuilder.AddStrings(f.Strings); var texpChunk = new BBData(new BinBuffer(), new int[0]); Console.WriteLine($"Preparing textures..."); int[] texPagOffsets = SectionWriter.WriteTexturePages(texpChunk, f.TexturePages); var codeChunk = new BBData(new BinBuffer(), new int[0]); Console.WriteLine($"Preparing code..."); var codeChunkStringOffsetOffsets = Assembler.WriteCodes(codeChunk, f, stringsChunkBuilder); var offsets = new int[0]; BBData writer = new BBData(new BinBuffer(), offsets); writer.Buffer.Write(SectionHeaders.Form); writer.Buffer.Write(0); var stringOffsetOffsets = new List <int>(); int stringsDataPosition = 0; var texpOffsetOffsets = new List <int>(); int texpChunkPosition = 0; var codeOffsetOffsets = new List <int>(); int codeChunkPosition = 0; foreach (SectionHeaders chunkId in f.ChunkOrder) { Console.WriteLine($"Writing {chunkId}..."); BBData chunk = new BBData(new BinBuffer(), new int[0]); int[] chunkStringOffsetOffsets = null; int[] chunkTexpOffsetOffsets = null; int[] chunkCodeOffsetOffsets = null; switch (chunkId) { case SectionHeaders.General: chunkStringOffsetOffsets = SectionWriter.WriteGeneral(chunk, f.General, f.Rooms, stringsChunkBuilder); break; case SectionHeaders.Options: chunkStringOffsetOffsets = SectionWriter.WriteOptions(chunk, f.Options, stringsChunkBuilder); break; case SectionHeaders.Sounds: chunkStringOffsetOffsets = SectionWriter.WriteSounds(chunk, f.Sound, stringsChunkBuilder, f.AudioGroups); break; case SectionHeaders.AudioGroup: chunkStringOffsetOffsets = SectionWriter.WriteAudioGroups(chunk, f.AudioGroups, stringsChunkBuilder); break; case SectionHeaders.Sprites: SectionWriter.WriteSprites(chunk, f.Sprites, stringsChunkBuilder, texPagOffsets, out chunkStringOffsetOffsets, out chunkTexpOffsetOffsets); break; case SectionHeaders.Backgrounds: SectionWriter.WriteBackgrounds(chunk, f.Backgrounds, stringsChunkBuilder, texPagOffsets, out chunkStringOffsetOffsets, out chunkTexpOffsetOffsets); break; case SectionHeaders.Paths: chunkStringOffsetOffsets = SectionWriter.WritePaths(chunk, f.Paths, stringsChunkBuilder); break; case SectionHeaders.Scripts: chunkStringOffsetOffsets = SectionWriter.WriteScripts(chunk, f.Scripts, stringsChunkBuilder); break; case SectionHeaders.Fonts: SectionWriter.WriteFonts(chunk, f.Fonts, stringsChunkBuilder, texPagOffsets, out chunkStringOffsetOffsets, out chunkTexpOffsetOffsets); break; case SectionHeaders.Objects: chunkStringOffsetOffsets = SectionWriter.WriteObjects(chunk, f.Objects, stringsChunkBuilder); break; case SectionHeaders.Rooms: chunkStringOffsetOffsets = SectionWriter.WriteRooms(chunk, f.Rooms, stringsChunkBuilder); break; case SectionHeaders.TexturePage: chunk = texpChunk; texpChunkPosition = writer.Buffer.Position + 8; break; case SectionHeaders.Code: chunk = codeChunk; chunkStringOffsetOffsets = codeChunkStringOffsetOffsets; codeChunkPosition = writer.Buffer.Position + 8; break; case SectionHeaders.Variables: if (f.VariableExtra != null) { foreach (var e in f.VariableExtra) { chunk.Buffer.Write(e); } } SectionWriter.WriteRefDefs(chunk, f.RefData.Variables, stringsChunkBuilder, f.General.IsOldBCVersion, false, out chunkStringOffsetOffsets, out chunkCodeOffsetOffsets); break; case SectionHeaders.Functions: SectionWriter.WriteRefDefs(chunk, f.RefData.Functions, stringsChunkBuilder, f.General.IsOldBCVersion, true, out chunkStringOffsetOffsets, out chunkCodeOffsetOffsets); chunkStringOffsetOffsets = chunkStringOffsetOffsets.Concat(SectionWriter.WriteFunctionLocals(chunk, f.FunctionLocals, stringsChunkBuilder)).ToArray(); break; case SectionHeaders.Strings: var stringOffsets = stringsChunkBuilder.WriteStringsChunk(chunk); stringsDataPosition = writer.Buffer.Position + stringOffsets[0] + 12; // for Textures chunk up next SectionWriter.Pad(chunk, 0x80, writer.Buffer.Position + 8); break; case SectionHeaders.Textures: SectionWriter.WriteTextures(chunk, f.Textures); break; case SectionHeaders.Audio: SectionWriter.WriteAudio(chunk, f.Audio, writer.Buffer.Position); break; case SectionHeaders.Shaders: chunkStringOffsetOffsets = SectionWriter.WriteShaders(chunk, f.Shaders, stringsChunkBuilder); break; default: var chunkName = chunkId.ToChunkName(); Console.Error.WriteLine($"Note: Don't know how to handle {chunkName}"); string chunkFile = null; if (projFile.Has("chunks") && projFile["chunks"].IsArray) { foreach (JsonData jd in projFile["chunks"]) { if (jd.IsString) { if (Path.GetFileNameWithoutExtension((string)jd) == chunkName) { chunkFile = (string)jd; break; } } } } BinBuffer chunkData; if (chunkFile == null) { Console.Error.WriteLine($"Note: Chunk {chunkName} was not dumped, assuming it's empty"); chunkData = new BinBuffer(); } else { Console.Error.WriteLine($"Note: Loading {chunkName} from dump"); try { chunkData = new BinBuffer(File.ReadAllBytes(Path.Combine(baseDir, chunkFile))); } catch (Exception e) { Console.Error.WriteLine($"Error loading {chunkName}, using empty"); Console.Error.WriteLine(e); chunkData = new BinBuffer(); } } chunk = new BBData(chunkData, new int[0]); break; } if (chunkStringOffsetOffsets != null) { foreach (var offset in chunkStringOffsetOffsets) { stringOffsetOffsets.Add(offset + writer.Buffer.Position); } } chunkStringOffsetOffsets = null; if (chunkTexpOffsetOffsets != null) { foreach (var offset in chunkTexpOffsetOffsets) { texpOffsetOffsets.Add(offset + writer.Buffer.Position); } } chunkTexpOffsetOffsets = null; if (chunkCodeOffsetOffsets != null) { foreach (var offset in chunkCodeOffsetOffsets) { codeOffsetOffsets.Add(offset + writer.Buffer.Position); } } chunkCodeOffsetOffsets = null; SectionWriter.WriteChunk(writer, chunkId, chunk); } writer.Buffer.Position = 4; writer.Buffer.Write(writer.Buffer.Size - 8); writer.Buffer.Position = writer.Buffer.Size; foreach (var stringOffset in stringOffsetOffsets) { writer.Buffer.Position = stringOffset; var o = writer.Buffer.ReadInt32(); //bb.Position -= sizeof(int); writer.Buffer.Write(o + stringsDataPosition); } foreach (var texpOffset in texpOffsetOffsets) { writer.Buffer.Position = texpOffset; var o = writer.Buffer.ReadInt32(); //bb.Position -= sizeof(int); writer.Buffer.Write(o + texpChunkPosition); } foreach (var codeOffset in codeOffsetOffsets) { writer.Buffer.Position = codeOffset; var o = writer.Buffer.ReadInt32(); //bb.Position -= sizeof(int); writer.Buffer.Write(o + codeChunkPosition); } writer.Buffer.Position = 0; return(writer.Buffer.ReadBytes(writer.Buffer.Size)); }