diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index 3c502a579..29a75f56b 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -474,6 +474,7 @@
+
@@ -938,6 +939,7 @@
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index 1a1bbc0de..c1a67ee8f 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -4851,6 +4851,9 @@
Source Files\src\pc\network\packets
+
+ Source Files\src\pc\djui\component\compound
+
@@ -5983,5 +5986,8 @@
Source Files\src\pc\network\packets
+
+ Source Files\src\pc\djui\component\compound
+
\ No newline at end of file
diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h
index fc8c88dc5..e3cb8fef1 100644
--- a/src/pc/djui/djui.h
+++ b/src/pc/djui/djui.h
@@ -23,6 +23,7 @@
#include "djui_button.h"
#include "djui_inputbox.h"
#include "djui_slider.h"
+#include "djui_progress_bar.h"
#include "djui_checkbox.h"
#include "djui_flow_layout.h"
#include "djui_selectionbox.h"
diff --git a/src/pc/djui/djui_panel_join_message.c b/src/pc/djui/djui_panel_join_message.c
index 8a1ddb530..c119b7a45 100644
--- a/src/pc/djui/djui_panel_join_message.c
+++ b/src/pc/djui/djui_panel_join_message.c
@@ -7,6 +7,7 @@
bool gDjuiPanelJoinMessageVisible = false;
static struct DjuiText* sPanelText = NULL;
static bool sDisplayingError = false;
+float gDownloadProgress = 0;
void djui_panel_join_message_error(char* message) {
djui_panel_join_message_create(NULL);
@@ -41,7 +42,7 @@ void djui_panel_join_message_create(struct DjuiBase* caller) {
// don't recreate panel if it's already visible
if (gDjuiPanelJoinMessageVisible) { return; }
- f32 bodyHeight = 64 + 16;
+ f32 bodyHeight = 64 + 16 + 16;
u16 directLines = 8;
f32 directTextHeight = 32 * 0.8125f * directLines + 8;
@@ -60,6 +61,9 @@ void djui_panel_join_message_create(struct DjuiBase* caller) {
text1->base.on_render_pre = djui_panel_join_message_render_pre;
sPanelText = text1;
+ gDownloadProgress = 0;
+ djui_progress_bar_create(&body->base, &gDownloadProgress, 0.0f, 1.0f);
+
struct DjuiButton* button1 = djui_button_create(&body->base, "Cancel");
djui_base_set_size_type(&button1->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&button1->base, 1.0f, 64);
diff --git a/src/pc/djui/djui_progress_bar.c b/src/pc/djui/djui_progress_bar.c
new file mode 100644
index 000000000..375436fa8
--- /dev/null
+++ b/src/pc/djui/djui_progress_bar.c
@@ -0,0 +1,51 @@
+#include "djui.h"
+
+void djui_progress_bar_render_pre(struct DjuiBase* base, UNUSED bool* unused) {
+ struct DjuiProgressBar* progress = (struct DjuiProgressBar*)base;
+ progress->smoothValue = progress->smoothValue * 0.95f + *progress->value * 0.05f;
+ float min = progress->min;
+ float max = progress->max;
+ djui_base_set_size(&progress->rectValue->base, ((f32)progress->smoothValue - min) / ((f32)max - min), 1.0f);
+}
+
+static void djui_progress_bar_set_default_style(struct DjuiBase* base) {
+ struct DjuiProgressBar* progress = (struct DjuiProgressBar*)base;
+ djui_base_set_border_color(&progress->rect->base, 173, 173, 173, 255);
+ djui_base_set_color(&progress->rect->base, 0, 0, 0, 0);
+ djui_base_set_color(&progress->rectValue->base, 200, 200, 200, 255);
+}
+
+static void djui_progress_bar_destroy(struct DjuiBase* base) {
+ struct DjuiProgressBar* progress = (struct DjuiProgressBar*)base;
+ free(progress);
+}
+
+struct DjuiProgressBar* djui_progress_bar_create(struct DjuiBase* parent, float* value, float min, float max) {
+ struct DjuiProgressBar* progress = calloc(1, sizeof(struct DjuiProgressBar));
+ struct DjuiBase* base = &progress->base;
+
+ progress->value = value;
+ progress->smoothValue = *value;
+ progress->min = min;
+ progress->max = max;
+
+ djui_base_init(parent, base, NULL, djui_progress_bar_destroy);
+ djui_base_set_size_type(base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
+ djui_base_set_size(base, 1.0f, 16);
+ base->on_render_pre = djui_progress_bar_render_pre;
+
+ struct DjuiRect* rect = djui_rect_create(&progress->base);
+ djui_base_set_size_type(&rect->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ djui_base_set_size(&rect->base, 1.0f, 1.0f);
+ djui_base_set_color(&rect->base, 0, 0, 0, 0);
+ djui_base_set_border_width(&rect->base, 2);
+ progress->rect = rect;
+
+ struct DjuiRect* rectValue = djui_rect_create(&rect->base);
+ djui_base_set_size_type(&rectValue->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
+ progress->rectValue = rectValue;
+
+ djui_progress_bar_set_default_style(base);
+
+ return progress;
+}
diff --git a/src/pc/djui/djui_progress_bar.h b/src/pc/djui/djui_progress_bar.h
new file mode 100644
index 000000000..f191d3200
--- /dev/null
+++ b/src/pc/djui/djui_progress_bar.h
@@ -0,0 +1,15 @@
+#pragma once
+#include "djui.h"
+
+#pragma pack(1)
+struct DjuiProgressBar {
+ struct DjuiBase base;
+ struct DjuiRect* rect;
+ struct DjuiRect* rectValue;
+ float* value;
+ float smoothValue;
+ float min;
+ float max;
+};
+
+struct DjuiProgressBar* djui_progress_bar_create(struct DjuiBase* parent, float* value, float min, float max);
diff --git a/src/pc/lua/smlua.c b/src/pc/lua/smlua.c
index 4d444efbb..2d01076be 100644
--- a/src/pc/lua/smlua.c
+++ b/src/pc/lua/smlua.c
@@ -92,7 +92,7 @@ void smlua_init(void) {
// load scripts
LOG_INFO("Loading scripts:");
- for (int i = 0; i < sModEntryCount; i++) {
+ for (int i = 0; i < gModEntryCount; i++) {
struct ModListEntry* entry = &gModEntries[i];
LOG_INFO(" %s", entry->path);
smlua_load_script(entry->path);
diff --git a/src/pc/mod_list.c b/src/pc/mod_list.c
index 342f6981b..a3e97a615 100644
--- a/src/pc/mod_list.c
+++ b/src/pc/mod_list.c
@@ -7,7 +7,9 @@
#define MAX_SESSION_CHARS 7
struct ModListEntry* gModEntries = NULL;
-u16 sModEntryCount = 0;
+u16 gModEntryCount = 0;
+u64 gModTotalSize = 0;
+
static char sTmpSession[MAX_SESSION_CHARS] = { 0 };
static char sTmpPath[PATH_MAX] = { 0 };
@@ -18,8 +20,8 @@ static bool acceptable_file(char* string) {
void mod_list_alloc(u16 count) {
mod_list_clear();
- sModEntryCount = count;
- gModEntries = (struct ModListEntry*)calloc(sModEntryCount, sizeof(struct ModListEntry));
+ gModEntryCount = count;
+ gModEntries = (struct ModListEntry*)calloc(gModEntryCount, sizeof(struct ModListEntry));
}
void mod_list_add(u16 index, char* name, size_t size, bool tmpFile) {
@@ -27,6 +29,7 @@ void mod_list_add(u16 index, char* name, size_t size, bool tmpFile) {
struct ModListEntry* entry = &gModEntries[index];
entry->name = name;
entry->size = size;
+ gModTotalSize += size;
if (tmpFile) {
snprintf(entry->path, PATH_MAX - 1, "%s/%s-%s", sTmpPath, sTmpSession, name);
@@ -62,6 +65,7 @@ void mod_list_load(void) {
u16 index = 0;
LOG_INFO("Loading mods:");
+ gModTotalSize = 0;
while ((dir = readdir(d)) != NULL) {
if (!acceptable_file(dir->d_name)) { continue; }
LOG_INFO(" %s", dir->d_name);
@@ -72,7 +76,7 @@ void mod_list_load(void) {
}
void mod_list_clear(void) {
- for (int i = 0; i < sModEntryCount; i++) {
+ for (int i = 0; i < gModEntryCount; i++) {
struct ModListEntry* entry = &gModEntries[i];
if (entry->name != NULL) {
free(entry->name);
@@ -88,7 +92,7 @@ void mod_list_clear(void) {
free(gModEntries);
gModEntries = NULL;
}
- sModEntryCount = 0;
+ gModEntryCount = 0;
}
static void mod_list_delete_tmp(void) {
diff --git a/src/pc/mod_list.h b/src/pc/mod_list.h
index 85021ccb9..49dbe840a 100644
--- a/src/pc/mod_list.h
+++ b/src/pc/mod_list.h
@@ -18,7 +18,8 @@ struct ModListEntry {
bool complete;
};
extern struct ModListEntry* gModEntries;
-extern u16 sModEntryCount;
+extern u16 gModEntryCount;
+extern u64 gModTotalSize;
void mod_list_alloc(u16 count);
void mod_list_add(u16 index, char* name, size_t size, bool tmpFile);
diff --git a/src/pc/network/packets/packet_download.c b/src/pc/network/packets/packet_download.c
index 711a5fef3..fb313c3c6 100644
--- a/src/pc/network/packets/packet_download.c
+++ b/src/pc/network/packets/packet_download.c
@@ -8,10 +8,12 @@
static u64 sOffset[OFFSET_COUNT] = { 0 };
static bool sWaitingForOffset[OFFSET_COUNT] = { 0 };
+u64 sTotalDownloadBytes = 0;
+extern float gDownloadProgress;
static void network_send_next_download_request(void) {
SOFT_ASSERT(gNetworkType == NT_CLIENT);
- for (int i = 0; i < sModEntryCount; i++) {
+ for (int i = 0; i < gModEntryCount; i++) {
struct ModListEntry* entry = &gModEntries[i];
if (entry->complete) { continue; }
network_send_download_request(i, entry->curOffset);
@@ -29,6 +31,11 @@ void network_send_download_request(u16 index, u64 offset) {
packet_write(&p, &index, sizeof(u16));
packet_write(&p, &offset, sizeof(u64));
+ if (index == 0 && offset == 0) {
+ sTotalDownloadBytes = 0;
+ gDownloadProgress = 0;
+ }
+
struct ModListEntry* entry = &gModEntries[index];
for (int i = 0; i < OFFSET_COUNT; i++) {
sOffset[i] = offset + CHUNK_SIZE * i;
@@ -47,7 +54,7 @@ void network_receive_download_request(struct Packet* p) {
packet_read(p, &offset, sizeof(u64));
struct ModListEntry* entry = &gModEntries[index];
- if (index >= sModEntryCount) {
+ if (index >= gModEntryCount) {
LOG_ERROR("Requested download of invalid index %u:%llu", index, offset);
return;
}
@@ -62,7 +69,7 @@ void network_receive_download_request(struct Packet* p) {
void network_send_download(u16 index, u64 offset) {
SOFT_ASSERT(gNetworkType == NT_SERVER);
- if (index >= sModEntryCount) {
+ if (index >= gModEntryCount) {
LOG_ERROR("Requested download of invalid index %u:%llu", index, offset);
return;
}
@@ -115,7 +122,7 @@ void network_receive_download(struct Packet* p) {
packet_read(p, &chunkSize, sizeof(u16));
packet_read(p, chunk, chunkSize * sizeof(u8));
- if (index >= sModEntryCount) {
+ if (index >= gModEntryCount) {
LOG_ERROR("Received download of invalid index %u:%llu", index, offset);
return;
}
@@ -148,7 +155,7 @@ void network_receive_download(struct Packet* p) {
}
if (!found) {
- LOG_ERROR("Received download of unexpected offset %llu != %llu", entry->curOffset, offset);
+ LOG_ERROR("Received download of unexpected offset [ %llu <-> %llu ] != %llu", entry->curOffset, entry->curOffset + CHUNK_SIZE * OFFSET_COUNT, offset);
return;
}
@@ -156,6 +163,10 @@ void network_receive_download(struct Packet* p) {
fseek(entry->fp, offset, SEEK_SET);
fwrite(chunk, sizeof(u8) * chunkSize, 1, entry->fp);
+ // update progress
+ sTotalDownloadBytes += chunkSize;
+ gDownloadProgress = (float)sTotalDownloadBytes / (float)gModTotalSize;
+
if (!waiting) {
// check if we're finished with this file
if (sOffset[OFFSET_COUNT - 1] >= entry->size) {
diff --git a/src/pc/network/packets/packet_mod_list.c b/src/pc/network/packets/packet_mod_list.c
index 5b9b18aa2..debb1eebd 100644
--- a/src/pc/network/packets/packet_mod_list.c
+++ b/src/pc/network/packets/packet_mod_list.c
@@ -44,9 +44,9 @@ void network_send_mod_list(void) {
struct Packet p = { 0 };
packet_init(&p, PACKET_MOD_LIST, true, PLMT_NONE);
- packet_write(&p, &sModEntryCount, sizeof(u16));
- LOG_INFO("sent mod list (%u):", sModEntryCount);
- for (int i = 0; i < sModEntryCount; i++) {
+ packet_write(&p, &gModEntryCount, sizeof(u16));
+ LOG_INFO("sent mod list (%u):", gModEntryCount);
+ for (int i = 0; i < gModEntryCount; i++) {
struct ModListEntry* entry = &gModEntries[i];
u16 nameLength = strlen(entry->name);
packet_write(&p, &nameLength, sizeof(u16));
@@ -74,6 +74,7 @@ void network_receive_mod_list(struct Packet* p) {
mod_list_alloc(modEntryCount);
LOG_INFO("received mod list (%u):", modEntryCount);
+ gModTotalSize = 0;
for (int i = 0; i < modEntryCount; i++) {
u16 nameLength = 0;
packet_read(p, &nameLength, sizeof(u16));