10 Commits

Author SHA1 Message Date
7965314c6b print download size 2023-10-28 19:35:28 +02:00
cb70b8c415 add av info on permission denied 2023-10-26 06:25:12 +02:00
79a1533eaa Merge pull request #36 from mxve/dependabot/cargo/serde-1.0.189
Bump serde from 1.0.188 to 1.0.189
2023-10-16 17:46:15 +02:00
665e580c79 Merge pull request #37 from mxve/dependabot/cargo/semver-1.0.20
Bump semver from 1.0.19 to 1.0.20
2023-10-16 17:46:08 +02:00
d5de7244e3 Bump semver from 1.0.19 to 1.0.20
Bumps [semver](https://github.com/dtolnay/semver) from 1.0.19 to 1.0.20.
- [Release notes](https://github.com/dtolnay/semver/releases)
- [Commits](https://github.com/dtolnay/semver/compare/1.0.19...1.0.20)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 15:07:26 +00:00
c2e90e17db Bump serde from 1.0.188 to 1.0.189
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.188 to 1.0.189.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.188...v1.0.189)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 15:07:20 +00:00
c81518aec7 update actions 2023-10-16 16:11:56 +02:00
f48cd874f0 Merge pull request #35 from mxve/dependabot/cargo/http_req-0.10.0
Bump http_req from 0.9.3 to 0.10.0
2023-10-09 18:29:43 +02:00
228abe5317 Bump http_req from 0.9.3 to 0.10.0
Bumps [http_req](https://github.com/jayjamesjay/http_req) from 0.9.3 to 0.10.0.
- [Release notes](https://github.com/jayjamesjay/http_req/releases)
- [Commits](https://github.com/jayjamesjay/http_req/compare/v0.9.3...v0.10.0)

---
updated-dependencies:
- dependency-name: http_req
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-09 15:58:32 +00:00
9efd34600a add client-args.md; document config file 2023-10-07 13:24:26 +02:00
10 changed files with 217 additions and 90 deletions

View File

@ -1,9 +1,3 @@
# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md
#
# While our "example" application has the platform-specific code,
# for simplicity we are compiling and testing everything on the Ubuntu environment only.
# For multi-OS testing see the `cross.yml` workflow.
on: [push, pull_request] on: [push, pull_request]
name: lint name: lint
@ -14,18 +8,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install stable toolchain - name: Install stable toolchain
uses: actions-rs/toolchain@v1 uses: dtolnay/rust-toolchain@stable
with: with:
profile: minimal
toolchain: stable toolchain: stable
override: true
- name: Run cargo check - name: Run cargo check
uses: actions-rs/cargo@v1 uses: clechasseur/rs-cargo@v2
continue-on-error: true # WARNING: only for this example, remove it! continue-on-error: true
with: with:
command: check command: check
@ -34,18 +26,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install stable toolchain - name: Install stable toolchain
uses: actions-rs/toolchain@v1 uses: dtolnay/rust-toolchain@stable
with: with:
profile: minimal
toolchain: stable toolchain: stable
override: true
- name: Run cargo test - name: Run cargo test
uses: actions-rs/cargo@v1 uses: clechasseur/rs-cargo@v2
continue-on-error: true # WARNING: only for this example, remove it! continue-on-error: true
with: with:
command: test command: test
@ -54,26 +44,24 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout sources - name: Checkout sources
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Install stable toolchain - name: Install stable toolchain
uses: actions-rs/toolchain@v1 uses: dtolnay/rust-toolchain@stable
with: with:
profile: minimal
toolchain: stable toolchain: stable
override: true
components: rustfmt, clippy components: rustfmt, clippy
- name: Run cargo fmt - name: Run cargo fmt
uses: actions-rs/cargo@v1 uses: clechasseur/rs-cargo@v2
continue-on-error: true # WARNING: only for this example, remove it! continue-on-error: true
with: with:
command: fmt command: fmt
args: --all -- --check args: --all -- --check
- name: Run cargo clippy - name: Run cargo clippy
uses: actions-rs/cargo@v1 uses: clechasseur/rs-cargo@v2
continue-on-error: true # WARNING: only for this example, remove it! continue-on-error: true
with: with:
command: clippy command: clippy
args: -- -D warnings args: -- -D warnings

View File

@ -9,7 +9,7 @@ jobs:
create-release: create-release:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: taiki-e/create-gh-release-action@v1 - uses: taiki-e/create-gh-release-action@v1
with: with:
draft: true draft: true
@ -29,7 +29,7 @@ jobs:
os: windows-latest os: windows-latest
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: taiki-e/upload-rust-binary-action@v1 - uses: taiki-e/upload-rust-binary-action@v1
with: with:
target: ${{ matrix.target }} target: ${{ matrix.target }}

110
Cargo.lock generated
View File

@ -30,7 +30,7 @@ dependencies = [
[[package]] [[package]]
name = "alterware-launcher" name = "alterware-launcher"
version = "0.5.3" version = "0.5.4"
dependencies = [ dependencies = [
"colored", "colored",
"http_req", "http_req",
@ -48,9 +48,9 @@ dependencies = [
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.13.1" version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
@ -312,11 +312,12 @@ dependencies = [
[[package]] [[package]]
name = "http_req" name = "http_req"
version = "0.9.3" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ce34c74ec562d68f2c23a532c62c1332ff1d1b6147fd118bd1938e090137d0" checksum = "158d4edacc70c9bdb0464314063b8d9d60fa776442dc13b00a13581b88b0a0a0"
dependencies = [ dependencies = [
"rustls", "rustls",
"rustls-pemfile",
"unicase", "unicase",
"webpki", "webpki",
"webpki-roots", "webpki-roots",
@ -656,12 +657,26 @@ dependencies = [
"cc", "cc",
"libc", "libc",
"once_cell", "once_cell",
"spin", "spin 0.5.2",
"untrusted", "untrusted 0.7.1",
"web-sys", "web-sys",
"winapi", "winapi",
] ]
[[package]]
name = "ring"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "911b295d2d302948838c8ac142da1ee09fa7863163b44e6715bc9357905878b8"
dependencies = [
"cc",
"getrandom",
"libc",
"spin 0.9.8",
"untrusted 0.9.0",
"windows-sys",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.8" version = "0.38.8"
@ -677,15 +692,33 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.19.1" version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
dependencies = [
"log",
"ring 0.16.20",
"rustls-webpki",
"sct",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [ dependencies = [
"base64", "base64",
"log", ]
"ring",
"sct", [[package]]
"webpki", name = "rustls-webpki"
version = "0.101.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe"
dependencies = [
"ring 0.16.20",
"untrusted 0.7.1",
] ]
[[package]] [[package]]
@ -696,12 +729,12 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]] [[package]]
name = "sct" name = "sct"
version = "0.6.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [ dependencies = [
"ring", "ring 0.16.20",
"untrusted", "untrusted 0.7.1",
] ]
[[package]] [[package]]
@ -717,24 +750,24 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.19" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.188" version = "1.0.189"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.188" version = "1.0.189"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -786,6 +819,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "steamlocate" name = "steamlocate"
version = "2.0.0-alpha.0" version = "2.0.0-alpha.0"
@ -889,9 +928,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.6.0" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [ dependencies = [
"version_check", "version_check",
] ]
@ -908,6 +947,12 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -986,22 +1031,19 @@ dependencies = [
[[package]] [[package]]
name = "webpki" name = "webpki"
version = "0.21.4" version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
dependencies = [ dependencies = [
"ring", "ring 0.17.2",
"untrusted", "untrusted 0.9.0",
] ]
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "0.21.1" version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
dependencies = [
"webpki",
]
[[package]] [[package]]
name = "winapi" name = "winapi"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "alterware-launcher" name = "alterware-launcher"
version = "0.5.3" version = "0.5.4"
edition = "2021" edition = "2021"
build = "res/build.rs" build = "res/build.rs"
@ -15,14 +15,14 @@ panic = "abort"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
http_req = { version = "0.9.3", default-features = false, features = [ http_req = { version = "0.10.0", default-features = false, features = [
"rust-tls", "rust-tls",
] } ] }
sha1_smol = "1.0.0" sha1_smol = "1.0.0"
serde = { version = "1.0.188", features = ["derive"] } serde = { version = "1.0.189", features = ["derive"] }
serde_json = "1.0.107" serde_json = "1.0.107"
rand = "0.8.5" rand = "0.8.5"
semver = "1.0.19" semver = "1.0.20"
zip = "0.6.6" zip = "0.6.6"
colored = "2.0.4" colored = "2.0.4"

View File

@ -32,6 +32,7 @@
- Do not include a trailing backslash in the path - Do not include a trailing backslash in the path
- ```--pass``` - ```--pass```
- Pass additional arguments to the game - Pass additional arguments to the game
- See [client-args.md](client-args.md)
- ```--version```, ```-v``` - ```--version```, ```-v```
- Print the launcher version - Print the launcher version
@ -41,6 +42,30 @@ Some arguments can be set in alterware-launcher.json, args generally override th
--- ---
#### Config file
alterware-launcher.json
- ```update_only```
- See --update
- Default: false
- ```skip_self_update```
- See --skip-launcher-update
- Default: false
- ```download_bonus_content```
- See --bonus
- Default: false
- ```ask_bonus_content```
- Ask the user if they want to download bonus content
- Default: true; false after asking
- ```force_update```
- See --force
- Default: false
- ```args```
- See --pass
- Default: ""
---
#### Support #### Support
Visit the [AlterWare Forum](https://forum.alterware.dev/) or [Discord](https://discord.gg/2ETE8engZM) for support. Visit the [AlterWare Forum](https://forum.alterware.dev/) or [Discord](https://discord.gg/2ETE8engZM) for support.

32
client-args.md Normal file
View File

@ -0,0 +1,32 @@
# IW4x
[github.com/iw4x/iw4x-client#command-line-arguments](https://github.com/iw4x/iw4x-client#command-line-arguments)
| Argument | Description |
|:------------------------|:-----------------------------------------------|
| `-tests` | Perform unit tests. |
| `-entries` | Print to the console a list of every asset as they are loaded from zonefiles. |
| `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. |
| `-console` | Allow the game to display its own separate interactive console window. |
| `-dedicated` | Starts the game as a headless dedicated server. |
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
| `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
| `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
| `-nointro` | Skip game's cinematic intro. |
| `-version` | Print IW4x build info on startup. |
| `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. |
| `-unprotect-dvars` | Allow the server to modify saved/archive dvars. |
| `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. |
| `-disable-notifies` | Disable "Anti-CFG" checks |
| `-disable-mongoose` | Disable Mongoose HTTP server |
| `-disable-rate-limit-check` | Disable RCOn rate limit checks |
| `+<command>` | Execute game command (ex. `+set net_port 1337`)|
# S1-Mod, IW6-Mod
| Argument | Description |
|:------------------------|:-----------------------------------------------|
| `-headless` | Use system console |
| `-dedicated` | Dedicated server |
| `-singleplayer` | Start singleplayer; Skip launcher |
| `-multiplayer` | Start multiplayer; Skip launcher |
| `+<command>` | Execute game command (ex. `+set net_port 1337`)|

View File

@ -66,7 +66,7 @@ pub fn download_file(url: &str, file_path: &Path) {
} }
std::io::ErrorKind::PermissionDenied => { std::io::ErrorKind::PermissionDenied => {
misc::fatal_error(&format!( misc::fatal_error(&format!(
"Permission to {} denied.\n Please try:\n 1. Running the launcher as administrator.\n 2. Manually deleting the last downloaded file.\n 3. If your game is in the program files directory try moving it to another location.\n\n\n{}", "Permission to {} denied.\n Please try:\n 1. Running the launcher as administrator.\n 2. Manually deleting the last downloaded file.\n 3. If your game is in the program files directory try moving it to another location.\n 4. Create an exception/exclusion in your Anti-Virus Software for either the last downloaded file or the entire game directory.\n\n\n{}",
file_path.to_str().unwrap(), file_path.to_str().unwrap(),
e e
)); ));

View File

@ -180,21 +180,35 @@ fn manual_install(games: &[Game]) {
std::process::exit(0); std::process::exit(0);
} }
fn total_download_size(cdn_info: &Vec<CdnFile>, remote_dir: &str) -> u64 {
let remote_dir = format!("{}/", remote_dir);
let mut size: u64 = 0;
for file in cdn_info {
if !file.name.starts_with(&remote_dir) || file.name == "iw4/iw4x.dll" {
continue;
}
size += file.size as u64;
}
size
}
fn update_dir( fn update_dir(
cdn_info: &Vec<CdnFile>, cdn_info: &Vec<CdnFile>,
remote_dir: &str, remote_dir: &str,
dir: &Path, dir: &Path,
hashes: &mut HashMap<String, String>, hashes: &mut HashMap<String, String>,
) { ) {
let remote_dir = format!("{}/", remote_dir); let remote_dir_pre = format!("{}/", remote_dir);
let mut files_to_download: Vec<CdnFile> = vec![];
for file in cdn_info { for file in cdn_info {
if !file.name.starts_with(&remote_dir) || file.name == "iw4/iw4x.dll" { if !file.name.starts_with(&remote_dir_pre) || file.name == "iw4/iw4x.dll" {
continue; continue;
} }
let sha1_remote = file.hash.to_lowercase(); let sha1_remote = file.hash.to_lowercase();
let file_name = &file.name.replace(remote_dir.as_str(), ""); let file_name = &file.name.replace(remote_dir_pre.as_str(), "");
let file_path = dir.join(file_name); let file_path = dir.join(file_name);
if file_path.exists() { if file_path.exists() {
let sha1_local = hashes let sha1_local = hashes
@ -204,21 +218,37 @@ fn update_dir(
.to_string(); .to_string();
if sha1_local != sha1_remote { if sha1_local != sha1_remote {
println!( files_to_download.push(file.clone());
"[{}] {}",
"Updating".bright_yellow(),
file_path.display()
);
http::download_file(&format!("{}/{}", MASTER, file.name), &file_path);
} else { } else {
println!("[{}] {}", "Checked".bright_blue(), file_path.display()); println!("[{}] {}", "Checked".bright_blue(), file_path.display());
} }
hashes.insert(file_name.to_owned(), sha1_remote.to_owned());
} else { } else {
files_to_download.push(file.clone());
}
}
if files_to_download.is_empty() {
println!( println!(
"[{}] {}", "[{}] No files to download for {}",
"Info".bright_magenta(),
remote_dir
);
return;
}
println!(
"[{}] Downloading outdated or missing files for {}, {}",
"Info".bright_magenta(),
remote_dir,
misc::human_readable_bytes(total_download_size(&files_to_download, &remote_dir))
);
for file in files_to_download {
let file_name = &file.name.replace(&format!("{}/", remote_dir), "/");
let file_path = dir.join(file_name);
println!(
"[{}] {} ({})",
"Downloading".bright_yellow(), "Downloading".bright_yellow(),
file_path.display() file_path.display(),
misc::human_readable_bytes(file.size as u64)
); );
if let Some(parent) = file_path.parent() { if let Some(parent) = file_path.parent() {
if !parent.exists() { if !parent.exists() {
@ -226,8 +256,7 @@ fn update_dir(
} }
} }
http::download_file(&format!("{}/{}", MASTER, file.name), &file_path); http::download_file(&format!("{}/{}", MASTER, file.name), &file_path);
hashes.insert(file_name.to_owned(), sha1_remote.to_owned()); hashes.insert(file_name.to_owned(), file.hash.to_lowercase());
}
} }
} }

View File

@ -26,3 +26,14 @@ pub fn fatal_error(error: &str) {
stdin(); stdin();
std::process::exit(1); std::process::exit(1);
} }
pub fn human_readable_bytes(bytes: u64) -> String {
let mut bytes = bytes as f64;
let mut i = 0;
let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
while bytes > 1024.0 {
bytes /= 1024.0;
i += 1;
}
format!("{:.2} {}", bytes, units[i])
}

View File

@ -1,4 +1,4 @@
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize, Clone)]
pub struct CdnFile { pub struct CdnFile {
pub name: String, pub name: String,
pub size: u32, pub size: u32,