Skip to content

Commit

Permalink
Merge pull request #17 from rafl/registry
Browse files Browse the repository at this point in the history
Add a simple module registry
  • Loading branch information
pjf committed Sep 24, 2014
2 parents a748ac9 + 6e606d3 commit 8768d18
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 188 deletions.
6 changes: 5 additions & 1 deletion CKAN/CKAN/CKAN.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
<Compile Include="Module.cs" />
<Compile Include="ModuleDict.cs" />
<Compile Include="KSP.cs" />
<Compile Include="InstalledModule.cs" />
<Compile Include="RegistryManager.cs" />
<Compile Include="Registry.cs" />
<Compile Include="ModuleInstaller.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
</Project>
24 changes: 24 additions & 0 deletions CKAN/CKAN/InstalledModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;

namespace CKAN
{
public class InstalledModuleFile
{
public string name;
public string sha1_sum;
}

public class InstalledModule
{
public InstalledModuleFile[] installed_files;
public Module source_module;
public DateTime install_time;

public InstalledModule (InstalledModuleFile[] installed_files, Module source_module, DateTime install_time)
{
this.installed_files = installed_files;
this.source_module = source_module;
this.install_time = install_time;
}
}
}
205 changes: 25 additions & 180 deletions CKAN/CKAN/Module.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
using System;
using System.IO;
using System.Net;
using System.Linq;

using Newtonsoft.Json;

using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;

using System.Text.RegularExpressions;

