diff --git a/build.zig b/build.zig deleted file mode 100755 index ecf681b..0000000 --- a/build.zig +++ /dev/null @@ -1,484 +0,0 @@ -//usr/bin/env zig run $0 -lc `pkgconf --libs libarchive libcurl`; exit -// This script requires a zig compiler, libarchive and libcurl to run. -// If you're on windows, screw you lol - -const std = @import("std"); -const c = @cImport({ - @cInclude("curl/curl.h"); - @cInclude("archive.h"); - @cInclude("archive_entry.h"); -}); -const settings = @import("settings.zig"); - -pub fn main() !void { - const simple_output = std.os.getenv("BUILD_SIMPLE_OUTPUT") != null; - - // used to buffer whatever - var buf: [512]u8 = undefined; - try std.fs.cwd().deleteTree(settings.build_dir); - try std.fs.cwd().makeDir(settings.build_dir); - - var zip = c.archive_write_new(); - if (zip == null) - return error.ArchiveNewError; - defer _ = c.archive_write_free(zip); - try handleArchiveErr(c.archive_write_set_format_zip(zip), zip); - try handleArchiveErr(c.archive_write_set_format_option( - zip, - "zip", - "compression-level", - settings.compression_level, - ), zip); - try handleArchiveErr(c.archive_write_open_filename( - zip, - settings.build_dir ++ "/" ++ settings.name ++ "-" ++ settings.version ++ ".zip", - ), zip); - - var entry = c.archive_entry_new(); - defer c.archive_entry_free(entry); - - try archiveCreateDir(zip.?, entry.?, "minecraft/"); - try archiveCreateDir(zip.?, entry.?, "minecraft/mods/"); - - const writer = ArchiveWriter{ .context = zip.? }; - - var overrides = try std.fs.cwd().openIterableDir("overrides", .{}); - defer overrides.close(); - var walker = try overrides.walk(std.heap.c_allocator); - defer walker.deinit(); - - const stdout = std.io.getStdOut().writer(); - while (try walker.next()) |e| { - switch (e.kind) { - .Directory => { - if (simple_output) { - stdout.print( - "Writing Directory\t{s}\n", - .{e.path}, - ) catch {}; - } else { - stdout.print( - "Writing Directory\t\x1b[34m{s}/\x1b[0m\n", - .{e.path}, - ) catch {}; - } - const path = try std.mem.concatWithSentinel( - std.heap.c_allocator, - u8, - &[_][]const u8{ "minecraft/", e.path }, - 0, - ); - defer std.heap.c_allocator.free(path); - try archiveCreateDir(zip.?, entry.?, path.ptr); - }, - .File => { - if (simple_output) { - stdout.print("Writing File\t\t{s}\n", .{e.path}) catch {}; - } else { - stdout.print("Writing File\t\t\x1b[34m{s}\x1b[0m\n", .{e.path}) catch {}; - } - const path = try std.mem.concatWithSentinel( - std.heap.c_allocator, - u8, - &[_][]const u8{ "minecraft/", e.path }, - 0, - ); - defer std.heap.c_allocator.free(path); - var file = try overrides.dir.openFile(e.path, .{}); - defer file.close(); - - try archiveFile( - zip.?, - entry.?, - &buf, - path.ptr, - file, - ); - }, - else => {}, - } - } - - try installMmcPackJson(zip.?, entry.?); - - c.archive_entry_set_pathname(entry, "instance.cfg"); - c.archive_entry_set_size(entry, settings.instance_cfg_data.len); - try handleArchiveErr(c.archive_write_header(zip, entry), zip); - try writer.writeAll(settings.instance_cfg_data); - - var mods_arena = std.heap.ArenaAllocator.init(std.heap.c_allocator); - defer mods_arena.deinit(); - var mods = std.ArrayList([]u8).init(std.heap.c_allocator); - defer mods.deinit(); - - readMods(&mods, mods_arena.allocator()) catch |err| { - std.log.err("Error reading mods.conf", .{}); - return err; - }; - - downloadMods(mods.items, zip.?, entry.?, simple_output) catch |err| { - std.log.err("Error downloading mods", .{}); - return err; - }; - - try handleArchiveErr(c.archive_write_close(zip), zip); -} - -const ArchiveWriter = std.io.Writer( - *c.archive, - error{ArchiveError}, - writeArchive, -); - -fn writeArchive(archive: *c.archive, bytes: []const u8) error{ArchiveError}!usize { - const result = c.archive_write_data(archive, bytes.ptr, bytes.len); - if (result < 0) { - try handleArchiveErr(result, archive); - } - return @intCast(usize, result); -} - -fn archiveFile( - archive: *c.archive, - entry: *c.archive_entry, - buf: []u8, - name: [*c]const u8, - file: std.fs.File, -) !void { - entrySetFile(entry); - c.archive_entry_set_pathname(entry, name); - c.archive_entry_set_size(entry, @intCast(i64, (try file.stat()).size)); - try handleArchiveErr(c.archive_write_header(archive, entry), archive); - - const writer = ArchiveWriter{ .context = archive }; - var fifo = std.fifo.LinearFifo(u8, .Slice).init(buf); - try fifo.pump(file.reader(), writer); -} - -/// `name` must end with '/'! -fn archiveCreateDir( - archive: *c.archive, - entry: *c.archive_entry, - name: [*c]const u8, -) !void { - entrySetDir(entry); - c.archive_entry_set_pathname(entry, name); - try handleArchiveErr(c.archive_write_header(archive, entry), archive); -} - -fn installMmcPackJson(archive: *c.archive, entry: *c.archive_entry) !void { - const Requires = struct { - uid: []const u8, - equals: ?[]const u8 = null, - suggests: ?[]const u8 = null, - }; - - const Component = struct { - cachedName: []const u8, - cachedRequires: ?[]const Requires = null, - cachedVersion: []const u8, - cachedVolatile: ?bool = null, - dependencyOnly: ?bool = null, - important: ?bool = null, - uid: []const u8, - version: []const u8, - }; - - const data = .{ - .components = &[_]Component{ - .{ - .cachedName = "LWJGL 2", - .cachedVersion = "2.9.4-nightly-20150209", - .cachedVolatile = true, - .dependencyOnly = true, - .uid = "org.lwjgl", - .version = "2.9.4-nightly-20150209", - }, - .{ - .cachedName = "Minecraft", - .cachedRequires = &.{ - .{ - .uid = "org.lwjgl", - .suggests = "2.9.4-nightly-20150209", - }, - }, - .cachedVersion = settings.minecraft_version, - .important = true, - .uid = "net.minecraft", - .version = settings.minecraft_version, - }, - .{ - .cachedName = "Forge", - .cachedRequires = &.{ - .{ - .uid = "net.minecraft", - .equals = settings.minecraft_version, - }, - }, - .uid = "net.minecraftforge", - .cachedVersion = settings.forge_version, - .version = settings.forge_version, - }, - }, - .formatVersion = 1, - }; - - // We run the serializer twice, because we need to know the size ahead of time for zip. - // This is faster than allocating the json on the heap. - var counter = std.io.countingWriter(std.io.null_writer); - try std.json.stringify( - data, - .{ .emit_null_optional_fields = false }, - counter.writer(), - ); - - entrySetFile(entry); - c.archive_entry_set_size(entry, @intCast(i64, counter.bytes_written)); - c.archive_entry_set_pathname(entry, "mmc-pack.json"); - try handleArchiveErr(c.archive_write_header(archive, entry), archive); - - try std.json.stringify( - data, - .{ .emit_null_optional_fields = false }, - ArchiveWriter{ .context = archive }, - ); -} - -fn readMods(list: *std.ArrayList([]u8), alloc: std.mem.Allocator) !void { - var file = try std.fs.cwd().openFile("mods.conf", .{}); - defer file.close(); - var line_buf: [1024]u8 = undefined; - - while (try file.reader().readUntilDelimiterOrEof(&line_buf, '\n')) |line| { - // mods.txt has comments with "#" - const line_without_comment = std.mem.sliceTo(line, '#'); - const trimmed_line = std.mem.trim(u8, line_without_comment, "\n\r\t "); - if (trimmed_line.len != 0) { - try list.append(try alloc.dupe(u8, trimmed_line)); - } - } -} - -fn curlWriteCallback( - data: [*]const u8, - size: usize, - nmemb: usize, - out: *std.ArrayList(u8), -) callconv(.C) usize { - const realsize = size * nmemb; - out.writer().writeAll(data[0..realsize]) catch return 0; - return realsize; -} - -const CurlInfo = struct { - filename: []const u8, - index: usize, - total: usize, - mod_number_width: usize, - simple_output: bool, - - fn logStart(self: *CurlInfo) !void { - if (!self.simple_output) - return; - - try std.io.getStdOut().writer().print( - "[{d:[3]}/{d}] {s} Downloading...\n", - .{ self.index, self.total, self.filename, self.mod_number_width }, - ); - } - - fn logProgress(self: *CurlInfo, percentage: u8) !void { - if (self.simple_output) - return; - - try std.io.getStdOut().writer().print( - "\r\x1b[34m[{d:[4]}/{d}] \x1b[97m{s} \x1b[32m{}%", - .{ - self.index, - self.total, - self.filename, - percentage, - self.mod_number_width, - }, - ); - } - - fn logZipping(self: *CurlInfo) !void { - if (!self.simple_output) { - try std.io.getStdOut().writer().print( - "\r\x1b[34m[{d:[3]}/{d}] \x1b[97m{s} \x1b[31mZipping...", - .{ self.index, self.total, self.filename, self.mod_number_width }, - ); - } else { - try std.io.getStdOut().writer().print( - "[{d:[3]}/{d}] {s} Zipping...\n", - .{ self.index, self.total, self.filename, self.mod_number_width }, - ); - } - } - - fn logDone(self: *CurlInfo) !void { - if (self.simple_output) - return; - - std.io.getStdOut().writer().print( - "\x1b[2K\r\x1b[34m[{d:[3]}/{d}] \x1b[97m{s}\n", - .{ self.index, self.total, self.filename, self.mod_number_width }, - ) catch {}; - } -}; - -fn curlInfoCallback( - info: *CurlInfo, - dltotal: c.curl_off_t, - dlnow: c.curl_off_t, - ultotal: c.curl_off_t, - ulnow: c.curl_off_t, -) callconv(.C) usize { - _ = ultotal; - _ = ulnow; - info.logProgress( - @intCast(u8, if (dltotal != 0) @divTrunc(dlnow * 100, dltotal) else 0), - ) catch {}; - return 0; -} - -fn downloadMods( - mods: []const []const u8, - zip: *c.archive, - entry: *c.archive_entry, - simple_output: bool, -) !void { - var curl = c.curl_easy_init(); - if (curl == null) - return error.CurlInitError; - defer c.curl_easy_cleanup(curl); - - try handleCurlErr(c.curl_easy_setopt( - curl, - c.CURLOPT_WRITEFUNCTION, - &curlWriteCallback, - )); - try handleCurlErr(c.curl_easy_setopt( - curl, - c.CURLOPT_XFERINFOFUNCTION, - &curlInfoCallback, - )); - try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_NOPROGRESS, @as(c_long, 0))); - try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 1))); - - const mod_number_width = std.math.log10(mods.len) + 1; - - const writer = ArchiveWriter{ .context = zip }; - var mod_buf = std.ArrayList(u8).init(std.heap.c_allocator); - defer mod_buf.deinit(); - var info = CurlInfo{ - .filename = "", - .index = 0, - .total = mods.len, - .mod_number_width = mod_number_width, - .simple_output = simple_output, - }; - try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_XFERINFODATA, &info)); - // hide cursor - if (!simple_output) { - std.io.getStdOut().writeAll("\x1b[?25l") catch {}; - } - // show cursor & reset - defer if (!simple_output) { - std.io.getStdOut().writeAll("\x1b[?25h\x1b[0\n") catch {}; - }; - - for (mods) |mod| { - info.index += 1; - - mod_buf.clearRetainingCapacity(); - var splits = std.mem.split(u8, mod, "/"); - var filename_esc: ?[]const u8 = null; - while (splits.next()) |split| - filename_esc = split; - - if (filename_esc == null or filename_esc.?.len == 0) { - std.log.err("Failed to get filename of URL {s}", .{mod}); - return error.BorkedUrl; - } - - var filename_len: c_int = undefined; - var filename_cstr = c.curl_easy_unescape( - curl, - filename_esc.?.ptr, - @intCast(c_int, filename_esc.?.len), - &filename_len, - ); - defer c.curl_free(filename_cstr); - var filename = filename_cstr[0..@intCast(usize, filename_len)]; - - // Replace + with space in URL decoded filename - for (filename) |*ch| { - if (ch.* == '+') { - ch.* = ' '; - } - } - - info.filename = filename; - - try handleCurlErr(c.curl_easy_setopt(curl, c.CURLOPT_WRITEDATA, &mod_buf)); - - const mod_cstr = try std.cstr.addNullByte(std.heap.c_allocator, mod); - defer std.heap.c_allocator.free(mod_cstr); - - try handleCurlErr(c.curl_easy_setopt( - curl, - c.CURLOPT_URL, - mod_cstr.ptr, - )); - - try info.logStart(); - - try handleCurlErr(c.curl_easy_perform(curl)); - - try info.logZipping(); - - var archive_path = try std.mem.concatWithSentinel( - std.heap.c_allocator, - u8, - &.{ "minecraft/mods/", filename }, - 0, - ); - defer std.heap.c_allocator.free(archive_path); - - c.archive_entry_set_pathname(entry, archive_path.ptr); - c.archive_entry_set_size(entry, @intCast(i64, mod_buf.items.len)); - try handleArchiveErr(c.archive_write_header(zip, entry), zip); - try writer.writeAll(mod_buf.items); - try info.logDone(); - } -} - -fn entrySetDir(entry: *c.archive_entry) void { - c.archive_entry_set_filetype(entry, c.S_IFDIR); - c.archive_entry_set_perm(entry, 0o755); - c.archive_entry_unset_size(entry); -} - -fn entrySetFile(entry: *c.archive_entry) void { - c.archive_entry_set_filetype(entry, c.S_IFREG); - c.archive_entry_set_perm(entry, 0o644); -} - -fn handleCurlErr(code: c.CURLcode) !void { - if (code != c.CURLE_OK) { - std.log.err("Curl error: {s}", .{c.curl_easy_strerror(code)}); - return error.CurlError; - } -} - -fn handleArchiveErr(err: anytype, archive: ?*c.archive) !void { - if (err != c.ARCHIVE_OK) { - if (archive) |ar| { - if (c.archive_error_string(ar)) |err_s| - std.log.err("Archive error: {s}", .{err_s}); - } - return error.ArchiveError; - } -} diff --git a/settings.zig b/settings.zig deleted file mode 100644 index a47711f..0000000 --- a/settings.zig +++ /dev/null @@ -1,15 +0,0 @@ -pub const name = "mineteck-reloaded"; - -/// Version number used for the archive name -pub const version = "0.1.0"; -pub const build_dir = "build"; -pub const minecraft_version = "1.7.10"; -pub const forge_version = "10.13.4.1614"; - -/// the data for the `instance.cfg` file -pub const instance_cfg_data = - \\InstanceType=OneSix -; - -/// zip compression level. 9 is max. ask libarchive why this is a string. -pub const compression_level = "9";