Skip to content
This repository was archived by the owner on May 10, 2023. It is now read-only.

Commit aa88fa6

Browse files
authored
Merge pull request #37 from yellowcake-org/develop
Parsing and exporting `.map` files, and everything needed for it.
2 parents 6b12a25 + fd04005 commit aa88fa6

File tree

139 files changed

+6600
-467
lines changed

Some content is hidden

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

139 files changed

+6600
-467
lines changed

Cargo.toml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
11
[package]
2-
edition = "2018"
2+
edition = "2021"
33
name = "libycresources"
4-
version = "0.1.0"
4+
version = "0.2.0"
55

66
[[example]]
77
name = "undat"
88

9+
[[example]]
10+
name = "fontview"
11+
12+
[[example]]
13+
name = "frameview"
14+
15+
[[example]]
16+
name = "protoview"
17+
18+
[[example]]
19+
name = "mapview"
20+
21+
[dependencies]
22+
byteorder = "1.2"
23+
924
[dev-dependencies]
10-
clap = "3.0.0-beta.2"
25+
png = "0.17.7"
26+
bmp = "0.5.0"
27+
clap = "3.0.0-beta.5"

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## libycresources
2+
3+
This repository is a part of [Yellowcake](https://github.com/yellowcake-org) project.
4+
5+
### What it does
6+
7+
The library intended to provide instruments for working with original Fallout™ resources, such as `.map`, `.pro`, `.dat` files, and many others, which were used in classic Fallout™ games. This repository also does include example tools, provided for each file format separately, using `examples` targets. Sometimes they are just print out available info, but very often they provide additional functionality, which goes beyond simple examples, like `mapview` tool.
8+
9+
### Why this is useful
10+
11+
It is useful in obvious way for reproducing original game, providing modern, safe and stable codebase to work with original games' resources. It also may serve as a documenation for those, who want to learn about original file formats. And finally, it's example tools could be a good starting point for modders of original games. In very first release it can't do much, though, but it's already better than original tools released by the publisher in what it actually allows to do. For instance, again, `mapview` can be used to create some arts from maps, or guides. In case of significant demand from the community, it might become something bigger, like full-featured map editor, so stay tuned.
12+
13+
### How to get started
14+
15+
Basically you just use Cargo and default Rust tooling to explore different modules and examples. Each file format is represented with different Rust module. It also has a couple of common modules, which provided basic data structures, needed for the game files' formats and abstractions. Examples' targets provide a view on intended way of using the library's functions.
16+
17+
### Where to get help
18+
19+
[GitHub Discussions](https://github.com/yellowcake-org/libycresources/discussions) is a good starting point. GitHub Issues are disabled at the moment, but will be enabled in case of significant activity from community. As for now, I am handling all issues internally, using additional software.
20+
21+
### Who maintains and contributes
22+
23+
Currently, only [@0xceed](https://github.com/0xceed) is working on the library. While help from the community will be _very_ needed in the future, this early in development I need to focus on creating good example of my vision, guidelines and many, many more.

examples/fontview/main.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use std::fs::File;
2+
3+
use clap::Parser;
4+
5+
use libycresources::formats::aaf;
6+
7+
pub(crate) mod print;
8+
9+
#[derive(Parser)]
10+
#[clap(name = "fontview", version)]
11+
struct Options {
12+
/// Path to the input font file (.aaf)
13+
#[clap(short, long)]
14+
input: String,
15+
#[clap(subcommand)]
16+
action: Action,
17+
}
18+
19+
#[derive(Parser)]
20+
enum Action {
21+
/// Prints all glyphs from specified font
22+
Dump,
23+
/// Prints specified string with glyphs from font
24+
Print(Print),
25+
}
26+
27+
#[derive(Parser)]
28+
struct Print {
29+
string: String,
30+
}
31+
32+
fn main() {
33+
let options = Options::parse();
34+
35+
let file = match File::open(&options.input) {
36+
Err(error) => {
37+
eprintln!("Couldn't open input file: {:?}", error);
38+
return;
39+
}
40+
Ok(value) => value,
41+
};
42+
43+
let mut reader = std::io::BufReader::with_capacity(1 * 1024 * 1024, file);
44+
45+
let font = match aaf::parse::font(&mut reader) {
46+
Err(error) => {
47+
eprintln!("Error occurred: {:?}", error);
48+
return;
49+
}
50+
Ok(value) => value,
51+
};
52+
53+
match options.action {
54+
Action::Dump => {
55+
println!("Line height: {:?}", font.height);
56+
println!("Vertical spacing: {:?}", font.spacing.vertical);
57+
println!("Horizontal spacing: {:?}", font.spacing.horizontal);
58+
59+
println!();
60+
61+
for glyph in font.glyphs {
62+
print::glyph(&glyph);
63+
}
64+
}
65+
Action::Print(arguments) => {
66+
for char in arguments.string.chars() {
67+
if char.is_ascii() {
68+
if let Some(glyph) = &font.glyphs.get(char as usize) {
69+
print::glyph(&glyph);
70+
} else {
71+
eprintln!("Encountered char, which is absent in provided font. Aborting.");
72+
return;
73+
}
74+
} else {
75+
eprintln!("Non-ASCII char was found within provided string. Aborting.");
76+
return;
77+
}
78+
}
79+
}
80+
}
81+
}

examples/fontview/print.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use libycresources::formats::aaf;
2+
3+
pub fn glyph(glyph: &aaf::Glyph) {
4+
if !glyph.dots.is_empty() {
5+
for row in 0..glyph.height {
6+
for column in 0..glyph.width {
7+
let index = (row * glyph.width + column) as usize;
8+
9+
if let Some(pixel) = &glyph.dots.get(index) {
10+
let levels = [' ', '.', ':', '-', '=', '+', '*', '#', '%', '@'];
11+
let index = levels.len() * pixel.value / (pixel.scale.end - pixel.scale.start);
12+
13+
if let Some(level) = levels.get(index) {
14+
print!("{:}", level);
15+
} else {
16+
eprintln!("Encountered a pixel, which brightness level is of bounds.")
17+
}
18+
} else {
19+
eprintln!("Encountered a pixel located out of bounds.")
20+
}
21+
}
22+
23+
if row != glyph.height { println!(); }
24+
}
25+
}
26+
}

examples/frameview/main.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use std::fs::File;
2+
3+
use clap::Parser;
4+
5+
use libycresources::formats::{frm, pal};
6+
7+
pub mod render;
8+
9+
#[derive(Parser)]
10+
#[clap(name = "frameview", version)]
11+
struct Options {
12+
/// Path to the input frame file (.frm, .fr0-5)
13+
#[clap(short, long)]
14+
input: String,
15+
#[clap(subcommand)]
16+
action: Action,
17+
}
18+
19+
#[derive(Parser)]
20+
enum Action {
21+
/// Prints summary info about contents of the file
22+
Info,
23+
/// Renders frame contents into .bmp file(s) in specified directory
24+
Render(Render),
25+
}
26+
27+
#[derive(Parser)]
28+
struct Render {
29+
/// Path to the input palette file (.pal)
30+
#[clap(short, long)]
31+
palette: String,
32+
/// Output directory path
33+
#[clap(short, long)]
34+
directory: String,
35+
}
36+
37+
fn main() {
38+
let options = Options::parse();
39+
40+
let file = match File::open(&options.input) {
41+
Err(error) => {
42+
eprintln!("Couldn't open input file: {:?}", error);
43+
return;
44+
}
45+
Ok(value) => value,
46+
};
47+
48+
let mut reader = std::io::BufReader::with_capacity(1 * 1024 * 1024, file);
49+
50+
let sprite = match frm::parse::sprite(&mut reader) {
51+
Err(error) => {
52+
eprintln!("Error occurred: {:?}", error);
53+
return;
54+
}
55+
Ok(value) => value,
56+
};
57+
58+
match options.action {
59+
Action::Info => {
60+
println!("FPS: {:}.", sprite.fps);
61+
println!("Frames per orientation: {:}.", sprite.count);
62+
println!("Unique animations: {:}.", sprite.animations.len());
63+
println!("Orientated animations: {:?}.", sprite.orientations);
64+
65+
println!();
66+
67+
for (index, animation) in sprite.animations.iter().enumerate() {
68+
println!(
69+
"Animation {:} has {:} frame(s).",
70+
index,
71+
animation.frames.len()
72+
);
73+
}
74+
}
75+
Action::Render(arguments) => {
76+
let output = std::path::Path::new(&arguments.directory);
77+
78+
if !output.exists() {
79+
eprintln!("Output path does not exist. Aborting.");
80+
return;
81+
}
82+
83+
if !output.is_dir() {
84+
eprintln!("Output path is not a directory. Aborting.");
85+
return;
86+
}
87+
88+
let directory_name = match std::path::Path::new(&options.input).file_stem() {
89+
Some(value) => value,
90+
None => {
91+
eprintln!("Couldn't determine frame output filename.");
92+
return;
93+
}
94+
};
95+
96+
let path = output.join(directory_name);
97+
98+
let mut reader = std::io::BufReader::with_capacity(
99+
1 * 1024 * 1024,
100+
match File::open(arguments.palette) {
101+
Err(error) => {
102+
eprintln!("Couldn't open palette file: {:?}", error);
103+
return;
104+
}
105+
Ok(value) => value,
106+
},
107+
);
108+
109+
let palette = match pal::parse::palette(&mut reader) {
110+
Err(error) => {
111+
eprintln!("Error occurred: {:?}", error);
112+
return;
113+
}
114+
Ok(value) => value,
115+
};
116+
117+
for (index, animation) in sprite.animations.iter().enumerate() {
118+
let animation_dir = path.join(index.to_string());
119+
120+
match std::fs::create_dir_all(&animation_dir) {
121+
Err(error) => {
122+
eprintln!(
123+
"Couldn't create animation output directory tree: {:?}, error: {:}",
124+
animation_dir, error
125+
);
126+
return;
127+
}
128+
Ok(_) => (),
129+
};
130+
131+
for (index, frame) in animation.frames.iter().enumerate() {
132+
let frame_path = animation_dir.join(index.to_string()).with_extension("bmp");
133+
134+
match render::frame(&frame, &palette) {
135+
Ok(image) => {
136+
match image.save(frame_path) {
137+
Ok(_) => {}
138+
Err(error) => {
139+
eprintln!("Couldn't write output file: {:}", error);
140+
return;
141+
}
142+
}
143+
}
144+
Err(error) => {
145+
eprintln!("Couldn't write output file: {:?}", error);
146+
return;
147+
}
148+
}
149+
}
150+
}
151+
}
152+
}
153+
}

examples/frameview/render.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use libycresources::formats::{frm, pal};
2+
3+
#[derive(Debug)]
4+
pub enum Error { Corrupted }
5+
6+
pub fn frame(frame: &frm::Frame, palette: &pal::Palette) -> Result<bmp::Image, Error> {
7+
let mut image = bmp::Image::new(frame.size.width as u32, frame.size.height as u32);
8+
9+
for (x, y) in image.coordinates() {
10+
let index = (frame.size.width as u32 * y + x) as usize;
11+
12+
if let Some(&index) = frame.indexes.get(index) {
13+
if let Some(&index) = frame.indexes.get(index as usize) {
14+
if let Some(pixel) = &palette.colors.get(index as usize) {
15+
image.set_pixel(
16+
x,
17+
y,
18+
match pixel {
19+
None => bmp::Pixel::new(0, 0, 0),
20+
Some(color) => {
21+
let red = ((color.red.value as usize * (u8::MAX as usize + 1)) / color.red.scale.len()) as u8;
22+
let green = ((color.green.value as usize * (u8::MAX as usize + 1)) / color.green.scale.len()) as u8;
23+
let blue = ((color.blue.value as usize * (u8::MAX as usize + 1)) / color.blue.scale.len()) as u8;
24+
25+
bmp::Pixel::new(red, green, blue)
26+
}
27+
},
28+
);
29+
} else {
30+
return Err(Error::Corrupted);
31+
}
32+
} else {
33+
return Err(Error::Corrupted);
34+
}
35+
} else {
36+
return Err(Error::Corrupted);
37+
}
38+
}
39+
40+
Ok(image)
41+
}

examples/mapview/cli.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use std::path::PathBuf;
2+
3+
use clap::Parser;
4+
5+
use export::Export;
6+
7+
pub(crate) mod export;
8+
9+
#[derive(Parser)]
10+
#[clap(name = "mapview", version)]
11+
pub(crate) struct Options {
12+
/// Path to the input map file (.map)
13+
#[clap(short, long)]
14+
pub(crate) input: PathBuf,
15+
/// Path to the root resources directory
16+
#[clap(short, long)]
17+
pub(crate) resources: PathBuf,
18+
#[clap(subcommand)]
19+
pub(crate) action: Action,
20+
}
21+
22+
#[derive(Parser)]
23+
pub(crate) enum Action {
24+
/// Prints out all available info about map
25+
Dump,
26+
/// Renders the map into .bmp file
27+
Export(Export),
28+
}

0 commit comments

Comments
 (0)