Skip to content

Commit 9c28b5f

Browse files
committed
add configuration options for chdman
1 parent df6ffa0 commit 9c28b5f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+367
-125
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Enhance the rom matching algorithm in `import-roms` to reduce prompts when multiple matches are found
55
- Use `createcd/createdvd` and `extractcd/extractdvd` appropriately for CDs and DVDs
66
- Add a `-r` flag to convert-roms to recompress files that already match the target format
7+
- Add configuration options for chdman
78

89
# 0.18.0
910

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ Available settings:
9797
- `REGIONS_ONE_SUBFOLDERS`: Sort 1G1R ROMs in subfolders, defaults to `none`, valid choices: `none`, `alpha`
9898
- `REGIONS_ONE_STRICT`: `true` will elect ROMs regardless of them being available, `false` will only elect available ROMs, defaults to `false`
9999
- `GROUP_SUBSYSTEMS`: Group all system variants in a single directory, defaults to `true`
100+
- `CHD_CD_HUNK_SIZE`: The CHD hunk size in bytes for CDs, defaults to auto, valid range: `16-1048576`
101+
- `CHD_CD_COMPRESSION_ALGORITHMS`: The CHD compression algorithms for CDs, up to 4 can be specified, defaults to auto, valid choices: `none`, `cdfl`, `cdlz`, `cdzl`, `cdzs`
102+
- `CHD_DVD_HUNK_SIZE`: The CHD hunk size in bytes for DVDs, defaults to auto, valid range: `16-1048576`
103+
- `CHD_DVD_COMPRESSION_ALGORITHMS`: The CHD compression algorithms for DVDs, up to 4 can be specified, defaults to auto, valid choices: `none`, `flac`, `huff`, `lzma`, `zlib`, `zstd`
100104
- `RVZ_BLOCK_SIZE`: The RVZ block size in KiB, defaults to `128`, valid range: `32-2048`
101105
- `RVZ_COMPRESSION_ALGORITHM`: The RVZ compression algorithm, defaults to `zstd`, valid choices: `none`, `zstd`, `bzip`, `lzma`, `lzma2`
102106
- `RVZ_COMPRESSION_LEVEL`: The RVZ compression level, defaults to `5`, valid ranges: `1-22` for zstd, `1-9` for the other algorithms
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
INSERT OR REPLACE INTO settings ("key", value)
2+
VALUES ('CHD_CD_HUNK_SIZE', NULL);
3+
4+
INSERT OR REPLACE INTO settings ("key", value)
5+
VALUES ('CHD_CD_COMPRESSION_ALGORITHMS', NULL);
6+
7+
INSERT OR REPLACE INTO settings ("key", value)
8+
VALUES ('CHD_DVD_HUNK_SIZE', NULL);
9+
10+
INSERT OR REPLACE INTO settings ("key", value)
11+
VALUES ('CHD_DVD_COMPRESSION_ALGORITHMS', NULL);

src/chdman.rs

+62-16
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,45 @@
1-
use std::path::{Path, PathBuf};
2-
use std::time::Duration;
3-
4-
use indicatif::ProgressBar;
5-
use regex::Regex;
6-
use sqlx::SqliteConnection;
7-
use tokio::io;
8-
use tokio::io::AsyncReadExt;
9-
use tokio::process::Command;
10-
111
use super::common::*;
122
use super::config::*;
133
use super::model::*;
144
use super::progress::*;
155
use super::util::*;
166
use super::SimpleResult;
7+
use indicatif::ProgressBar;
8+
use regex::Regex;
9+
use sqlx::SqliteConnection;
10+
use std::path::{Path, PathBuf};
11+
use std::time::Duration;
12+
use strum::{Display, EnumString, VariantNames};
13+
use tokio::io;
14+
use tokio::io::AsyncReadExt;
15+
use tokio::process::Command;
1716

1817
const CHDMAN: &str = "chdman";
1918

19+
pub const CHD_HUNK_SIZE_RANGE: [usize; 2] = [16, 1048576];
2020
pub const MIN_DREAMCAST_VERSION: &str = "0.264";
2121

22+
#[derive(Display, PartialEq, EnumString, VariantNames)]
23+
#[strum(serialize_all = "lowercase")]
24+
pub enum ChdCdCompressionAlgorithm {
25+
None,
26+
Cdfl,
27+
Cdlz,
28+
Cdzl,
29+
Cdzs,
30+
}
31+
32+
#[derive(Display, PartialEq, EnumString, VariantNames)]
33+
#[strum(serialize_all = "lowercase")]
34+
pub enum ChdDvdCompressionAlgorithm {
35+
None,
36+
Flac,
37+
Huff,
38+
Lzma,
39+
Zlib,
40+
Zstd,
41+
}
42+
2243
lazy_static! {
2344
static ref VERSION_REGEX: Regex = Regex::new(r"\d+\.\d+").unwrap();
2445
}
@@ -77,6 +98,8 @@ pub trait ToChd {
7798
progress_bar: &ProgressBar,
7899
destination_directory: &P,
79100
media_type: &MediaType,
101+
compression_algorithms: &[String],
102+
hunk_size: &Option<usize>,
80103
) -> SimpleResult<ChdRomfile>;
81104
}
82105

