z_mirror/fmm
git clone https://git.raiguard.me/z_mirror/fmm
1cbbe7f
refactor
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 :: types:: ModIdent ; 15 16const READ_SIZE : usize =1_048_576 ; 17 18pub struct SaveFile { 19pub map_version : Version , 20pub mods : Vec < ModIdent >, 21pub path : PathBuf , 22pub scenario_mod_name : String , 23pub scenario : String , 24} 25 26impl SaveFile { 27pub fn from ( path : PathBuf ) ->Result < Self > { 28println! ( "Reading save file..." ); 29let mut archive =ZipArchive :: new ( File :: open ( & path) ?) ?; 30let mut compressed =true ; 31let filenames: Vec < & str > = archive. file_names (). collect (); 32let filename = filenames 33. iter () 34. find ( |filename| filename. contains ( "level.dat0" )) 35. or_else ( ||{ 36 compressed =false ; 37 filenames 38. iter () 39. find ( |filename| filename. contains ( "level.dat" )) 40}) 41. map ( ToString :: to_string) 42. ok_or ( SaveFileErr :: NoLevelDat ) ?; 43let file = archive. by_name ( & filename) ?; 44 45let decompressed =if compressed{ 46let mut bytes =Vec :: with_capacity ( READ_SIZE ); 47 zlib:: Decoder :: new ( file). read_to_end ( & mut bytes) ?; 48 bytes 49} else { 50 file. bytes () 51. take ( READ_SIZE ) 52. filter_map ( |byte| byte. ok ()) 53. collect () 54}; 55 56let mut cursor =Cursor :: new ( decompressed); 57let version_major = cursor. read_u16 ::< LittleEndian >() ?; 58let version_minor = cursor. read_u16 ::< LittleEndian >() ?; 59let version_patch = cursor. read_u16 ::< LittleEndian >() ?; 60let _version_build = cursor. read_u16 ::< LittleEndian >() ?; 61 62 cursor. seek ( SeekFrom :: Current ( 2 )) ?; 63 64let scenario_name =read_string ( & mut cursor) ?; 65let scenario_mod_name =read_string ( & mut cursor) ?; 66 67// TODO: Handle campaigns 68 cursor. seek ( SeekFrom :: Current ( 14 )) ?; 69 70let num_mods = cursor. read_u8 () ?; 71 72let mut mods =Vec :: with_capacity ( num_modsas usize ); 73for _in 0 ..num_mods{ 74 mods. push ( read_mod ( & mut cursor) ?); 75} 76 77Ok ( Self { 78 mods, 79map_version : Version :: new ( 80 version_majoras u64 , 81 version_minoras u64 , 82 version_patchas u64 , 83), 84 path, 85scenario : scenario_name, 86 scenario_mod_name, 87}) 88} 89} 90 91# [ derive ( Debug , Error )] 92pub enum SaveFileErr { 93# [ error ( "No level.dat was found in the save file" )] 94NoLevelDat , 95} 96 97fn read_string ( reader : & mut Cursor < Vec < u8 >>) ->Result < String > { 98let scenario_len = reader. read_u8 () ?; 99let mut scenario_name =vec! [ 0 ; scenario_lenas usize ]; 100 reader. read_exact ( & mut scenario_name) ?; 101 102Ok ( String :: from_utf8_lossy ( & scenario_name). to_string ()) 103} 104 105fn read_mod ( reader : & mut Cursor < Vec < u8 >>) ->Result < ModIdent > { 106let mod_name =read_string ( reader) ?; 107 108// TODO: Read mod versions 109 reader. seek ( SeekFrom :: Current ( 7 )) ?; 110 111Ok ( ModIdent { 112name : mod_name, 113version_req : None , 114}) 115}