/// <summary>
/// Describes a CKAN module (ie, what's in the CKAN.schema file).
///
Expand All @@ -35,226 +26,80 @@ namespace CKAN {
public class Module {

[JsonProperty("name", Required = Required.Always)]
public string _name;
public string name;

[JsonProperty("identifier", Required = Required.Always)]
public string _identifier; // TODO: Strong type
public string identifier; // TODO: Strong type

// TODO: Change spec: abstract -> description
[JsonProperty("abstract", Required = Required.Always)]
public string _abstract;
public string @abstract;

[JsonProperty("comment")]
public string _comment;
public string comment;

[JsonProperty("author")]
public string[] _author;
public string[] author;

[JsonProperty("download", Required = Required.Always)]
public Uri _download;
public Uri download;

[JsonProperty("license", Required= Required.Always)]
public dynamic _license; // TODO: Strong type
public dynamic license; // TODO: Strong type

[JsonProperty("version", Required = Required.Always)]
public string _version; // TODO: Strong type
public string version; // TODO: Strong type

[JsonProperty("release_status")]
public string _release_status; // TODO: Strong type
public string release_status; // TODO: Strong type

[JsonProperty("min_ksp")]
public string _min_ksp; // TODO: Type
public string min_ksp; // TODO: Type

[JsonProperty("max_ksp")]
public string _max_ksp; // TODO: Type
public string max_ksp; // TODO: Type

[JsonProperty("requires")]
public dynamic[] _requires;
public dynamic[] requires;

[JsonProperty("recommends")]
public dynamic[] _recommends;
public dynamic[] recommends;

[JsonProperty("conflicts")]
public dynamic[] _conflicts;
public dynamic[] conflicts;

[JsonProperty("resourcs")]
public dynamic[] _resources;
public dynamic[] resources;

[JsonProperty("install", Required = Required.Always)]
public dynamic[] _install;
public dynamic[] install;

[JsonProperty("bundles")]
public dynamic[] _bundles;

// Private record of which file we came from.
string origCkanFile;
public dynamic[] bundles;

/// <summary> Generates a CKAN.Meta object given a filename</summary>
public static Module from_file(string filename) {
string json = System.IO.File.ReadAllText (filename);

Module built = Module.from_string (json);

// Attach which file this came from.
built.origCkanFile = filename;

return built;
return Module.from_string (json);
}

/// <summary> Generates a CKAN.META object from a string </summary>
public static Module from_string(string json) {
return JsonConvert.DeserializeObject<Module> (json);
}

/// <summary>
/// Download the given mod. Returns the filename it was saved to.
///
/// If no filename is provided, the standard_name() will be used.
///
/// </summary>
/// <param name="filename">Filename.</param>
public string download(string filename = null) {

// Generate a temporary file if none is provided.
if (filename == null) {
filename = standard_name();
}

Console.WriteLine (" * Downloading " + filename + "...");

WebClient agent = new WebClient ();
agent.DownloadFile (_download, filename);

return filename;
}

/// <summary>
/// Returns a standardised name for this module, in the form
/// "identifier-version.zip". For example, `RealSolarSystem-7.3.zip`
/// </summary>
public string standard_name() {
return _identifier + "-" + _version + ".zip";
}

/// <summary>
/// Install our mod from the filename supplied.
/// If no file is supplied, we will fetch() it first.
/// </summary>

public void install(string filename = null) {

Console.WriteLine (_identifier + ":\n");

// Fetch our file if we don't already have it.
if (filename == null) {
filename = download ();
}

// Open our zip file for processing
ZipFile zipfile = new ZipFile (File.OpenRead (filename));

// Walk through our install instructions.
foreach (dynamic stanza in _install) {
install_component (stanza, zipfile);

// TODO: We should just *serialise* our current state, not
// copy the original file, because we can't always guarantee
// there will be an original file.

// TODO: This will just throw them in GameData.
// We need a way to convert stanzas to install locations.
// We really should make Stanza its own class.

File.Copy (origCkanFile, KSP.gameData() + "/" + _identifier + ".ckan", true);
}

// Finish now if we have no bundled mods.
if (_bundles == null) { return; }

// Do the same with our bundled mods.
foreach (dynamic stanza in _bundles) {

// TODO: Check versions, so we don't double install.

install_component (stanza, zipfile);

// TODO: Generate CKAN metadata for the bundled component.
}

return;

}

void install_component(dynamic stanza, ZipFile zipfile) {
string fileToInstall = stanza.file;

Console.WriteLine (" * Installing " + fileToInstall);

string[] path = fileToInstall.Split('/');

// TODO: This will depend upon the `install_to` in the JSON file
string installDir = KSP.gameData ();

// This is what we strip off paths
string stripDir = String.Join("/", path.Take(path.Count() - 1)) + "/";

// Console.WriteLine("InstallDir is "+installDir);
// Console.WriteLine ("StripDir is " + stripDir);

// This is awful. There's got to be a better way to extract a tree?
string filter = "^" + stanza.file + "(/|$)";

// O(N^2) solution. Surely there's a better way...
foreach (ZipEntry entry in zipfile) {

// Skip things we don't want.
if (! Regex.IsMatch (entry.Name, filter)) {
continue;
}

// Get the full name of the file.
string outputName = entry.Name;

// Strip off the prefix (often GameData/)
// TODO: The C# equivalent of "\Q stripDir \E" so we can't be caught by metacharacters.
outputName = Regex.Replace (outputName, @"^" + stripDir, "");

// Aww hell yes, let's write this file out!

string fullPath = Path.Combine (installDir, outputName);
// Console.WriteLine (fullPath);

copyZipEntry (zipfile, entry, fullPath);
}

return;
public string standard_name ()
{
return identifier + "-" + version + ".zip";
}

// TODO: Test that this actually throws exceptions if it can't do its job.
void copyZipEntry(ZipFile zipfile, ZipEntry entry, string fullPath) {

if (entry.IsDirectory) {
// Console.WriteLine ("Making directory " + fullPath);
Directory.CreateDirectory (fullPath);
}
else {
// Console.WriteLine ("Writing file " + fullPath);

// It's a file! Prepare the streams
Stream zipStream = zipfile.GetInputStream(entry);
FileStream output = File.Create (fullPath);

// Copy
zipStream.CopyTo (output);

// Tidy up.
zipStream.Close();
output.Close();
}

return;

public string serialise ()
{
return JsonConvert.SerializeObject (this);
}
}
}

}
8 changes: 4 additions & 4 deletions CKAN/CKAN/ModuleDict.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public ModuleDict () {
string module = Regex.Replace (file, "^.*/([^.]+).*", "$1");

this [module] = new Module ();
this [module]._version = "0"; // We can say it exists, but have no idea of other info.
this [module]._identifier = module;
this [module].version = "0"; // We can say it exists, but have no idea of other info.
this [module].identifier = module;

// Console.WriteLine (this[module]._identifier + " ( " + file + " ) ");
}
Expand All @@ -59,7 +59,7 @@ public ModuleDict () {
foreach (string file in ckanFiles) {
Module module = Module.from_file (file);

this [module._identifier] = module;
this [module.identifier] = module;

// Console.WriteLine (module._identifier + " " + module._version + " ( " + file + " ) ");

Expand All @@ -69,7 +69,7 @@ public ModuleDict () {

public void showInstalled() {
foreach(KeyValuePair<string, Module> entry in this) {
Console.WriteLine (entry.Value._identifier + " " + entry.Value._version);
Console.WriteLine (entry.Value.identifier + " " + entry.Value.version);
}

return;
Expand Down
Loading

0 comments on commit 8768d18

Please sign in to comment.