@@ -86,12 +109,16 @@ impl ToChd for CueBinRomfile {
86109
progress_bar: &ProgressBar,
87110
destination_directory: &P,
88111
media_type: &MediaType,
112+
compression_algorithms: &[String],
113+
hunk_size: &Option<usize>,
89114
) -> SimpleResult<ChdRomfile> {
90115
let path = create_chd(
91116
progress_bar,
92117
&self.cue_romfile.path,
93118
destination_directory,
94119
media_type,
120+
hunk_size,
121+
compression_algorithms,
95122
)
96123
.await?;
97124
Ok(ChdRomfile {
@@ -107,8 +134,18 @@ impl ToChd for IsoRomfile {
107134
progress_bar: &ProgressBar,
108135
destination_directory: &P,
109136
media_type: &MediaType,
137+
compression_algorithms: &[String],
138+
hunk_size: &Option<usize>,
110139
) -> SimpleResult<ChdRomfile> {
111-
let path = create_chd(progress_bar, &self.path, destination_directory, media_type).await?;
140+
let path = create_chd(
141+
progress_bar,
142+
&self.path,
143+
destination_directory,
144+
media_type,
145+
hunk_size,
146+
compression_algorithms,
147+
)
148+
.await?;
112149
Ok(ChdRomfile {
113150
path,
114151
cue_path: None,
@@ -280,6 +317,8 @@ async fn create_chd<P: AsRef<Path>, Q: AsRef<Path>>(
280317
romfile_path: &P,
281318
destination_directory: &Q,
282319
media_type: &MediaType,
320+
hunk_size: &Option<usize>,
321+
compression_algorithms: &[String],
283322
) -> SimpleResult<PathBuf> {
284323
progress_bar.set_message("Creating chd");
285324
progress_bar.set_style(get_none_progress_style());
@@ -295,18 +334,25 @@ async fn create_chd<P: AsRef<Path>, Q: AsRef<Path>>(
295334
chd_path.file_name().unwrap().to_str().unwrap()
296335
));
297336

298-
let output = Command::new(CHDMAN)
337+
let mut command = Command::new(CHDMAN);
338+
command
299339
.arg(match media_type {
300340
MediaType::Cd => "createcd",
301341
MediaType::Dvd => "createdvd",
302342
})
303343
.arg("-i")
304344
.arg(romfile_path.as_ref())
305345
.arg("-o")
306-
.arg(&chd_path)
307-
.output()
308-
.await
309-
.expect("Failed to create chd");
346+
.arg(&chd_path);
347+
if let Some(hunk_size) = hunk_size {
348+
command.arg("--hunksize").arg(hunk_size.to_string());
349+
}
350+
if !compression_algorithms.is_empty() {
351+
command
352+
.arg("--compression")
353+
.arg(compression_algorithms.join(","));
354+
}
355+
let output = command.output().await.expect("Failed to create chd");
310356

311357
if !output.status.success() {
312358
bail!(String::from_utf8(output.stderr).unwrap().as_str())

src/common.rs

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
use std::fs::File;
2-
use std::io;
3-
use std::io::prelude::*;
4-
use std::path::{Path, PathBuf};
5-
6-
use digest::Digest;
7-
use indicatif::ProgressBar;
8-
use md5::Md5;
9-
use sha1::Sha1;
10-
use simple_error::SimpleResult;
11-
use sqlx::SqliteConnection;
12-
131
use super::config::*;
142
use super::crc32::*;
153
use super::database::*;
164
use super::model::Header;
175
use super::model::*;
186
use super::progress::*;
197
use super::util::*;
8+
use digest::Digest;
9+
use indicatif::ProgressBar;
10+
use md5::Md5;
11+
use sha1::Sha1;
12+
use simple_error::SimpleResult;
13+
use sqlx::SqliteConnection;
14+
use std::fs::File;
15+
use std::io;
16+
use std::io::prelude::*;
17+
use std::path::{Path, PathBuf};
2018

2119
#[derive(Clone)]
2220
pub struct CommonRomfile {

src/config.rs

+26-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
use super::chdman::{ChdCdCompressionAlgorithm, ChdDvdCompressionAlgorithm, CHD_HUNK_SIZE_RANGE};
12
use super::database::*;
2-
use super::dolphin::{RVZ_BLOCK_SIZE_RANGE, RVZ_COMPRESSION_LEVEL_RANGE};
3+
use super::dolphin::{RvzCompressionAlgorithm, RVZ_BLOCK_SIZE_RANGE, RVZ_COMPRESSION_LEVEL_RANGE};
34
use super::sevenzip::{SEVENZIP_COMPRESSION_LEVEL_RANGE, ZIP_COMPRESSION_LEVEL_RANGE};
45
use super::util::*;
56
use super::SimpleResult;
@@ -39,16 +40,6 @@ pub enum HashAlgorithm {
3940
Sha1,
4041
}
4142

42-
#[derive(Display, PartialEq, EnumString, VariantNames)]
43-
#[strum(serialize_all = "lowercase")]
44-
pub enum RvzCompressionAlgorithm {
45-
None,
46-
Zstd,
47-
Bzip,
48-
Lzma,
49-
Lzma2,
50-
}
51-
5243
#[derive(PartialEq, EnumString, VariantNames)]
5344
#[strum(serialize_all = "lowercase")]
5445
pub enum SubfolderScheme {
@@ -86,7 +77,13 @@ const CHOICES: phf::Map<&str, &[&str]> = phf_map! {
8677
"REGIONS_ONE_SUBFOLDERS" => SubfolderScheme::VARIANTS,
8778
"RVZ_COMPRESSION_ALGORITHM" => RvzCompressionAlgorithm::VARIANTS,
8879
};
80+
const CHOICE_LISTS: phf::Map<&str, &[&str]> = phf_map! {
81+
"CHD_CD_COMPRESSION_ALGORITHMS" => ChdCdCompressionAlgorithm::VARIANTS,
82+
"CHD_DVD_COMPRESSION_ALGORITHMS" => ChdDvdCompressionAlgorithm::VARIANTS,
83+
};
8984
const INTEGERS: phf::Map<&str, &[usize; 2]> = phf_map! {
85+
"CHD_CD_HUNK_SIZE" => &CHD_HUNK_SIZE_RANGE,
86+
"CHD_DVD_HUNK_SIZE" => &CHD_HUNK_SIZE_RANGE,
9087
"RVZ_BLOCK_SIZE" => &RVZ_BLOCK_SIZE_RANGE,
9188
"RVZ_COMPRESSION_LEVEL" => &RVZ_COMPRESSION_LEVEL_RANGE,
9289
"SEVENZIP_COMPRESSION_LEVEL" => &SEVENZIP_COMPRESSION_LEVEL_RANGE,
@@ -287,14 +284,12 @@ pub async fn set_bool(connection: &mut SqliteConnection, key: &str, value: bool)
287284
};
288285
}
289286

290-
pub async fn get_integer(connection: &mut SqliteConnection, key: &str) -> usize {
287+
pub async fn get_integer(connection: &mut SqliteConnection, key: &str) -> Option<usize> {
291288
find_setting_by_key(connection, key)
292289
.await
293290
.unwrap()
294291
.value
295-
.unwrap()
296-
.parse()
297-
.unwrap()
292+
.map(|value| value.parse().unwrap())
298293
}
299294

300295
async fn set_integer(connection: &mut SqliteConnection, key: &str, value: usize) {
@@ -328,13 +323,26 @@ pub async fn add_to_list(connection: &mut SqliteConnection, key: &str, value: &s
328323
} else {
329324
println!("Value already in list");
330325
}
326+
} else if CHOICE_LISTS.keys().any(|&s| s == key) {
327+
if CHOICE_LISTS.get(key).unwrap().contains(&value) {
328+
let mut list = get_list(connection, key).await;
329+
if !list.contains(&String::from(value)) {
330+
list.push(value.to_owned());
331+
list.sort();
332+
set_list(connection, key, &list).await;
333+
} else {
334+
println!("Value already in list");
335+
}
336+
} else {
337+
println!("Valid choices: {:?}", CHOICE_LISTS.get(key).unwrap());
338+
}
331339
} else {
332340
println!("Only list settings are supported");
333341
}
334342
}
335343

336344
pub async fn remove_from_list(connection: &mut SqliteConnection, key: &str, value: &str) {
337-
if LISTS.contains(&key) {
345+
if LISTS.contains(&key) || CHOICE_LISTS.keys().any(|&s| s == key) {
338346
let mut list = get_list(connection, key).await;
339347
if list.contains(&String::from(value)) {
340348
list.remove(list.iter().position(|v| v == value).unwrap());
@@ -383,12 +391,8 @@ pub async fn set_directory<P: AsRef<Path>>(
383391
};
384392
}
385393

386-
pub async fn get_string(connection: &mut SqliteConnection, key: &str) -> String {
387-
find_setting_by_key(connection, key)
388-
.await
389-
.unwrap()
390-
.value
391-
.unwrap()
394+
pub async fn get_string(connection: &mut SqliteConnection, key: &str) -> Option<String> {
395+
find_setting_by_key(connection, key).await.unwrap().value
392396
}
393397

394398
pub async fn set_string(connection: &mut SqliteConnection, key: &str, value: &str) {

0 commit comments

Comments
 (0)