mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
bunch of bug fixes with mods that get updated, prepping for Alpha
This commit is contained in:
parent
dbd1adb719
commit
81a51265b0
31
README.md
31
README.md
@ -69,12 +69,41 @@ Wabbajack can currently install from many different file hosting sources. Curren
|
||||
|
||||
### FAQ
|
||||
|
||||
**Why does each modpack install another copy of Mod Organizer 2**
|
||||
**Why does each modpack install another copy of Mod Organizer 2?**
|
||||
|
||||
Self-contained folders are a cleaner abstraction than dumping tons of modlists into the same set of folders. It's easy to uninstall a modlist (simply delete the folder),
|
||||
and MO2 really isn't designed to support lots of disparate modlists. For example if two modlists both wanted a given texture mod, but different options they would
|
||||
somehow have to keep the names of their mods separate. MO2 isn't that big of an app, so there's really no reason not to install a new copy for each modlist.
|
||||
|
||||
**Why don't I see any mods when I open Mod Organizer 2 after install?**
|
||||
|
||||
Make sure you selected the "Portable" mode when starting MO2 for the first time. In addition, make sure you haven't installed MO2 in a non-portable way on the same box.
|
||||
Really, always use "Portable Mode" it's cleaner and there really isn't a reason not too do so. Make the data self-contained. It's cleaner that way.
|
||||
|
||||
**Will Wabbajack ever support Vortex/other mod managers?**
|
||||
|
||||
I'll be honest, I don't use anything but MO2, so I probably won't write the code. If someone were to write a patch for the functionality
|
||||
I wouldn't throw away the code, but it would have to be done in a way that was relatively seamless for users. Since Wabbajack treats all files in the same way
|
||||
it doesn't know what mod manager a user is using. This means that if the modlist creator used Vortex all users of the modlist would have to use Vortex. This doesn't seem
|
||||
optimal. It's possible perhaps, but it's at the bottom of the priority list.
|
||||
|
||||
**Where is the modlist? Why am I just given an .exe?**
|
||||
|
||||
When Wabbajack creates a modlist, as a final step it copies itself (the wabbajack.exe) and tacks onto the end of the file the modlist data, and a few bits
|
||||
of magic text. When Wabbajack starts it looks at itself to see if it has this extra data tacked on to the end of the executable. If the data is found the app kicks
|
||||
into installation mode. This means that Wabbajack acts a lot like a self-extracting installer.
|
||||
|
||||
**Do you know that some mod authors don't like their mods being automatically installed?**
|
||||
|
||||
Yes, I've heard this, but they chose to host their data on a public site. And no, they don't have the right to dictate what HTTP client is used to download a file.
|
||||
We're using official Nexus APIs for nexus downloads, so any downloads Wabbajack performs are correctly tracked, and MO2 encourages users to endorse mods. It's 2019, we can
|
||||
have better tools than manually clicking links.
|
||||
|
||||
**How does Wabbajack differ from Automaton?**
|
||||
|
||||
I (halgari) used to be a developer working on Automaton. Sadly development was moving a bit too slowly for my liking, and I realized that a complete rewrite would allow the
|
||||
implementation of some really nice features (like BSA packing). As such I made the decision to strike out on my own and make an app that worked first, and then make it pretty.
|
||||
The end result is an app with a ton of features, and a less than professional UI. But that's my motto when coding "make it work, then make it pretty".
|
||||
|
||||
### License & Copyright
|
||||
|
||||
|
@ -26,6 +26,6 @@ namespace Wabbajack.Common
|
||||
}
|
||||
}
|
||||
|
||||
public static String AppName = "Vortex";
|
||||
public static String AppName = "Wabbajack";
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -153,6 +154,9 @@ namespace Wabbajack.Common
|
||||
public class DirectURLArchive : Archive
|
||||
{
|
||||
public string URL;
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public List<string> Headers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -177,6 +181,13 @@ namespace Wabbajack.Common
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archive that comes from MediaFire
|
||||
/// </summary>
|
||||
public class MediaFireArchive : DirectURLArchive
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The indexed contents of an archive
|
||||
/// </summary>
|
||||
|
@ -237,10 +237,14 @@ namespace Wabbajack
|
||||
{
|
||||
installer.Install();
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
LogMsg(ex.StackTrace);
|
||||
LogMsg(ex.InnerException.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMsg(ex.ToString());
|
||||
LogMsg(ex.StackTrace);
|
||||
LogMsg($"{ex.Message} - Can't continue");
|
||||
}
|
||||
});
|
||||
th.Priority = ThreadPriority.BelowNormal;
|
||||
|
@ -179,7 +179,10 @@ namespace Wabbajack
|
||||
foreach (var file in nomatch)
|
||||
Info(" {0}", file.To);
|
||||
if (nomatch.Count() > 0)
|
||||
Error("Exiting due to no way to compile these files");
|
||||
{
|
||||
Info("Exiting due to no way to compile these files");
|
||||
return;
|
||||
}
|
||||
|
||||
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList();
|
||||
|
||||
@ -372,12 +375,28 @@ namespace Wabbajack
|
||||
URL = general.directURL
|
||||
};
|
||||
}
|
||||
else if (general.directURL != null && general.directURL.StartsWith("http://www.mediafire.com/file/"))
|
||||
{
|
||||
Error("Mediafire links are not currently supported");
|
||||
return null;
|
||||
/*result = new MediaFireArchive()
|
||||
{
|
||||
URL = general.directURL
|
||||
};*/
|
||||
}
|
||||
else if (general.directURL != null)
|
||||
{
|
||||
result = new DirectURLArchive()
|
||||
|
||||
var tmp = new DirectURLArchive()
|
||||
{
|
||||
URL = general.directURL
|
||||
};
|
||||
if (general.directURLHeaders != null)
|
||||
{
|
||||
tmp.Headers = new List<string>();
|
||||
tmp.Headers.AddRange(general.directURLHeaders.Split('|'));
|
||||
}
|
||||
result = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -686,6 +705,7 @@ namespace Wabbajack
|
||||
return source => {
|
||||
var result = source.EvolveTo<NoMatch>();
|
||||
result.Reason = "No Match in Stack";
|
||||
Info($"No match for: {source.Path}");
|
||||
return result;
|
||||
};
|
||||
}
|
||||
@ -698,14 +718,31 @@ namespace Wabbajack
|
||||
.GroupBy(e => e.entry.Hash)
|
||||
.ToDictionary(e => e.Key);
|
||||
|
||||
var mod_inis = Directory.EnumerateDirectories(Path.Combine(MO2Folder, "mods"))
|
||||
.Select(f =>
|
||||
{
|
||||
var mod_name = Path.GetFileName(f);
|
||||
var meta_path = Path.Combine(f, "meta.ini");
|
||||
if (File.Exists(meta_path))
|
||||
return (mod_name, meta_path.LoadIniFile());
|
||||
return (null, null);
|
||||
})
|
||||
.Where(f => f.Item2 != null)
|
||||
.ToDictionary(f => f.Item1, f => f.Item2);
|
||||
|
||||
return source =>
|
||||
{
|
||||
if (indexed.TryGetValue(source.Hash, out var found))
|
||||
{
|
||||
var result = source.EvolveTo<FromArchive>();
|
||||
var match = found.FirstOrDefault(f => Path.GetFileName(f.entry.Path) == Path.GetFileName(source.Path));
|
||||
|
||||
var match = found.Where(f => Path.GetFileName(f.entry.Path) == Path.GetFileName(source.Path))
|
||||
.OrderByDescending(f => new FileInfo(f.archive.AbsolutePath).LastWriteTime)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (match == null)
|
||||
match = found.First();
|
||||
match = found.OrderByDescending(f => new FileInfo(f.archive.AbsolutePath).LastWriteTime)
|
||||
.FirstOrDefault();
|
||||
|
||||
result.ArchiveHash = match.archive.Hash;
|
||||
result.From = match.entry.Path;
|
||||
|
@ -241,7 +241,17 @@ namespace Wabbajack
|
||||
{
|
||||
switch (archive) {
|
||||
case NexusMod a:
|
||||
var url = NexusAPI.GetNexusDownloadLink(a as NexusMod, NexusAPIKey);
|
||||
Info($"Downloading Nexus Archive - {archive.Name} - {a.GameName} - {a.ModID} - {a.FileID}");
|
||||
string url;
|
||||
try
|
||||
{
|
||||
url = NexusAPI.GetNexusDownloadLink(a as NexusMod, NexusAPIKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Info($"{a.Name} - Error Getting Nexus Download URL - {ex.Message}");
|
||||
return;
|
||||
}
|
||||
DownloadURLDirect(archive, url);
|
||||
break;
|
||||
case GoogleDriveMod a:
|
||||
@ -250,8 +260,11 @@ namespace Wabbajack
|
||||
case MODDBArchive a:
|
||||
DownloadModDBArchive(archive, (archive as MODDBArchive).URL);
|
||||
break;
|
||||
case MediaFireArchive a:
|
||||
DownloadMediaFireArchive(archive, a.URL);
|
||||
break;
|
||||
case DirectURLArchive a:
|
||||
DownloadURLDirect(archive, (archive as DirectURLArchive).URL);
|
||||
DownloadURLDirect(archive, a.URL, headers:a.Headers);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -260,6 +273,15 @@ namespace Wabbajack
|
||||
});
|
||||
}
|
||||
|
||||
private void DownloadMediaFireArchive(Archive a, string url)
|
||||
{
|
||||
var client = new HttpClient();
|
||||
var result = client.GetStringSync(url);
|
||||
var regex = new Regex("(?<= href =\\\").*\\.mediafire\\.com.*(?=\\\")");
|
||||
var confirm = regex.Match(result);
|
||||
DownloadURLDirect(a, confirm.ToString(), client);
|
||||
}
|
||||
|
||||
private void DownloadGoogleDriveArchive(GoogleDriveMod a)
|
||||
{
|
||||
var initial_url = $"https://drive.google.com/uc?id={a.Id}&export=download";
|
||||
@ -279,47 +301,66 @@ namespace Wabbajack
|
||||
DownloadURLDirect(archive, match.Value);
|
||||
}
|
||||
|
||||
private void DownloadURLDirect(Archive archive, string url, HttpClient client = null)
|
||||
private void DownloadURLDirect(Archive archive, string url, HttpClient client = null, List<string> headers = null)
|
||||
{
|
||||
if (client == null) {
|
||||
client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", Consts.UserAgent);
|
||||
}
|
||||
long total_read = 0;
|
||||
int buffer_size = 1024 * 32;
|
||||
|
||||
var response = client.GetSync(url);
|
||||
var stream = response.Content.ReadAsStreamAsync();
|
||||
stream.Wait();
|
||||
|
||||
string header = "1";
|
||||
if (response.Content.Headers.Contains("Content-Length"))
|
||||
header = response.Content.Headers.GetValues("Content-Length").FirstOrDefault();
|
||||
|
||||
long content_size = header != null ? long.Parse(header) : 1;
|
||||
|
||||
var output_path = Path.Combine(DownloadFolder, archive.Name);
|
||||
|
||||
if (output_path.FileExists())
|
||||
File.Delete(output_path);
|
||||
|
||||
using (var webs = stream.Result)
|
||||
using (var fs = File.OpenWrite(output_path))
|
||||
try
|
||||
{
|
||||
var buffer = new byte[buffer_size];
|
||||
while (true)
|
||||
if (client == null)
|
||||
{
|
||||
var read = webs.Read(buffer, 0, buffer_size);
|
||||
if (read == 0) break;
|
||||
Status((int)(total_read * 100 / content_size), "Downloading {0}", archive.Name);
|
||||
|
||||
fs.Write(buffer, 0, read);
|
||||
total_read += read;
|
||||
|
||||
client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", Consts.UserAgent);
|
||||
}
|
||||
|
||||
if (headers != null) {
|
||||
foreach (var header in headers)
|
||||
{
|
||||
var idx = header.IndexOf(':');
|
||||
var k = header.Substring(0, idx);
|
||||
var v = header.Substring(idx + 1);
|
||||
client.DefaultRequestHeaders.Add(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
long total_read = 0;
|
||||
int buffer_size = 1024 * 32;
|
||||
|
||||
var response = client.GetSync(url);
|
||||
var stream = response.Content.ReadAsStreamAsync();
|
||||
stream.Wait();
|
||||
|
||||
string header_var = "1";
|
||||
if (response.Content.Headers.Contains("Content-Length"))
|
||||
header_var = response.Content.Headers.GetValues("Content-Length").FirstOrDefault();
|
||||
|
||||
long content_size = header_var != null ? long.Parse(header_var) : 1;
|
||||
|
||||
var output_path = Path.Combine(DownloadFolder, archive.Name);
|
||||
|
||||
if (output_path.FileExists())
|
||||
File.Delete(output_path);
|
||||
|
||||
using (var webs = stream.Result)
|
||||
using (var fs = File.OpenWrite(output_path))
|
||||
{
|
||||
var buffer = new byte[buffer_size];
|
||||
while (true)
|
||||
{
|
||||
var read = webs.Read(buffer, 0, buffer_size);
|
||||
if (read == 0) break;
|
||||
Status((int)(total_read * 100 / content_size), "Downloading {0}", archive.Name);
|
||||
|
||||
fs.Write(buffer, 0, read);
|
||||
total_read += read;
|
||||
|
||||
}
|
||||
}
|
||||
Status("Hashing {0}", archive.Name);
|
||||
HashArchive(output_path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Info($"{archive.Name} - Error downloading from: {url}");
|
||||
}
|
||||
Status("Hashing {0}", archive.Name);
|
||||
HashArchive(output_path);
|
||||
}
|
||||
|
||||
private object GetNexusAPIKey()
|
||||
|
BIN
logos/logo_with_text.jpg
Normal file
BIN
logos/logo_with_text.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
BIN
logos/logo_with_text_transparent.png
Normal file
BIN
logos/logo_with_text_transparent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 120 KiB |
Loading…
Reference in New Issue
Block a user