z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
216c7c7
rust-ice
1use anyhow:: Result ; 2use byteorder::{ LittleEndian , ReadBytesExt }; 3use compress:: zlib; 4use semver:: Version ; 5use std:: fs:: File ; 6use std:: io:: prelude:: * ; 7use std:: io:: Cursor ; 8use std:: io:: Read ; 9use std:: io:: SeekFrom ; 10use std:: path:: PathBuf ; 11use thiserror:: Error ; 12use zip:: ZipArchive ; 13 14use crate :: read; 15use crate :: read:: PropertyTree ; 16use crate :: types:: ModIdent ; 17 18const READ_SIZE : usize =1_048_576 ; 19 20pub struct SaveFile { 21pub map_version : Version , 22pub mods : Vec < ModIdent >, 23pub path : PathBuf , 24pub startup_settings : PropertyTree , 25} 26 27impl SaveFile { 28pub fn from ( path : PathBuf ) ->Result < Self > { 29println! ( "Reading save file..." ); 30let mut archive =ZipArchive :: new ( File :: open ( & path) ?) ?; 31let mut compressed =true ; 32let filenames: Vec < & str > = archive. file_names (). collect (); 33let filename = filenames 34. iter () 35. find ( |filename| filename. contains ( "level.dat0" )) 36. or_else ( ||{ 37 compressed =false ; 38 filenames 39. iter () 40. find ( |filename| filename. contains ( "level.dat" )) 41}) 42. map ( ToString :: to_string) 43. ok_or ( SaveFileErr :: NoLevelDat ) ?; 44let file = archive. by_name ( & filename) ?; 45 46let decompressed =if compressed{ 47let mut bytes =Vec :: with_capacity ( READ_SIZE ); 48 zlib:: Decoder :: new ( file). read_to_end ( & mut bytes) ?; 49 bytes 50} else { 51 file. bytes () 52. take ( READ_SIZE ) 53. filter_map ( |byte| byte. ok ()) 54. collect () 55}; 56 57let mut cursor =Cursor :: new ( decompressed); 58let version_major = cursor. read_u16 ::< LittleEndian >() ?; 59let version_minor = cursor. read_u16 ::< LittleEndian >() ?; 60let version_patch = cursor. read_u16 ::< LittleEndian >() ?; 61let _version_build = cursor. read_u16 ::< LittleEndian >() ?; 62 63// What are these for? 64 cursor. seek ( SeekFrom :: Current ( 2 )) ?; 65 66let _scenario_name = read:: read_string ( & mut cursor) ?; 67let _scenario_mod_name = read:: read_string ( & mut cursor) ?; 68 69// TODO: Handle campaigns 70 cursor. seek ( SeekFrom :: Current ( 14 )) ?; 71 72let num_mods = cursor. read_u8 () ?; 73 74let mut mods =Vec :: with_capacity ( num_modsas usize ); 75for _in 0 ..num_mods{ 76 mods. push ( read:: read_mod ( & mut cursor) ?); 77} 78 79// What are these for? 80 cursor. seek ( SeekFrom :: Current ( 4 )) ?; 81 82let startup_settings =PropertyTree :: load ( & mut cursor) ?; 83 84Ok ( Self { 85 mods, 86map_version : Version :: new ( 87 version_majoras u64 , 88 version_minoras u64 , 89 version_patchas u64 , 90), 91 path, 92 startup_settings, 93}) 94} 95} 96 97# [ derive ( Debug , Error )] 98pub enum SaveFileErr { 99# [ error ( "No level.dat was found in the save file" )] 100NoLevelDat , 101}