Files
crosslangdevstudio/CrossLangFile.cs
2025-08-31 00:25:32 -05:00

407 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace CrossLangDevStudio;
public class CrossLangFile
{
const string CROSSVM = "TCROSSVM";
public void Load(string path)
{
if (File.Exists(path))
using (var strm = File.OpenRead(path))
Load(strm);
}
public void Load(Stream strm)
{
byte[] header = new byte[18];
strm.ReadExactly(header);
for (int i = 0; i < CROSSVM.Length; i++)
{
if (header[i] != (byte)CROSSVM[i]) throw new Exception("Invalid signature");
}
Version = new CrossLangVersion(header, 13);
uint ReadInt()
{
strm.ReadExactly(header, 0, 4);
if (BitConverter.IsLittleEndian)
Array.Reverse(header, 0, 4);
return BitConverter.ToUInt32(header, 0);
}
void ReadString()
{
uint length = ReadInt();
byte[] data = new byte[length];
strm.ReadExactly(data, 0, data.Length);
Strings.Add(Encoding.UTF8.GetString(data));
}
string ReadSectionName()
{
strm.ReadExactly(header, 0, 4);
return Encoding.UTF8.GetString(header, 0, 4);
}
string GetString()
{
uint index = ReadInt();
return Strings[(int)index];
}
uint count = ReadInt();
for (uint i = 0; i < count; i++)
{
string section = ReadSectionName();
uint sectionSize = ReadInt();
switch (section)
{
case "STRS":
{
uint theCount = ReadInt();
for (uint j = 0; j < theCount; j++)
{
ReadString();
}
}
break;
case "NAME":
Name = GetString();
break;
case "INFO":
Info = GetString();
break;
case "ICON":
Icon = (int)ReadInt();
break;
case "RESO":
byte[] data = new byte[sectionSize];
strm.ReadExactly(data, 0, data.Length);
Resources.Add(data);
break;
case "DEPS":
if (sectionSize == 9)
{
string name = GetString();
strm.ReadExactly(header, 0, 5);
var version = new CrossLangVersion(header);
Dependencies.Add(new CrossLangDependency() { Name = name, Version = version });
}
break;
default:
strm.Position += sectionSize;
break;
}
}
}
public string Name { get; private set; } = "";
public CrossLangVersion Version { get; private set; }
public string Info { get; private set; } = "";
public int Icon { get; private set; } = -1;
public CrossLangInfo InfoObject => JsonConvert.DeserializeObject<CrossLangInfo>(Info) ?? new CrossLangInfo();
public Bitmap GetIcon(int height=128)
{
using (Stream strm = (Icon > -1 && Icon < Resources.Count) ? new MemoryStream(Resources[Icon], false) : AssetLoader.Open(new Uri("avares://CrossLangDevStudio/Assets/crosslang.ico")))
{
return Bitmap.DecodeToHeight(strm, height);
}
}
internal string GetTemplatePrettyName()
{
var info = InfoObject;
if (info is not null)
{
if (!string.IsNullOrWhiteSpace(info.TemplateNamePretty)) return info.TemplateNamePretty;
if (!string.IsNullOrWhiteSpace(info.TemplateName)) return info.TemplateName;
}
return this.Name;
}
public List<string> Strings { get; } = new List<string>();
public List<byte[]> Resources { get; } = new List<byte[]>();
public List<CrossLangDependency> Dependencies { get; } = new List<CrossLangDependency>();
}
public class CrossLangConfig
{
[JsonProperty("name")]
public string? Name { get; set; }
[JsonProperty("version")]
public CrossLangVersion Version { get; set; }
[JsonProperty("dependencies")]
public List<CrossLangDependency> Dependencies { get; set; } = new List<CrossLangDependency>();
[JsonProperty("info")]
public CrossLangInfo Info { get; set; } = new CrossLangInfo();
[JsonProperty("project_dependencies")]
public List<string> ProjectDependencies { get; set; } = new List<string>();
[JsonProperty("icon")]
public string? Icon { get; set; }
}
public class CrossLangInfo
{
[JsonProperty("maintainer")]
public string? Maintainer { get; set; } //Not checked when building project
[JsonProperty("type")]
public string? Type { get; set; } //gets checked whether it is compile_tool by build tool, possible values are in file: project_types.txt
[JsonProperty("repo")]
public string? Repoository { get; set; } //optional, is the place where the code is stored
[JsonProperty("homepage")]
public string? HomePage { get; set; } //optional, is the website for the project
[JsonProperty("license")]
public string? License { get; set; } //optional, but recommended to tell people what the license is
[JsonProperty("description")]
public string? Description { get; set; } //optional but tells people about the package
[JsonProperty("template_name")]
public string? TemplateName { get; set; }
[JsonProperty("template_name_pretty")]
public string? TemplateNamePretty { get; set; }
[JsonProperty("template_project_dependencies")]
public JToken? TemplateDependencies { get; set; }
[JsonProperty("template_info")]
public JToken? TemplateInfo { get; set; }
[JsonProperty("template_extra_text_ftles")]
public JToken? TemplateExtraTextFiles { get; set; }
[JsonProperty("template_ignored_files")]
public JToken? TemplateIgnoredFiles { get; set; }
}
public class CrossLangDependency
{
[JsonProperty("name")]
public string Name { get; set; } = "";
[JsonProperty("verson")]
public CrossLangVersion Version { get; set; }
}
public enum CrossLangVersionStage
{
Development = 0,
Alpha = 1,
Beta = 2,
Production = 3
}
public class CrossLangVersionConverter : Newtonsoft.Json.JsonConverter<CrossLangVersion>
{
public override CrossLangVersion ReadJson(JsonReader reader, Type objectType, CrossLangVersion existingValue, bool hasExistingValue, JsonSerializer serializer)
{
string? s = reader.Value as string;
if (!string.IsNullOrWhiteSpace(s))
{
if (CrossLangVersion.TryParse(s, out var version)) return version;
}
if (hasExistingValue)
return existingValue;
return new CrossLangVersion();
}
public override void WriteJson(JsonWriter writer, CrossLangVersion value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
}
[Newtonsoft.Json.JsonConverter( typeof(CrossLangVersionConverter))]
public struct CrossLangVersion : IComparable<CrossLangVersion>, IComparable
{
public void CopyTo(byte[] data, int offset = 0)
{
if (offset + 5 > data.Length) throw new IndexOutOfRangeException("Cannot write outside of array");
data[offset + 0] = Major;
data[offset + 1] = Minor;
data[offset + 2] = Patch;
data[offset + 3] = (byte)(BuildAndStage >> 8);
data[offset + 4] = (byte)(BuildAndStage & 0xFF);
}
public byte[] ToArray()
{
byte[] data = new byte[5];
CopyTo(data);
return data;
}
public static bool TryParse(string str, out CrossLangVersion version)
{
var dashPart = str.Split(new char[] { '-' }, 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
byte major = 1;
byte minor = 0;
byte patch = 0;
ushort build = 0;
CrossLangVersionStage stage = CrossLangVersionStage.Development;
if (dashPart.Length >= 1)
{
var dotPart = dashPart[0].Split(new char[] { '.' }, 4, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
if (dotPart.Length > 0)
{
if (!byte.TryParse(dotPart[0], out major))
{
version = new CrossLangVersion();
return false;
}
if (dotPart.Length > 1 && !byte.TryParse(dotPart[1], out minor))
{
version = new CrossLangVersion();
return false;
}
if (dotPart.Length > 2 && !byte.TryParse(dotPart[2], out patch))
{
version = new CrossLangVersion();
return false;
}
if (dotPart.Length > 3 && !ushort.TryParse(dotPart[3], out build))
{
version = new CrossLangVersion();
return false;
}
if (dashPart.Length == 2)
{
switch (dashPart[1])
{
case "dev":
stage = CrossLangVersionStage.Development;
break;
case "alpha":
stage = CrossLangVersionStage.Alpha;
break;
case "beta":
stage = CrossLangVersionStage.Beta;
break;
case "prod":
stage = CrossLangVersionStage.Production;
break;
default:
version = new CrossLangVersion();
return false;
}
}
version = new CrossLangVersion(major, minor, patch, build, stage);
return true;
}
}
version = new CrossLangVersion();
return false;
}
public CrossLangVersion()
{
}
public CrossLangVersion(byte major, byte minor, byte patch, ushort build, CrossLangVersionStage stage)
{
Major = major;
Minor = minor;
Patch = patch;
Build = build;
Stage = stage;
}
public CrossLangVersion(byte[] data, int offset = 0)
{
if (offset + 5 > data.Length) return;
Major = data[offset+0];
Minor = data[offset+1];
Patch = data[offset+2];
ushort build = (ushort)(data[offset+3] << 8 | data[offset+4]);
Build = (ushort)(build >> 2);
Stage = (CrossLangVersionStage)(build & 3);
}
public byte Major { get; set; } = 1;
public byte Minor { get; set; } = 0;
public byte Patch { get; set; } = 0;
public ushort Build { get; set; } = 0;
public static CrossLangVersion OnePointZeroProd => new CrossLangVersion(){Major=1,Minor=0,Patch=0,Build=0,Stage= CrossLangVersionStage.Production};
public CrossLangVersionStage Stage { get; set; } = CrossLangVersionStage.Development;
public ushort BuildAndStage => (ushort)(Build << 2 | (ushort)Stage);
public ulong AsInteger => (ulong)((ulong)Major << 32 | (ulong)Minor << 24 | (ulong)Patch << 16 | (ulong)BuildAndStage);
public int CompareTo(CrossLangVersion other)
{
return AsInteger.CompareTo(other.AsInteger);
}
public int CompareTo(object? obj)
{
if (obj is CrossLangVersion oth)
return CompareTo(oth);
return 1;
}
public override string ToString()
{
string stage="dev";
switch (Stage)
{
case CrossLangVersionStage.Development:
stage = "dev";
break;
case CrossLangVersionStage.Alpha:
stage = "alpha";
break;
case CrossLangVersionStage.Beta:
stage = "beta";
break;
case CrossLangVersionStage.Production:
stage = "prod";
break;
}
return $"{Major}.{Minor}.{Patch}.{Build}-{stage}";
}
}