RD-1: Added an inital port of d_main.c/d_main.h. Added some other basic files #1
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
# ---> VisualStudioCode
|
# ---> VisualStudioCode
|
||||||
.vscode/*
|
.vscode/
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
@@ -12,3 +12,10 @@
|
|||||||
# Built Visual Studio Code Extensions
|
# Built Visual Studio Code Extensions
|
||||||
*.vsix
|
*.vsix
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|
||||||
|
/WADS/*
|
||||||
|
|||||||
44
.vscode/launch.json
vendored
Normal file
44
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug Executable",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=RustyDoom"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "RustyDoom",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"./WADS/DOOM.WAD", "-dev", "@configs.txt", "-nomonsters", "-file", "test.WAD", "test2.WAD", "-skill", "3"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Release Executable",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--release",
|
||||||
|
"--bin=RustyDoom"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "RustyDoom",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"./WADS/DOOM.WAD", "arg2", "-fast"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "RustyDoom"
|
||||||
|
version = "0.1.0"
|
||||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "RustyDoom"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
1
configs.txt
Normal file
1
configs.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-warp 1 5
|
||||||
554
src/d_main/mod.rs
Normal file
554
src/d_main/mod.rs
Normal file
@@ -0,0 +1,554 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::path::{self};
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
use crate::m_argv;
|
||||||
|
use crate::m_argv::M_CheckParm;
|
||||||
|
use crate::m_argv::M_GetOptionalArgumentValueByArgument;
|
||||||
|
use crate::m_argv::PARM_NOT_FOUND;
|
||||||
|
|
||||||
|
use crate::doomdef::{
|
||||||
|
VERSION,
|
||||||
|
// D_DEVSTR,
|
||||||
|
GameMode,
|
||||||
|
Skill,
|
||||||
|
DOOMGLOBALS
|
||||||
|
};
|
||||||
|
|
||||||
|
fn flush_and_wait() {
|
||||||
|
let _ = io::stdout().flush();
|
||||||
|
|
||||||
|
// Replaces C's getchar() by waiting for the Enter key
|
||||||
|
let mut buffer = String::new();
|
||||||
|
let _ = io::stdin().read_line(&mut buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn D_AddFile(file: &str) {
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.wadfiles.push(file.to_string());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn D_ProcessFileParameters(p_file: i32) {
|
||||||
|
let args = m_argv::M_GetAll();
|
||||||
|
let pos = p_file as usize;
|
||||||
|
// Find the index of "-file"
|
||||||
|
// Look at everything after the index
|
||||||
|
for &arg in args.iter().skip(pos + 1) {
|
||||||
|
if arg.starts_with('-') {
|
||||||
|
break; // Stop if we hit another flag like -debug
|
||||||
|
}
|
||||||
|
D_AddFile(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a Response file for additional command line arguments
|
||||||
|
/// A response file is a text file that may store additional command line parameters.
|
||||||
|
/// The file may have any name that is valid to the system, optionally with an extension.
|
||||||
|
/// The parameters are typed as in the command line (-episode 2, for example),
|
||||||
|
/// but one per line, where up to 100 lines may be used.
|
||||||
|
/// The additional parameters may be disabled for later use by placing a vertical bar
|
||||||
|
/// (the | character) between the prefixing dash (-) and the rest of the parameter name.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn FindResponseFile(args: &mut Vec<String>) -> Result<(), String> {
|
||||||
|
const MAX_ARGVS: usize = 100;
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < args.len() {
|
||||||
|
let arg = &args[i];
|
||||||
|
if arg.len() > 0 && arg.as_bytes()[0] == b'@' {
|
||||||
|
|
||||||
|
let f_name = &arg[1..]; // remove @
|
||||||
|
let f_content: String;
|
||||||
|
|
||||||
|
match fs::read_to_string(f_name) {
|
||||||
|
Ok(content) => f_content = content,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Found response file {}! (Error reading: {})", f_name, e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!("Found response file {}!", f_name);
|
||||||
|
|
||||||
|
let f_args: Vec<String> = f_content
|
||||||
|
.split_whitespace()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
args.remove(i);
|
||||||
|
|
||||||
|
for (offset, new_arg) in f_args.into_iter().enumerate() {
|
||||||
|
args.insert(i+offset, new_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.len() > MAX_ARGVS {
|
||||||
|
eprintln!("Warning: Recieved {} total arguments, exceeding original MAX_ARGSV limit {}", args.len(), MAX_ARGVS)
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{} command-line args:", args.len());
|
||||||
|
for arg in args {
|
||||||
|
println!("\t{}", arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i +=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn IdentifyVersion() -> Result<(), String> {
|
||||||
|
|
||||||
|
if M_CheckParm("-shdev") != PARM_NOT_FOUND {
|
||||||
|
eprintln!("-shddev param not implemented in this port");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
if M_CheckParm("-regdev") != PARM_NOT_FOUND {
|
||||||
|
eprintln!("-regdev param not implemented in this port");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
if M_CheckParm("-comdev") != PARM_NOT_FOUND {
|
||||||
|
eprintln!("-comdev param not implemented in this port");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let iwad_arg = m_argv::M_GetPositionalArgv()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
let Ok(iwad_path) = path::absolute(std::path::PathBuf::from(iwad_arg)) else {
|
||||||
|
eprintln!("FATAL: Could not resolve the IWAD path: {}", iwad_arg);
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
if !iwad_path.is_file() {
|
||||||
|
eprintln!("FATAL: IWAD file not found: {}", iwad_path.display());
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = iwad_path
|
||||||
|
.file_name()
|
||||||
|
.and_then(|f| f.to_str())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
eprintln!("FATAL: Unable to get WAD filename from path: {:?}", iwad_path);
|
||||||
|
std::process::exit(1);
|
||||||
|
})
|
||||||
|
.to_ascii_lowercase();
|
||||||
|
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.language = 0;
|
||||||
|
|
||||||
|
g.gamemode = match filename.as_str() {
|
||||||
|
|
||||||
|
"doom1.wad" => GameMode::Shareware,
|
||||||
|
|
||||||
|
"doom.wad" => GameMode::Registered,
|
||||||
|
|
||||||
|
"doomu.wad" | "ultimate.wad" => {
|
||||||
|
GameMode::Retail
|
||||||
|
}
|
||||||
|
|
||||||
|
"doom2.wad" => GameMode::Commercial,
|
||||||
|
|
||||||
|
"doom2f.wad" => {
|
||||||
|
g.language = 1;
|
||||||
|
GameMode::Commercial
|
||||||
|
}
|
||||||
|
|
||||||
|
"tnt.wad" => GameMode::Commercial,
|
||||||
|
|
||||||
|
"plutonia.wad" => GameMode::Commercial,
|
||||||
|
|
||||||
|
_ => GameMode::Indetermined,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
D_AddFile(&iwad_arg);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts the given string into a valid [u8; 128] array
|
||||||
|
fn to_fixed_bytes(input: &str) -> [u8; 128] {
|
||||||
|
let mut b = [0u8; 128];
|
||||||
|
let src = input.as_bytes();
|
||||||
|
let len = src.len().min(128);
|
||||||
|
b[..len].copy_from_slice(&src[..len]);
|
||||||
|
b
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn D_DoomMain() {
|
||||||
|
// get cli args from env
|
||||||
|
// Original doom uses a static module argc, argv
|
||||||
|
// Collects arguments into a Vector of Strings
|
||||||
|
// Process args
|
||||||
|
let mut args: Vec<String> = env::args().collect();
|
||||||
|
let _ = FindResponseFile(&mut args);
|
||||||
|
|
||||||
|
m_argv::init(args);
|
||||||
|
let _ = IdentifyVersion();
|
||||||
|
|
||||||
|
// setbuf (stdout, NULL); // TODO: Investigate this
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.modifiedgame = false;
|
||||||
|
g.nomonsters = M_CheckParm("-nomonsters") != 0;
|
||||||
|
g.respawnparm = M_CheckParm("-respawn") != 0;
|
||||||
|
g.fastparm = M_CheckParm("-fastparm") != 0;
|
||||||
|
g.devparm = M_CheckParm("-devparam") != 0;
|
||||||
|
g.deathmatch = if M_CheckParm("-altdeath") != 0 {
|
||||||
|
2
|
||||||
|
} else if M_CheckParm("-deathmatch") != 0 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
// TODO: fix the original issue to handle TnT + Plutonia
|
||||||
|
g.title = match g.gamemode {
|
||||||
|
GameMode::Retail => to_fixed_bytes(&format!(" The Ultimate DOOM Startup v{}.{} ", VERSION / 100, VERSION % 100)),
|
||||||
|
GameMode::Shareware => to_fixed_bytes(&format!(" DOOM Shareware Startup v{}.{} ", VERSION / 100, VERSION % 100)),
|
||||||
|
GameMode::Registered => to_fixed_bytes(&format!(" DOOM Registered Startup v{}.{} ", VERSION / 100, VERSION % 100)),
|
||||||
|
GameMode::Commercial => to_fixed_bytes(&format!(" DOOM 2: Hell on Earth v{}.{} ", VERSION / 100, VERSION % 100)),
|
||||||
|
GameMode::Indetermined => to_fixed_bytes(&format!(" Public DOOM - v{}.{} ", VERSION / 100, VERSION % 100)),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{}", String::from_utf8_lossy(&g.title));
|
||||||
|
|
||||||
|
if g.devparm {
|
||||||
|
eprintln!("devparm not implemented in this port");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check and parse turbo options if applicable
|
||||||
|
if M_CheckParm("-turbo") != PARM_NOT_FOUND {
|
||||||
|
let mut scale :i32 = 200;
|
||||||
|
// TODO: Resolve externs (int should be fixed_t) or FixedPoint in our case
|
||||||
|
// extern int forwardmove[2];
|
||||||
|
// extern int sidemove[2];
|
||||||
|
if let Some(arg_str) = M_GetOptionalArgumentValueByArgument("-turbo") {
|
||||||
|
if let Ok(parsed_val) = arg_str.parse() {
|
||||||
|
scale = parsed_val;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scale = scale.clamp(10, 400);
|
||||||
|
println!("turbo scale: {}%", scale);
|
||||||
|
// TODO: Resolve externs
|
||||||
|
//forwardmove[0] = forwardmove[0]*scale/100;
|
||||||
|
//forwardmove[1] = forwardmove[1]*scale/100;
|
||||||
|
//sidemove[0] = sidemove[0]*scale/100;
|
||||||
|
//sidemove[1] = sidemove[1]*scale/100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if M_CheckParm ("-wart") != PARM_NOT_FOUND {
|
||||||
|
eprintln!("-wart is not implemented in this port");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut p = M_CheckParm("-file");{
|
||||||
|
if p != PARM_NOT_FOUND {
|
||||||
|
// Params after -file are wadfile/lump names
|
||||||
|
// Continue collecting until end of params or another `-` preceded param
|
||||||
|
DOOMGLOBALS::with_mut(|g|{
|
||||||
|
g.modifiedgame = true;
|
||||||
|
});
|
||||||
|
D_ProcessFileParameters(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Playing a dmeo?
|
||||||
|
p = M_CheckParm("-playdemo");
|
||||||
|
if p != PARM_NOT_FOUND {
|
||||||
|
p = M_CheckParm("-timedemo");
|
||||||
|
}
|
||||||
|
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
if let Some(demo_file) = m_argv::M_GetOptionalArgumentValueByIndex(p+1) {
|
||||||
|
D_AddFile(demo_file);
|
||||||
|
println!("Playing demo {}.lmp.\n", demo_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the skill, episode and map from the parms if given
|
||||||
|
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.startskill = Skill::sk_medium as i32;
|
||||||
|
g.startepisode = 1;
|
||||||
|
g.startmap = 1;
|
||||||
|
g.autostart = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
p = M_CheckParm("-skill");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
if let Some(skill_val) = m_argv::M_GetOptionalArgumentValueByIndex(p + 1) {
|
||||||
|
if let Some(first_char) = skill_val.chars().next() {
|
||||||
|
let start_skill = (first_char as i32).wrapping_sub('1' as i32);
|
||||||
|
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.startskill = start_skill;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-episode");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
if let Some(episode_val) = m_argv::M_GetOptionalArgumentValueByIndex(p + 1) {
|
||||||
|
if let Some(first_char) = episode_val.chars().next() {
|
||||||
|
let start_episode = (first_char as i32).wrapping_sub('0' as i32);
|
||||||
|
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.startepisode = start_episode;
|
||||||
|
g.startmap = 1;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-timer");
|
||||||
|
let is_deathmath = DOOMGLOBALS::with_ref(|g| g.deathmatch);
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 && is_deathmath > 0 {
|
||||||
|
if let Some(timer_val) = m_argv::M_GetOptionalArgumentValueByIndex(p + 1) {
|
||||||
|
match timer_val.parse::<u32>() {
|
||||||
|
Ok(time) => {
|
||||||
|
if time > 1 {
|
||||||
|
println!("Levels will end after {} minutes", time)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
println!("Levels will end after {} minute", time)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("value for -timer is not a valid number of minutes");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-avg");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 && is_deathmath > 0 {
|
||||||
|
println!("Austin Virtual Gaming: Levels will end after 20 minutes\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-warp");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
let g_mode = DOOMGLOBALS::with_ref(|g| g.gamemode);
|
||||||
|
if g_mode == GameMode::Commercial {
|
||||||
|
if let Some(gamemode_val) = m_argv::M_GetOptionalArgumentValueByIndex(p + 1) {
|
||||||
|
match gamemode_val.parse::<i32>() {
|
||||||
|
Ok(mapstart) => {
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.startmap = mapstart;
|
||||||
|
g.autostart = true;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("value for -warp is not a valid map number");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(episode_val) = m_argv::M_GetOptionalArgumentValueByIndex(p + 1) {
|
||||||
|
if let Some(map_val) = m_argv::M_GetOptionalArgumentValueByIndex(p + 2) {
|
||||||
|
match (episode_val.parse::<i32>(), map_val.parse::<i32>()) {
|
||||||
|
(Ok(startepisode), Ok(startmap)) => {
|
||||||
|
DOOMGLOBALS::with_mut(|g| {
|
||||||
|
g.startepisode = startepisode;
|
||||||
|
g.startmap = startmap;
|
||||||
|
g.autostart = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(Ok(_), Err(_)) => {
|
||||||
|
eprintln!("value for starting map in -warp is not a valid number");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
(Err(_), Ok(_)) => {
|
||||||
|
eprintln!("value for starting episode in -warp is not a valid number");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
(Err(_), Err(_)) => {
|
||||||
|
eprintln!("Values for starting episode and map in -warp are not a valid numbers");
|
||||||
|
eprintln!("Usage: RustyDoom <iwad-file> <Options>");
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement subsytem inits
|
||||||
|
/*
|
||||||
|
println!("V_Init: allocate screens.");
|
||||||
|
V_Init();
|
||||||
|
|
||||||
|
println!("M_LoadDefaults: Load system defaults.")
|
||||||
|
M_LoadDefaults();
|
||||||
|
|
||||||
|
println!("Z_init: Init zone memory allocation daemon.");
|
||||||
|
Z_Init();
|
||||||
|
|
||||||
|
println!("W_Init: Init WADfiles");
|
||||||
|
W_InitMultipleFiles(wadfiles);
|
||||||
|
*/
|
||||||
|
|
||||||
|
let is_modified = DOOMGLOBALS::with_ref(|g| g.modifiedgame);
|
||||||
|
|
||||||
|
if is_modified {
|
||||||
|
let iwads_lumps_to_check: Vec<&str> = vec!["e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9",
|
||||||
|
"e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9",
|
||||||
|
"dphoof","bfgga0","heada1","cybra1","spida1d1"];
|
||||||
|
|
||||||
|
let g_mode = DOOMGLOBALS::with_ref(|g| g.gamemode);
|
||||||
|
|
||||||
|
if g_mode == GameMode::Shareware {
|
||||||
|
// I_ERROR?
|
||||||
|
}
|
||||||
|
|
||||||
|
if g_mode == GameMode::Registered {
|
||||||
|
for lump in iwads_lumps_to_check.iter().take(24) {
|
||||||
|
// TODO: Implement check
|
||||||
|
// for (i = 0;i < 23; i++)
|
||||||
|
// if (W_CheckNumForName(name[i])<0)
|
||||||
|
// I_Error("\nThis is not the registered version.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the modifed game header
|
||||||
|
|
||||||
|
if is_modified {
|
||||||
|
print!("===========================================================================\n\
|
||||||
|
ATTENTION: This version of DOOM has been modified. If you would like to\n\
|
||||||
|
get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n\
|
||||||
|
You will not receive technical support for modified games.\n\
|
||||||
|
press enter to continue\n\
|
||||||
|
===========================================================================\n"
|
||||||
|
);
|
||||||
|
flush_and_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
match DOOMGLOBALS::with_ref(|g| g.gamemode) {
|
||||||
|
GameMode::Shareware | GameMode::Indetermined => {
|
||||||
|
print!("===========================================================================\n\
|
||||||
|
Shareware!\n\
|
||||||
|
===========================================================================\n")
|
||||||
|
}
|
||||||
|
GameMode::Registered | GameMode::Retail | GameMode::Commercial => {
|
||||||
|
print!("===========================================================================\n\
|
||||||
|
Commercial product - do not distribute!\n\
|
||||||
|
Please report software piracy to the SPA: 1-800-388-PIR8\n\
|
||||||
|
===========================================================================\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement other inits
|
||||||
|
/*
|
||||||
|
println!("M_Init: Init miscellaneous info.");
|
||||||
|
M_Init();
|
||||||
|
|
||||||
|
println!("R_Init: Init DOOM refresh daemon - ")
|
||||||
|
R_Init();
|
||||||
|
|
||||||
|
println!("P_Init: Init Playloop state.");
|
||||||
|
P_Init();
|
||||||
|
|
||||||
|
println!("I_Init: Setting up machine state.");
|
||||||
|
I_Init()
|
||||||
|
|
||||||
|
println!("D_CheckNetGame: Checking network game status.");
|
||||||
|
D_CheckNetGame()
|
||||||
|
|
||||||
|
println!("S_Init: Setting up sound.");
|
||||||
|
S_Init (snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ );
|
||||||
|
|
||||||
|
printf ("HU_Init: Setting up heads up display.\n");
|
||||||
|
HU_Init ();
|
||||||
|
|
||||||
|
printf ("ST_Init: Init status bar.\n");
|
||||||
|
ST_Init ();
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check for a driver that wants intermission stats
|
||||||
|
|
||||||
|
|
||||||
|
p = M_CheckParm("-statcopy");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
// TODO: implement for statistics driver
|
||||||
|
/*extern void* statcopy;
|
||||||
|
|
||||||
|
statcopy = (void*)atoi(myargv[p+1]);
|
||||||
|
printf ("External statistics registered.\n");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the approiate gaem based on parms
|
||||||
|
p = M_CheckParm("-record");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
// TODO: Implement G_RecordDemo
|
||||||
|
//G_RecordDemo(m_argv::M_GetOptionalArgumentValueByIndex(p+1));
|
||||||
|
DOOMGLOBALS::with_mut(|g| g.autostart = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-playdemo");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
// TODO: Implement G_DeferedPlayDemo
|
||||||
|
// G_DeferedPlayDemo (myargv[p+1]);
|
||||||
|
// D_DoomLoop (); // never returns
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-timedemo");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
// TODO: Implement G_TimeDemo
|
||||||
|
// G_TimeDemo (myargv[p+1]);
|
||||||
|
// D_DoomLoop (); // never returns
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-loadgame");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
// TODO: Implement loadgame param G_LoadGame
|
||||||
|
// G_TimeDemo (myargv[p+1]);
|
||||||
|
// D_DoomLoop (); // never returns
|
||||||
|
if M_CheckParm("-cdrom") != PARM_NOT_FOUND {
|
||||||
|
// sprintf(file, "c:\\doomdata\\"SAVEGAMENAME"%c.dsg",myargv[p+1][0]);
|
||||||
|
} else {
|
||||||
|
// sprintf(file, SAVEGAMENAME"%c.dsg",myargv[p+1][0]);
|
||||||
|
}
|
||||||
|
// G_LoagGame(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
p = M_CheckParm("-ga_loadgame");
|
||||||
|
if p != PARM_NOT_FOUND && p < m_argv::M_GetArgC() - 1 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let g_action = DOOMGLOBALS::with_ref(|g| g.gameaction);
|
||||||
|
|
||||||
|
// TODO: Impement Game actions
|
||||||
|
// if g_action != ga_loadgame {
|
||||||
|
// let autostart = DOOMGLOBALS::with_ref(|g| g.autostart);
|
||||||
|
// let netgame = DOOMGLOBALS::with_ref(|G| G.)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// D_DoomLoop()
|
||||||
|
|
||||||
|
}
|
||||||
147
src/doomdef.rs
Normal file
147
src/doomdef.rs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||||
|
|
||||||
|
|
||||||
|
pub const VERSION:i32 = 110;
|
||||||
|
pub const D_DEVSTR: &str = "Development mode ON.";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum GameMode {
|
||||||
|
Shareware,
|
||||||
|
Registered,
|
||||||
|
Retail,
|
||||||
|
Commercial,
|
||||||
|
Indetermined
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum GameState {
|
||||||
|
Level,
|
||||||
|
Intermission,
|
||||||
|
Finale,
|
||||||
|
Demoscreen,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
#[repr(i32)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum Skill {
|
||||||
|
sk_baby = 0,
|
||||||
|
sk_easy = 1,
|
||||||
|
sk_medium = 2,
|
||||||
|
sk_hard = 3,
|
||||||
|
sk_nightmare = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DoomGlobalState {
|
||||||
|
pub devparm: bool,
|
||||||
|
pub nomonsters: bool,
|
||||||
|
pub respawnparm: bool,
|
||||||
|
pub fastparm: bool,
|
||||||
|
pub drone: bool,
|
||||||
|
pub singletics: bool,
|
||||||
|
pub advancedemo: bool,
|
||||||
|
pub automapactive: bool,
|
||||||
|
pub scaledviewwidth: usize,
|
||||||
|
pub viewheight: usize,
|
||||||
|
pub inhelpscreens: bool,
|
||||||
|
pub paused: bool,
|
||||||
|
pub viewactive: bool,
|
||||||
|
pub menuactive: bool,
|
||||||
|
pub gameaction: bool,
|
||||||
|
pub usergame: bool,
|
||||||
|
pub autostart: bool,
|
||||||
|
pub demorecording: bool,
|
||||||
|
pub modifiedgame: bool,
|
||||||
|
pub deathmatch: u32,
|
||||||
|
pub language: u32,
|
||||||
|
pub singledemo: bool,
|
||||||
|
pub consoleplayer: u32,
|
||||||
|
pub maketic: u32,
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
pub wadfile: [u8; 1024],
|
||||||
|
pub mapdir: [u8; 1024],
|
||||||
|
pub basedefault: [u8; 1024],
|
||||||
|
pub title: [u8; 128],
|
||||||
|
|
||||||
|
// System State
|
||||||
|
pub gamemode: GameMode,
|
||||||
|
pub startskill: i32,
|
||||||
|
pub startepisode: i32,
|
||||||
|
pub startmap: i32,
|
||||||
|
pub wipegamestate: GameState,
|
||||||
|
pub gametic: u32,
|
||||||
|
|
||||||
|
// Lists
|
||||||
|
pub wadfiles: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DoomGlobalState {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self {
|
||||||
|
devparm: false,
|
||||||
|
nomonsters: false,
|
||||||
|
respawnparm: false,
|
||||||
|
fastparm: false,
|
||||||
|
drone: false,
|
||||||
|
singletics: false,
|
||||||
|
advancedemo: false,
|
||||||
|
automapactive: false,
|
||||||
|
scaledviewwidth: 0,
|
||||||
|
viewheight: 0,
|
||||||
|
inhelpscreens: false,
|
||||||
|
paused: false,
|
||||||
|
viewactive: true,
|
||||||
|
menuactive: false,
|
||||||
|
gameaction: false, // ga_nothing
|
||||||
|
usergame: true,
|
||||||
|
autostart: false,
|
||||||
|
demorecording: false,
|
||||||
|
modifiedgame: false,
|
||||||
|
deathmatch: 0,
|
||||||
|
language: 0, // english
|
||||||
|
singledemo: false,
|
||||||
|
consoleplayer: 0,
|
||||||
|
maketic: 0,
|
||||||
|
wadfile: [0; 1024],
|
||||||
|
mapdir: [0; 1024],
|
||||||
|
basedefault: [0; 1024],
|
||||||
|
title: [0; 128],
|
||||||
|
gamemode: GameMode::Indetermined,
|
||||||
|
startskill: Skill::sk_medium as i32,
|
||||||
|
startepisode: 1,
|
||||||
|
startmap: 1,
|
||||||
|
wipegamestate: GameState::Demoscreen,
|
||||||
|
gametic: 0,
|
||||||
|
wadfiles: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
// We promise not to be naughty and access this from another thread :)
|
||||||
|
static GLOBALS: RefCell<DoomGlobalState> = RefCell::new(DoomGlobalState::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DOOMGLOBALS;
|
||||||
|
|
||||||
|
impl DOOMGLOBALS {
|
||||||
|
/// Provides mutable access to the globals
|
||||||
|
pub fn with_mut<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut DoomGlobalState) -> R
|
||||||
|
{
|
||||||
|
GLOBALS.with(|g| f(&mut g.borrow_mut()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides reference only access to the globals
|
||||||
|
pub fn with_ref<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&DoomGlobalState) -> R
|
||||||
|
{
|
||||||
|
GLOBALS.with(|g| f(&g.borrow()))
|
||||||
|
}
|
||||||
|
}
|
||||||
101
src/m_argv.rs
Normal file
101
src/m_argv.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// Stores argc and argv from the original Doom
|
||||||
|
// M_CheckParm as well as a helper to get a param value (e.g -file MyWad.wad)
|
||||||
|
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static MYARGC: OnceLock<usize> = OnceLock::new();
|
||||||
|
|
||||||
|
static MYARGV: OnceLock<Vec<String>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub const PARM_NOT_FOUND: i32 = 0;
|
||||||
|
|
||||||
|
pub fn init(args: Vec<String>) {
|
||||||
|
MYARGC.set(args.len()).ok();
|
||||||
|
MYARGV.set(args).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of the pass CLI arguments at the position specified by `index`
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_GetPositionalArgv() -> Option<&'static str> {
|
||||||
|
let args = MYARGV.get()?;
|
||||||
|
|
||||||
|
args.iter()
|
||||||
|
.skip(1)
|
||||||
|
.find(|arg| {
|
||||||
|
!arg.starts_with('-')
|
||||||
|
})
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of a optional argument specified by the argument
|
||||||
|
///
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_GetOptionalArgumentValueByArgument(arg: &str) -> Option<&'static str> {
|
||||||
|
|
||||||
|
let idx: usize = M_CheckParm(arg) as usize;
|
||||||
|
|
||||||
|
if idx == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let argv: &Vec<String> = MYARGV.get()?;
|
||||||
|
let idx_val = idx + 1;
|
||||||
|
if idx_val > argv.len() - 1 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
Some(argv[idx_val].as_str())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value of a optional argument specified by the argument
|
||||||
|
///
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_GetOptionalArgumentValueByIndex(index: i32) -> Option<&'static str> {
|
||||||
|
|
||||||
|
let idx = index as usize;
|
||||||
|
if idx == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let argv: &Vec<String> = MYARGV.get()?;
|
||||||
|
let idx_val = idx + 1;
|
||||||
|
if idx_val > argv.len() - 1 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
Some(argv[idx_val].as_str())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks for the given paramter in the program command line arugments
|
||||||
|
/// Returns the argument number or zero if not present
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_CheckParm(c_str: &str) -> i32 {
|
||||||
|
|
||||||
|
let args = match MYARGV.get() {
|
||||||
|
Some(a) => a,
|
||||||
|
None => return 0, // Not initialized yet
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 1..args.len() {
|
||||||
|
if args[i] == c_str {
|
||||||
|
return i as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PARM_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all the parameters of myargv
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_GetAll() -> Vec<&'static str> {
|
||||||
|
match MYARGV.get() {
|
||||||
|
Some(args) => args.iter().map(|s| s.as_str()).collect(),
|
||||||
|
None => Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of arguments passed to the program
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_GetArgC() -> i32 {
|
||||||
|
*MYARGC.get().unwrap() as i32
|
||||||
|
}
|
||||||
16
src/main.rs
Normal file
16
src/main.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
mod d_main;
|
||||||
|
mod m_argv;
|
||||||
|
mod doomdef;
|
||||||
|
mod math;
|
||||||
|
|
||||||
|
|
||||||
|
/// Main entry pointz
|
||||||
|
///
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
if let Err(e) = std::panic::catch_unwind(|| {
|
||||||
|
d_main::D_DoomMain();
|
||||||
|
}) {
|
||||||
|
eprintln!("Panic in Doom Main: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/math/m_fixed.rs
Normal file
47
src/math/m_fixed.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/* Implemnetation of *m_fixed.c/h
|
||||||
|
|
||||||
|
This provides fixed-point arithmetic as 32-bit 16.16
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub const FRACBITS: i32 = 16;
|
||||||
|
pub const FRACUNIT: i32 = 1 << FRACBITS;
|
||||||
|
|
||||||
|
pub const MININT: i32 = 0x80000000;
|
||||||
|
pub const MAXINT: i32 = 0x7fffffff;
|
||||||
|
/// Struct representing a fixed point 32-bit value
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub struct FixedPoint {
|
||||||
|
pub value: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FixedPoint {
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn FixedMul(a: FixedPoint, b: FixedPoint) -> FixedPoint {
|
||||||
|
let value = ((a.value as u64) * (b.value as u64) >> FRACBITS) as i32;
|
||||||
|
FixedPoint{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn FixedDiv(a: FixedPoint, b: FixedPoint) -> FixedPoint {
|
||||||
|
if (i32::abs(a.value) >> 14) >= i32::abs(b.value) {
|
||||||
|
if a.value^b.value == 0 {
|
||||||
|
FixedPoint{value: MININT};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FixedPoint{value: MAXINT};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FixedPoint::FixedDiv2(a,b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn FixedDiv2(a: FixedPoint, b: FixedPoint) -> FixedPoint {
|
||||||
|
let c: f64 = ((a.value as f64) / (b.value as f64)) * FRACUNIT as f64;
|
||||||
|
if c >= 2147483648.0 || c < -2147483648.0 {
|
||||||
|
panic!("FixedDiv: divide by zero");
|
||||||
|
}
|
||||||
|
FixedPoint { value: c as i32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
src/math/mod.rs
Normal file
3
src/math/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// pub mod m_fixed;
|
||||||
|
|
||||||
|
// pub use m_fixed::FixedPoint;
|
||||||
Reference in New Issue
Block a user