Skip to content

Commit

Permalink
Allow installation into Ships/Script
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Nov 5, 2020
1 parent 7b9da0c commit dbfe567
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 21 deletions.
2 changes: 1 addition & 1 deletion CKAN.schema
Expand Up @@ -411,7 +411,7 @@
{
"description" : "Spec v1.12 Ships subfolder paths",
"type" : "string",
"pattern" : "^Ships/(SPH|VAB)$"
"pattern" : "^Ships/(SPH|VAB|Script)$"
},
{
"description" : "Spec v1.16 thumbs paths",
Expand Down
7 changes: 7 additions & 0 deletions Core/KSP.cs
Expand Up @@ -388,6 +388,13 @@ public string ShipsThumbsVAB()
Path.Combine(ShipsThumbs(), "VAB")
);
}

public string ShipsScript()
{
return KSPPathUtils.NormalizePath(
Path.Combine(Ships(), "Script")
);
}

public string Tutorial()
{
Expand Down
10 changes: 6 additions & 4 deletions Core/KSPPathUtils.cs
Expand Up @@ -168,13 +168,15 @@ public static string KSPSteamPath()
}

/// <summary>
/// Normalizes the path by replace any \ with / and removing any trailing slash.
/// Normalizes the path by replacing all \ with / and removing any trailing slash.
/// </summary>
/// <returns>The normalized path.</returns>
/// <param name="path">The path to normalize.</param>
/// <param name="path">The path to normalize</param>
/// <returns>The normalized path</returns>
public static string NormalizePath(string path)
{
return path?.Replace('\\', '/').TrimEnd('/');
return path == null ? null
: path.Length < 2 ? path.Replace('\\', '/')
: path.Replace('\\', '/').TrimEnd('/');
}

/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions Core/ModuleInstaller.cs
Expand Up @@ -511,7 +511,8 @@ private bool IsReservedDirectory(string path)
|| path == ksp.Scenarios() || path == ksp.GameData()
|| path == ksp.GameDir() || path == ksp.CkanDir()
|| path == ksp.ShipsThumbs() || path == ksp.ShipsThumbsVAB()
|| path == ksp.ShipsThumbsSPH() || path == ksp.Missions();
|| path == ksp.ShipsThumbsSPH() || path == ksp.ShipsScript()
|| path == ksp.Missions();
}

/// <summary>
Expand Down Expand Up @@ -881,7 +882,8 @@ public HashSet<string> AddParentDirectories(HashSet<string> directories)
var results = new HashSet<string>();
// adding in the DirectorySeparatorChar fixes attempts on Windows
// to parse "X:" which resolves to Environment.CurrentDirectory
var dirInfo = new DirectoryInfo(dir + Path.DirectorySeparatorChar);
var dirInfo = new DirectoryInfo(
dir.EndsWith("/") ? dir : dir + Path.DirectorySeparatorChar);
// if this is a parentless directory (Windows)
// or if the Root equals the current directory (Mono)
Expand Down
29 changes: 18 additions & 11 deletions Core/Types/ModuleInstallDescriptor.cs
Expand Up @@ -333,7 +333,6 @@ private bool IsWanted(string path, int? matchWhere)
public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, KSP ksp)
{
string installDir;
bool makeDirs;
var files = new List<InstallableFile>();

// Normalize the path before doing everything else
Expand All @@ -351,13 +350,9 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, KSP ksp)

// Add the extracted subdirectory to the path of KSP's GameData
installDir = ksp == null ? null : (KSPPathUtils.NormalizePath(ksp.GameData() + "/" + subDir));
makeDirs = true;
}
else if (install_to.StartsWith("Ships"))
{
// Don't allow directory creation in ships directory
makeDirs = false;

switch (install_to)
{
case "Ships":
Expand All @@ -378,6 +373,9 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, KSP ksp)
case "Ships/@thumbs/SPH":
installDir = ksp?.ShipsThumbsSPH();
break;
case "Ships/Script":
installDir = ksp?.ShipsScript();
break;
default:
throw new BadInstallLocationKraken("Unknown install_to " + install_to);
}
Expand All @@ -388,22 +386,18 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, KSP ksp)
{
case "Tutorial":
installDir = ksp?.Tutorial();
makeDirs = true;
break;

case "Scenarios":
installDir = ksp?.Scenarios();
makeDirs = true;
break;

case "Missions":
installDir = ksp?.Missions();
makeDirs = true;
break;

case "GameRoot":
installDir = ksp?.GameDir();
makeDirs = false;
break;

default:
Expand Down Expand Up @@ -435,7 +429,7 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, KSP ksp)
InstallableFile file_info = new InstallableFile
{
source = entry,
makedir = makeDirs,
makedir = false,
destination = null
};

