Compare commits
2 Commits
main
...
ec6811e6e7
| Author | SHA1 | Date | |
|---|---|---|---|
| ec6811e6e7 | |||
|
|
c7f7d9c324 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
# ---> VisualStudioCode
|
||||
.vscode/*
|
||||
.vscode/
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
@@ -12,3 +12,10 @@
|
||||
# Built Visual Studio Code Extensions
|
||||
*.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