Expand All @@ -446,6 +440,9 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, KSP ksp)
// Update our file info with the install location
file_info.destination = TransformOutputName(
entry.Name, installDir, @as);
file_info.makedir = AllowDirectoryCreation(
ksp?.ToRelativeGameDir(file_info.destination)
?? file_info.destination);
}

files.Add(file_info);
Expand All @@ -461,6 +458,16 @@ public List<InstallableFile> FindInstallableFiles(ZipFile zipfile, KSP ksp)
return files;
}

private static string[] CreateableDirs = {
"GameData", "Tutorial", "Scenarios", "Missions", "Ships/Script"
};

private bool AllowDirectoryCreation(string relativePath)
{
return CreateableDirs.Any(dir =>
relativePath == dir || relativePath.StartsWith($"{dir}/"));
}

/// <summary>
/// Transforms the name of the output. This will strip the leading directories from the stanza file from
/// output name and then combine it with the installDir.
Expand Down Expand Up @@ -506,7 +513,7 @@ internal string TransformOutputName(string outputName, string installDir, string
else
{
var reservedPrefix = ReservedPaths.FirstOrDefault(prefix =>
outputName.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase));
outputName.StartsWith(prefix + "/", StringComparison.InvariantCultureIgnoreCase));
if (reservedPrefix != null)
{
// If we try to install a folder with the same name as
Expand Down
5 changes: 3 additions & 2 deletions Netkan/Services/ModuleService.cs
Expand Up @@ -82,10 +82,11 @@ public IEnumerable<InstallableFile> GetPlugins(CkanModule module, ZipFile zip)

public IEnumerable<string> FileDestinations(CkanModule module, string filePath)
{
var ksp = new KSP("/", "dummy", null, false);
return ModuleInstaller
.FindInstallableFiles(module, filePath, new KSP("/", "dummy", null, false))
.FindInstallableFiles(module, filePath, ksp)
.Where(f => !f.source.IsDirectory)
.Select(f => f.destination);
.Select(f => ksp.ToRelativeGameDir(f.destination));
}

/// <summary>
Expand Down
5 changes: 5 additions & 0 deletions Netkan/Validators/InstallValidator.cs
Expand Up @@ -18,6 +18,10 @@ public void Validate(Metadata metadata)
{
throw new Kraken("spec_version v1.2+ required for GameData with path");
}
if (metadata.SpecVersion < v1p29 && install_to.StartsWith("Ships/Script"))
{
throw new Kraken("spec_version v1.29+ required to install to Ships/Script");
}
if (metadata.SpecVersion < v1p12 && install_to.StartsWith("Ships/"))
{
throw new Kraken("spec_version v1.12+ required to install to Ships/ with path");
Expand Down Expand Up @@ -65,6 +69,7 @@ public void Validate(Metadata metadata)
private static readonly ModuleVersion v1p12 = new ModuleVersion("v1.12");
private static readonly ModuleVersion v1p16 = new ModuleVersion("v1.16");
private static readonly ModuleVersion v1p18 = new ModuleVersion("v1.18");
private static readonly ModuleVersion v1p29 = new ModuleVersion("v1.29");
private static readonly string[] pathProperties = {"find", "file", "install_to"};
}
}
2 changes: 1 addition & 1 deletion Spec.md
Expand Up @@ -293,7 +293,7 @@ three source directives:
In addition a destination directive *must* be provided:

- `install_to`: The target location where the matched file or directory should be installed.
- Valid values for this entry are `GameData`, `Missions`(**v1.25**), `Ships`, `Ships/SPH`(**v1.12**), `Ships/VAB`(**v1.12**), `Ships/@thumbs/VAB`(**v1.16**), `Ships/@thumbs/SPH`(**v1.16**), `Tutorial`, `Scenarios` (**v1.14**)
- Valid values for this entry are `GameData`, `Missions`(**v1.25**), `Ships`, `Ships/SPH`(**v1.12**), `Ships/VAB`(**v1.12**), `Ships/@thumbs/VAB`(**v1.16**), `Ships/@thumbs/SPH`(**v1.16**), `Ships/Script`(**v1.29**), `Tutorial`, `Scenarios` (**v1.14**)
and `GameRoot` (which should be used sparingly, if at all).
- A path to a given subfolder location can be specified *only* under `GameData` (**v1.2**);
for example: `GameData/MyMod/Plugins`. The client *must* check this path and abort the install
Expand Down

0 comments on commit dbfe567

Please sign in to comment.