mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-04 08:23:13 +00:00
Switched to a 120 max width.
This commit is contained in:
parent
443c6f6c38
commit
53963b5a7e
@ -26,7 +26,13 @@ pub fn proxy_sentinel_derive(input: proc_macro::TokenStream) -> proc_macro::Toke
|
||||
let mut wrapper_type = None;
|
||||
for attr in &input.attrs {
|
||||
match attr.parse_meta() {
|
||||
Ok(syn::Meta::List(list)) if list.path.get_ident().map(|ident| ident == "proxy_sentinel").unwrap_or(false) => {
|
||||
Ok(syn::Meta::List(list))
|
||||
if list
|
||||
.path
|
||||
.get_ident()
|
||||
.map(|ident| ident == "proxy_sentinel")
|
||||
.unwrap_or(false) =>
|
||||
{
|
||||
for nested_attr in &list.nested {
|
||||
match nested_attr {
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) => match name_value.path.get_ident() {
|
||||
@ -46,7 +52,8 @@ pub fn proxy_sentinel_derive(input: proc_macro::TokenStream) -> proc_macro::Toke
|
||||
let sentinel_value = sentinel_value.expect("You must supply a sentinel value via `proxy_sentinel(value = ...)`");
|
||||
let wrapper_type = wrapper_type.expect("You must supply the wrapper type via `proxy_sentinel(wrapper_type = ...)`");
|
||||
// TODO: Do this better, it's awful
|
||||
let wrapper_type: syn::TypePath = syn::parse_str(&wrapper_type.to_token_stream().to_string().trim_matches('"')).expect("");
|
||||
let wrapper_type: syn::TypePath =
|
||||
syn::parse_str(&wrapper_type.to_token_stream().to_string().trim_matches('"')).expect("");
|
||||
//let wrapper_type = syn::parse_macro_input!(wrapper_type_token_stream as );
|
||||
|
||||
let struct_name = input.ident;
|
||||
|
||||
@ -13,7 +13,9 @@ macro_rules! derive_bytes_split {
|
||||
|
||||
#[allow(clippy::ptr_offset_with_cast)] // `arrayref` does it
|
||||
impl $crate::Bytes for $T {
|
||||
type ByteArray = [u8; {0 $( + <<$U as ByteOrderExt<$crate::byteorder::$BYTEORDER>>::ByteArray as ByteArray>::SIZE )*}];
|
||||
type ByteArray = [u8;
|
||||
{0 $( + <<$U as ByteOrderExt<$crate::byteorder::$BYTEORDER>>::ByteArray as ByteArray>::SIZE )*}
|
||||
];
|
||||
|
||||
type FromError = !;
|
||||
type ToError = !;
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
#![doc(include = "lib.md")]
|
||||
// Features
|
||||
#![feature(never_type, stmt_expr_attributes, unwrap_infallible, format_args_capture, array_methods, external_doc)]
|
||||
#![feature(
|
||||
never_type,
|
||||
stmt_expr_attributes,
|
||||
unwrap_infallible,
|
||||
format_args_capture,
|
||||
array_methods,
|
||||
external_doc
|
||||
)]
|
||||
// Lints
|
||||
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
|
||||
// We'll disable the ones we don't need
|
||||
|
||||
@ -40,7 +40,10 @@ impl Sector {
|
||||
subheader,
|
||||
};
|
||||
|
||||
Ok(Self { header, data: data.into() })
|
||||
Ok(Self {
|
||||
header,
|
||||
data: data.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -84,8 +84,12 @@ impl Bytes for Header {
|
||||
*bytes.sync = Self::SYNC;
|
||||
self.address.to_bytes(bytes.address).map_err(ToBytesError::Address)?;
|
||||
*bytes.mode = 2;
|
||||
self.subheader.to_bytes(bytes.subheader1).map_err(ToBytesError::SubHeader)?;
|
||||
self.subheader.to_bytes(bytes.subheader2).map_err(ToBytesError::SubHeader)?;
|
||||
self.subheader
|
||||
.to_bytes(bytes.subheader1)
|
||||
.map_err(ToBytesError::SubHeader)?;
|
||||
self.subheader
|
||||
.to_bytes(bytes.subheader2)
|
||||
.map_err(ToBytesError::SubHeader)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -57,9 +57,15 @@ impl dcb_bytes::Bytes for Address {
|
||||
block: 0x1,
|
||||
);
|
||||
|
||||
let min = BcdU8(*bytes.min).to_u8().ok_or(FromBytesError::InvalidMinute(*bytes.min))?;
|
||||
let sec = BcdU8(*bytes.sec).to_u8().ok_or(FromBytesError::InvalidSecond(*bytes.sec))?;
|
||||
let block = BcdU8(*bytes.block).to_u8().ok_or(FromBytesError::InvalidBlock(*bytes.block))?;
|
||||
let min = BcdU8(*bytes.min)
|
||||
.to_u8()
|
||||
.ok_or(FromBytesError::InvalidMinute(*bytes.min))?;
|
||||
let sec = BcdU8(*bytes.sec)
|
||||
.to_u8()
|
||||
.ok_or(FromBytesError::InvalidSecond(*bytes.sec))?;
|
||||
let block = BcdU8(*bytes.block)
|
||||
.to_u8()
|
||||
.ok_or(FromBytesError::InvalidBlock(*bytes.block))?;
|
||||
|
||||
if !Self::SECS_RANGE.contains(&sec) {
|
||||
return Err(FromBytesError::OutOfRangeSecond(sec));
|
||||
@ -85,9 +91,15 @@ impl dcb_bytes::Bytes for Address {
|
||||
return Err(ToBytesError::OutOfRangeBlock(self.block));
|
||||
}
|
||||
|
||||
let min = BcdU8::from_u8(self.min).ok_or(ToBytesError::OutOfRangeMinute(self.min))?.0;
|
||||
let sec = BcdU8::from_u8(self.sec).ok_or(ToBytesError::OutOfRangeSecond(self.sec))?.0;
|
||||
let block = BcdU8::from_u8(self.block).ok_or(ToBytesError::OutOfRangeBlock(self.block))?.0;
|
||||
let min = BcdU8::from_u8(self.min)
|
||||
.ok_or(ToBytesError::OutOfRangeMinute(self.min))?
|
||||
.0;
|
||||
let sec = BcdU8::from_u8(self.sec)
|
||||
.ok_or(ToBytesError::OutOfRangeSecond(self.sec))?
|
||||
.0;
|
||||
let block = BcdU8::from_u8(self.block)
|
||||
.ok_or(ToBytesError::OutOfRangeBlock(self.block))?
|
||||
.0;
|
||||
|
||||
*bytes.min = min;
|
||||
*bytes.sec = sec;
|
||||
|
||||
@ -78,7 +78,14 @@ impl SubMode {
|
||||
/// Validates this submode
|
||||
fn validate(self) -> Result<(), BytesError> {
|
||||
// If more than 1 of `Audio`, `Video` or `Data` are set, return Err
|
||||
if self.bitand(Self::AUDIO).bitand(Self::VIDEO).bitand(Self::DATA).bits().count_ones() > 1 {
|
||||
if self
|
||||
.bitand(Self::AUDIO)
|
||||
.bitand(Self::VIDEO)
|
||||
.bitand(Self::DATA)
|
||||
.bits()
|
||||
.count_ones() >
|
||||
1
|
||||
{
|
||||
return Err(BytesError::MoreThan1VideoAudioDataSet);
|
||||
}
|
||||
|
||||
|
||||
@ -70,7 +70,10 @@ impl DirEntryReader {
|
||||
#[allow(clippy::single_match)] // We might add more matches in the future
|
||||
match bytes.name {
|
||||
[0x83, 0x52, 0x83, 0x53, 0x81, 0x5B, 0x20, 0x81, 0x60, 0x20, 0x43, 0x41, 0x52, 0x44, 0x32, 0x00] => {
|
||||
log::warn!("Ignoring special directory entry: {:?}", String::from_utf8_lossy(bytes.name));
|
||||
log::warn!(
|
||||
"Ignoring special directory entry: {:?}",
|
||||
String::from_utf8_lossy(bytes.name)
|
||||
);
|
||||
return Ok(None);
|
||||
},
|
||||
_ => (),
|
||||
|
||||
@ -52,7 +52,9 @@ impl DirReader {
|
||||
}
|
||||
|
||||
// And parse it
|
||||
DirEntryReader::from_bytes(&entry_bytes).map_err(ReadEntryError::ParseEntry).transpose()
|
||||
DirEntryReader::from_bytes(&entry_bytes)
|
||||
.map_err(ReadEntryError::ParseEntry)
|
||||
.transpose()
|
||||
});
|
||||
|
||||
Ok(iter)
|
||||
|
||||
@ -52,7 +52,8 @@ impl<L: DirWriterLister> DirWriter<L> {
|
||||
if start_pos % 2048 != 0 {
|
||||
return Err(WriteEntriesError::WriterNotAtSectorStart);
|
||||
}
|
||||
let start_sector_pos = u32::try_from(start_pos / 2048).map_err(|_err| WriteEntriesError::WriterSectorPastMax)?;
|
||||
let start_sector_pos =
|
||||
u32::try_from(start_pos / 2048).map_err(|_err| WriteEntriesError::WriterSectorPastMax)?;
|
||||
|
||||
// Get the starting sector position for the first entry.
|
||||
// Note: We start right after this directory
|
||||
@ -84,7 +85,9 @@ impl<L: DirWriterLister> DirWriter<L> {
|
||||
file.write(writer).map_err(WriteEntriesError::WriteFile)?;
|
||||
(size + 2047) / 2048
|
||||
},
|
||||
DirEntryWriterKind::Dir(dir) => dir.write_entries(writer).map_err(|err| WriteEntriesError::WriteDir(Box::new(err)))?,
|
||||
DirEntryWriterKind::Dir(dir) => dir
|
||||
.write_entries(writer)
|
||||
.map_err(|err| WriteEntriesError::WriteDir(Box::new(err)))?,
|
||||
};
|
||||
|
||||
// Update our sector pos
|
||||
@ -95,7 +98,9 @@ impl<L: DirWriterLister> DirWriter<L> {
|
||||
writer
|
||||
.seek(SeekFrom::Start(u64::from(start_sector_pos) * 2048))
|
||||
.map_err(WriteEntriesError::SeekEntries)?;
|
||||
writer.write_all(&entries_bytes).map_err(WriteEntriesError::WriteEntries)?;
|
||||
writer
|
||||
.write_all(&entries_bytes)
|
||||
.map_err(WriteEntriesError::WriteEntries)?;
|
||||
|
||||
// And return the number of sectors we wrote.
|
||||
Ok(cur_sector_pos - start_sector_pos)
|
||||
|
||||
@ -22,7 +22,11 @@ impl FileReader {
|
||||
/// Creates a new file reader from it's extension, sector position and size.
|
||||
#[must_use]
|
||||
pub const fn new(extension: AsciiStrArr<0x3>, sector_pos: u32, size: u32) -> Self {
|
||||
Self { extension, sector_pos, size }
|
||||
Self {
|
||||
extension,
|
||||
sector_pos,
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this file's extension
|
||||
@ -75,7 +79,11 @@ pub struct FileWriter<R: io::Read> {
|
||||
impl<R: io::Read> FileWriter<R> {
|
||||
/// Creates a new file writer from it's extension and reader.
|
||||
pub fn new(extension: AsciiStrArr<0x3>, reader: R, size: u32) -> Self {
|
||||
Self { extension, reader, size }
|
||||
Self {
|
||||
extension,
|
||||
reader,
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this file's extension
|
||||
|
||||
@ -97,7 +97,9 @@ pub struct DrvFsWriter;
|
||||
|
||||
impl DrvFsWriter {
|
||||
/// Creates a `.DRV` filesystem
|
||||
pub fn write_fs<W: io::Write + io::Seek, L: DirWriterLister>(writer: &mut W, root_entries: L) -> Result<(), WriteFsError<L::Error>> {
|
||||
pub fn write_fs<W: io::Write + io::Seek, L: DirWriterLister>(
|
||||
writer: &mut W, root_entries: L,
|
||||
) -> Result<(), WriteFsError<L::Error>> {
|
||||
// Get the root and write it
|
||||
let root = DirWriter::new(root_entries);
|
||||
root.write_entries(writer).map_err(WriteFsError::RootDir)?;
|
||||
|
||||
@ -145,21 +145,28 @@ impl Data {
|
||||
/// Searches all instructions for references to
|
||||
/// executable data using certain heuristics.
|
||||
#[must_use]
|
||||
pub fn search_instructions<'a>(insts_range: Range<Pos>, insts: impl Iterator<Item = (Pos, Inst<'a>)> + Clone) -> Vec<Self> {
|
||||
pub fn search_instructions<'a>(
|
||||
insts_range: Range<Pos>, insts: impl Iterator<Item = (Pos, Inst<'a>)> + Clone,
|
||||
) -> Vec<Self> {
|
||||
// Get all possible references to data
|
||||
let references: BTreeSet<Pos> = insts
|
||||
.clone()
|
||||
.filter_map(|(pos, inst)| match inst {
|
||||
Inst::Basic(basic::Inst::Load(basic::load::Inst { offset, .. }) | basic::Inst::Store(basic::store::Inst { offset, .. })) => {
|
||||
Some(pos + offset.sign_extended::<i32>())
|
||||
},
|
||||
Inst::Basic(
|
||||
basic::Inst::Load(basic::load::Inst { offset, .. }) |
|
||||
basic::Inst::Store(basic::store::Inst { offset, .. }),
|
||||
) => Some(pos + offset.sign_extended::<i32>()),
|
||||
Inst::Pseudo(
|
||||
pseudo::Inst::LoadImm(pseudo::load_imm::Inst {
|
||||
kind: pseudo::load_imm::Kind::Address(Pos(address)) | pseudo::load_imm::Kind::Word(address),
|
||||
..
|
||||
}) |
|
||||
pseudo::Inst::Load(pseudo::load::Inst { target: Pos(address), .. }) |
|
||||
pseudo::Inst::Store(pseudo::store::Inst { target: Pos(address), .. }),
|
||||
pseudo::Inst::Load(pseudo::load::Inst {
|
||||
target: Pos(address), ..
|
||||
}) |
|
||||
pseudo::Inst::Store(pseudo::store::Inst {
|
||||
target: Pos(address), ..
|
||||
}),
|
||||
) |
|
||||
Inst::Directive(Directive::Dw(address)) => Some(Pos(address)),
|
||||
_ => None,
|
||||
|
||||
@ -107,7 +107,14 @@ impl DataTable {
|
||||
},
|
||||
|
||||
// If we can't go any deeper, go as deep as we can at the start of `next_node`
|
||||
None => return Some(next_node.get_containing_deepest(next_node.data().start_pos()).unwrap_or(next_node).data()),
|
||||
None => {
|
||||
return Some(
|
||||
next_node
|
||||
.get_containing_deepest(next_node.data().start_pos())
|
||||
.unwrap_or(next_node)
|
||||
.data(),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -69,7 +69,10 @@ impl DataNode {
|
||||
// If it contains it, check if we can insert it there
|
||||
else if node.contains(&data) {
|
||||
// If `data` is heuristics and `node`'s data is known and not a marker, return Err
|
||||
if data.kind().is_heuristics() && node.data.kind().is_known() && !matches!(node.data.ty(), DataType::Marker { .. }) {
|
||||
if data.kind().is_heuristics() &&
|
||||
node.data.kind().is_known() &&
|
||||
!matches!(node.data.ty(), DataType::Marker { .. })
|
||||
{
|
||||
return Err(InsertError::InsertHeuristicsIntoNonMarkerKnown {
|
||||
data,
|
||||
known: node.data.clone(),
|
||||
@ -105,7 +108,11 @@ impl DataNode {
|
||||
}
|
||||
|
||||
// And insert it
|
||||
assert_eq!(self.nodes.replace(Self::new(data)), None, "No node with this position should exist",);
|
||||
assert_eq!(
|
||||
self.nodes.replace(Self::new(data)),
|
||||
None,
|
||||
"No node with this position should exist",
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -126,7 +133,10 @@ impl DataNode {
|
||||
pub fn get_containing(&self, pos: Pos) -> Option<&Self> {
|
||||
// Note: We search backwards as the nodes will be sorted
|
||||
// by their start position
|
||||
self.nodes.range(..=pos).next_back().filter(|node| node.data.contains_pos(pos))
|
||||
self.nodes
|
||||
.range(..=pos)
|
||||
.next_back()
|
||||
.filter(|node| node.data.contains_pos(pos))
|
||||
}
|
||||
|
||||
/// Returns the deepest data node containing `pos`
|
||||
@ -226,7 +236,9 @@ fn range_intersect<T: Ord>(lhs: Range<T>, rhs: Range<T>) -> bool {
|
||||
///
|
||||
/// It is a logical error to modify an element's order.
|
||||
/// This function *might* panic if the order is changed
|
||||
fn btree_set_modify<T: Ord + Borrow<Q>, Q: Ord, U>(set: &mut BTreeSet<T>, element: &Q, f: impl FnOnce(&mut T) -> U) -> U {
|
||||
fn btree_set_modify<T: Ord + Borrow<Q>, Q: Ord, U>(
|
||||
set: &mut BTreeSet<T>, element: &Q, f: impl FnOnce(&mut T) -> U,
|
||||
) -> U {
|
||||
// Take the element from the set
|
||||
let mut node = set.take(element).expect("Element didn't exist");
|
||||
|
||||
|
||||
@ -79,8 +79,10 @@ impl FuncTable {
|
||||
|
||||
/// Creates a new list of functions from an iterator over insts
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_lines)] // TODO: Refactor
|
||||
pub fn search_instructions<'a>(
|
||||
insts_range: Range<Pos>, insts: impl Iterator<Item = (Pos, Inst<'a>)> + Clone, known_func_table: &Self, data_table: &DataTable,
|
||||
insts_range: Range<Pos>, insts: impl Iterator<Item = (Pos, Inst<'a>)> + Clone, known_func_table: &Self,
|
||||
data_table: &DataTable,
|
||||
) -> Self {
|
||||
// Get all returns
|
||||
let returns: BTreeSet<Pos> = insts
|
||||
@ -159,7 +161,11 @@ impl FuncTable {
|
||||
|
||||
// If there's a function in between us and the return, use the last tailcall instead
|
||||
if let Some(next_func_pos) = function_entries.range(func_pos + 4i32..end_pos).next() {
|
||||
end_pos = tailcalls.range(..next_func_pos).next_back().copied().unwrap_or(func_pos) + 8i32;
|
||||
end_pos = tailcalls
|
||||
.range(..next_func_pos)
|
||||
.next_back()
|
||||
.copied()
|
||||
.unwrap_or(func_pos) + 8i32;
|
||||
|
||||
// If we got a tailcall before this function, just end it 2 insts
|
||||
if end_pos <= func_pos {
|
||||
@ -168,13 +174,22 @@ impl FuncTable {
|
||||
}
|
||||
|
||||
// If this function would intersect any other, skip this one.
|
||||
if cur_funcs.range(..=func_pos).next_back().map_or(false, |func| func.end_pos > func_pos) ||
|
||||
cur_funcs.range(func_pos..).next().map_or(false, |func| func.start_pos < end_pos) ||
|
||||
if cur_funcs
|
||||
.range(..=func_pos)
|
||||
.next_back()
|
||||
.map_or(false, |func| func.end_pos > func_pos) ||
|
||||
cur_funcs
|
||||
.range(func_pos..)
|
||||
.next()
|
||||
.map_or(false, |func| func.start_pos < end_pos) ||
|
||||
known_func_table
|
||||
.range(..=func_pos)
|
||||
.next_back()
|
||||
.map_or(false, |func| func.end_pos > func_pos) ||
|
||||
known_func_table.range(func_pos..).next().map_or(false, |func| func.start_pos < end_pos)
|
||||
known_func_table
|
||||
.range(func_pos..)
|
||||
.next()
|
||||
.map_or(false, |func| func.start_pos < end_pos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -45,7 +45,9 @@ pub enum Inst<'a> {
|
||||
|
||||
impl<'a> Inst<'a> {
|
||||
/// Decodes an instruction from bytes and it's position.
|
||||
pub fn decode(pos: Pos, bytes: &'a [u8], data_table: &'a DataTable, func_table: &'a FuncTable) -> Result<Self, DecodeError<'a>> {
|
||||
pub fn decode(
|
||||
pos: Pos, bytes: &'a [u8], data_table: &'a DataTable, func_table: &'a FuncTable,
|
||||
) -> Result<Self, DecodeError<'a>> {
|
||||
// If `bytes` is empty, return Err
|
||||
if bytes.is_empty() {
|
||||
return Err(DecodeError::NoBytes);
|
||||
@ -96,7 +98,9 @@ impl<'a> Inst<'a> {
|
||||
}
|
||||
|
||||
// Else read it as a directive
|
||||
Directive::decode(pos, bytes).map(Self::Directive).ok_or(DecodeError::NoBytes)
|
||||
Directive::decode(pos, bytes)
|
||||
.map(Self::Directive)
|
||||
.ok_or(DecodeError::NoBytes)
|
||||
}
|
||||
|
||||
/// Writes an instruction
|
||||
|
||||
@ -54,7 +54,9 @@ impl Kind {
|
||||
pub fn value(self) -> i64 {
|
||||
match self {
|
||||
Kind::Add(value) | Kind::AddUnsigned(value) | Kind::SetLessThan(value) => i64::from(value),
|
||||
Kind::SetLessThanUnsigned(value) | Kind::And(value) | Kind::Or(value) | Kind::Xor(value) => i64::from(value),
|
||||
Kind::SetLessThanUnsigned(value) | Kind::And(value) | Kind::Or(value) | Kind::Xor(value) => {
|
||||
i64::from(value)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,7 +121,9 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
#[rustfmt::skip]
|
||||
let to_kind = match mnemonic {
|
||||
"addi" => |value: i64| value.try_into().map(Kind::Add ),
|
||||
@ -134,16 +138,17 @@ impl<'a> Parsable<'a> for Inst {
|
||||
|
||||
match *args {
|
||||
// Disallow `slti` and `sltiu` in short form
|
||||
[LineArg::Register(_), LineArg::Literal(_)] if ["slti", "sltiu"].contains(&mnemonic) => Err(ParseError::InvalidArguments),
|
||||
[LineArg::Register(_), LineArg::Literal(_)] if ["slti", "sltiu"].contains(&mnemonic) => {
|
||||
Err(ParseError::InvalidArguments)
|
||||
},
|
||||
|
||||
// Else parse both `$dst, $lhs, value` and `$dst, value`.
|
||||
[LineArg::Register(lhs @ dst), LineArg::Literal(value)] | [LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(value)] => {
|
||||
Ok(Self {
|
||||
dst,
|
||||
lhs,
|
||||
kind: to_kind(value).map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
})
|
||||
},
|
||||
[LineArg::Register(lhs @ dst), LineArg::Literal(value)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(value)] => Ok(Self {
|
||||
dst,
|
||||
lhs,
|
||||
kind: to_kind(value).map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
}),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
}
|
||||
@ -167,7 +172,11 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
// only return one of them
|
||||
match !matches!(kind, Kind::SetLessThan(_) | Kind::SetLessThanUnsigned(_)) && dst == lhs {
|
||||
true => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::literal(value)]),
|
||||
false => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::Register(lhs), InstFmtArg::literal(value)]),
|
||||
false => array::IntoIter::new([
|
||||
InstFmtArg::Register(dst),
|
||||
InstFmtArg::Register(lhs),
|
||||
InstFmtArg::literal(value),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +133,9 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
#[rustfmt::skip]
|
||||
let kind = match mnemonic {
|
||||
"add" => Kind::Add ,
|
||||
@ -149,16 +151,19 @@ impl<'a> Parsable<'a> for Inst {
|
||||
_ => return Err(ParseError::UnknownMnemonic),
|
||||
};
|
||||
|
||||
match *args {
|
||||
let inst = match *args {
|
||||
// Disallow `slt` and `sltu` in short form
|
||||
[LineArg::Register(_), LineArg::Register(_)] if ["slt", "sltu"].contains(&mnemonic) => Err(ParseError::InvalidArguments),
|
||||
[LineArg::Register(_), LineArg::Register(_)] if ["slt", "sltu"].contains(&mnemonic) => {
|
||||
return Err(ParseError::InvalidArguments)
|
||||
},
|
||||
|
||||
// Else parse both `$dst, $lhs, $rhs` and `$dst, $rhs`.
|
||||
[LineArg::Register(lhs @ dst), LineArg::Register(rhs)] | [LineArg::Register(dst), LineArg::Register(lhs), LineArg::Register(rhs)] => {
|
||||
Ok(Self { dst, lhs, rhs, kind })
|
||||
},
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
[LineArg::Register(lhs @ dst), LineArg::Register(rhs)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Register(rhs)] => Self { dst, lhs, rhs, kind },
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
Ok(inst)
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +184,11 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
// only return one of them
|
||||
match !matches!(kind, Kind::SetLessThan | Kind::SetLessThanUnsigned) && dst == lhs {
|
||||
true => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::Register(rhs)]),
|
||||
false => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::Register(lhs), InstFmtArg::Register(rhs)]),
|
||||
false => array::IntoIter::new([
|
||||
InstFmtArg::Register(dst),
|
||||
InstFmtArg::Register(lhs),
|
||||
InstFmtArg::Register(rhs),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +169,9 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let inst = match mnemonic {
|
||||
"cop0" | "cop1" | "cop2" | "cop3" => {
|
||||
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
|
||||
@ -178,13 +180,18 @@ impl<'a> Parsable<'a> for Inst {
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
Inst { n, kind: Kind::CopN { imm } }
|
||||
Inst {
|
||||
n,
|
||||
kind: Kind::CopN { imm },
|
||||
}
|
||||
},
|
||||
"mfc0" | "mfc1" | "mfc2" | "mfc3" | "cfc0" | "cfc1" | "cfc2" | "cfc3" | "mtc0" | "mtc1" | "mtc2" | "mtc3" | "ctc0" | "ctc1" |
|
||||
"ctc2" | "ctc3" => {
|
||||
"mfc0" | "mfc1" | "mfc2" | "mfc3" | "cfc0" | "cfc1" | "cfc2" | "cfc3" | "mtc0" | "mtc1" | "mtc2" |
|
||||
"mtc3" | "ctc0" | "ctc1" | "ctc2" | "ctc3" => {
|
||||
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
|
||||
let (reg, imm) = match *args {
|
||||
[LineArg::Register(dst), LineArg::Literal(src)] => (dst, src.try_into().map_err(|_| ParseError::LiteralOutOfRange)?),
|
||||
[LineArg::Register(dst), LineArg::Literal(src)] => {
|
||||
(dst, src.try_into().map_err(|_| ParseError::LiteralOutOfRange)?)
|
||||
},
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
@ -197,11 +204,19 @@ impl<'a> Parsable<'a> for Inst {
|
||||
match &mnemonic[1..=1] {
|
||||
"f" => Inst {
|
||||
n,
|
||||
kind: Kind::MoveFrom { dst: reg, src: imm, kind },
|
||||
kind: Kind::MoveFrom {
|
||||
dst: reg,
|
||||
src: imm,
|
||||
kind,
|
||||
},
|
||||
},
|
||||
"t" => Inst {
|
||||
n,
|
||||
kind: Kind::MoveTo { dst: imm, src: reg, kind },
|
||||
kind: Kind::MoveTo {
|
||||
dst: imm,
|
||||
src: reg,
|
||||
kind,
|
||||
},
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -209,7 +224,9 @@ impl<'a> Parsable<'a> for Inst {
|
||||
"lwc0" | "lwc1" | "lwc2" | "lwc3" | "swc0" | "swc1" | "swc2" | "swc3" => {
|
||||
let n = mnemonic[3..].parse().expect("Unable to parse 0..=3");
|
||||
let (dst, src, offset) = match *args {
|
||||
[LineArg::Literal(dst), LineArg::Register(src)] => (dst.try_into().map_err(|_| ParseError::LiteralOutOfRange)?, src, 0),
|
||||
[LineArg::Literal(dst), LineArg::Register(src)] => {
|
||||
(dst.try_into().map_err(|_| ParseError::LiteralOutOfRange)?, src, 0)
|
||||
},
|
||||
[LineArg::Literal(dst), LineArg::RegisterOffset { register: src, offset }] => (
|
||||
dst.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
src,
|
||||
@ -273,11 +290,19 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
let &Self { kind, .. } = self;
|
||||
match kind {
|
||||
Kind::CopN { imm } => array::IntoIter::new([InstFmtArg::literal(imm)]),
|
||||
Kind::MoveFrom { dst, src, .. } => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::literal(src)]),
|
||||
Kind::MoveTo { dst, src, .. } => array::IntoIter::new([InstFmtArg::Register(src), InstFmtArg::literal(dst)]),
|
||||
Kind::MoveFrom { dst, src, .. } => {
|
||||
array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::literal(src)])
|
||||
},
|
||||
Kind::MoveTo { dst, src, .. } => {
|
||||
array::IntoIter::new([InstFmtArg::Register(src), InstFmtArg::literal(dst)])
|
||||
},
|
||||
Kind::Branch { offset, .. } => array::IntoIter::new([InstFmtArg::literal(offset)]),
|
||||
Kind::Load { dst, src, offset } => array::IntoIter::new([InstFmtArg::literal(dst), InstFmtArg::register_offset(src, offset)]),
|
||||
Kind::Store { dst, src, offset } => array::IntoIter::new([InstFmtArg::literal(dst), InstFmtArg::register_offset(src, offset)]),
|
||||
Kind::Load { dst, src, offset } => {
|
||||
array::IntoIter::new([InstFmtArg::literal(dst), InstFmtArg::register_offset(src, offset)])
|
||||
},
|
||||
Kind::Store { dst, src, offset } => {
|
||||
array::IntoIter::new([InstFmtArg::literal(dst), InstFmtArg::register_offset(src, offset)])
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -286,7 +311,9 @@ impl ModifiesReg for Inst {
|
||||
fn modifies_reg(&self, reg: Register) -> bool {
|
||||
match self.kind {
|
||||
Kind::MoveFrom { dst, .. } => dst == reg,
|
||||
Kind::CopN { .. } | Kind::MoveTo { .. } | Kind::Branch { .. } | Kind::Load { .. } | Kind::Store { .. } => false,
|
||||
Kind::CopN { .. } | Kind::MoveTo { .. } | Kind::Branch { .. } | Kind::Load { .. } | Kind::Store { .. } => {
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,15 +149,21 @@ impl<'a> Parsable<'a> for Inst {
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
"bnez" => match *args {
|
||||
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::NotEqual(Register::Zr)),
|
||||
[LineArg::Register(arg), ref target] => {
|
||||
(arg, target_arg_to_offset(target)?, Kind::NotEqual(Register::Zr))
|
||||
},
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
"beq" => match *args {
|
||||
[LineArg::Register(arg), LineArg::Register(reg), ref target] => (arg, target_arg_to_offset(target)?, Kind::Equal(reg)),
|
||||
[LineArg::Register(arg), LineArg::Register(reg), ref target] => {
|
||||
(arg, target_arg_to_offset(target)?, Kind::Equal(reg))
|
||||
},
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
"bne" => match *args {
|
||||
[LineArg::Register(arg), LineArg::Register(reg), ref target] => (arg, target_arg_to_offset(target)?, Kind::NotEqual(reg)),
|
||||
[LineArg::Register(arg), LineArg::Register(reg), ref target] => {
|
||||
(arg, target_arg_to_offset(target)?, Kind::NotEqual(reg))
|
||||
},
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
"blez" => match *args {
|
||||
@ -181,7 +187,9 @@ impl<'a> Parsable<'a> for Inst {
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
"bgezal" => match *args {
|
||||
[LineArg::Register(arg), ref target] => (arg, target_arg_to_offset(target)?, Kind::GreaterOrEqualZeroLink),
|
||||
[LineArg::Register(arg), ref target] => {
|
||||
(arg, target_arg_to_offset(target)?, Kind::GreaterOrEqualZeroLink)
|
||||
},
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
|
||||
@ -220,18 +228,28 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
}
|
||||
|
||||
#[auto_enums::auto_enum(Iterator)]
|
||||
#[rustfmt::skip]
|
||||
fn args<Ctx: DisplayCtx>(&'a self, ctx: &Ctx) -> Self::Args {
|
||||
use InstFmtArg::Register as register;
|
||||
let &Self { arg, offset, kind } = self;
|
||||
let target = Self::target_of(offset, ctx.cur_pos());
|
||||
|
||||
match (arg, kind) {
|
||||
(Register::Zr, Kind::Equal (Register::Zr)) => array::IntoIter::new([ InstFmtArg::Target(target)]),
|
||||
(_ , Kind::Equal (Register::Zr)) => array::IntoIter::new([register(arg), InstFmtArg::Target(target)]),
|
||||
(_ , Kind::Equal (reg) ) => array::IntoIter::new([register(arg), register(reg), InstFmtArg::Target(target)]),
|
||||
(_ , Kind::NotEqual(Register::Zr)) => array::IntoIter::new([register(arg), InstFmtArg::Target(target)]),
|
||||
(_ , Kind::NotEqual(reg) ) => array::IntoIter::new([register(arg), register(reg), InstFmtArg::Target(target)]),
|
||||
(Register::Zr, Kind::Equal(Register::Zr)) => array::IntoIter::new([InstFmtArg::Target(target)]),
|
||||
(_, Kind::Equal(Register::Zr)) => {
|
||||
array::IntoIter::new([InstFmtArg::Register(arg), InstFmtArg::Target(target)])
|
||||
},
|
||||
(_, Kind::Equal(reg)) => array::IntoIter::new([
|
||||
InstFmtArg::Register(arg),
|
||||
InstFmtArg::Register(reg),
|
||||
InstFmtArg::Target(target),
|
||||
]),
|
||||
(_, Kind::NotEqual(Register::Zr)) => {
|
||||
array::IntoIter::new([InstFmtArg::Register(arg), InstFmtArg::Target(target)])
|
||||
},
|
||||
(_, Kind::NotEqual(reg)) => array::IntoIter::new([
|
||||
InstFmtArg::Register(arg),
|
||||
InstFmtArg::Register(reg),
|
||||
InstFmtArg::Target(target),
|
||||
]),
|
||||
(
|
||||
_,
|
||||
Kind::LessOrEqualZero |
|
||||
@ -240,7 +258,7 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
Kind::GreaterOrEqualZero |
|
||||
Kind::LessThanZeroLink |
|
||||
Kind::GreaterOrEqualZeroLink,
|
||||
) => array::IntoIter::new([register(arg), InstFmtArg::Target(target)]),
|
||||
) => array::IntoIter::new([InstFmtArg::Register(arg), InstFmtArg::Target(target)]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +74,9 @@ impl Encode for Inst {
|
||||
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let (target, kind) = match mnemonic {
|
||||
"jr" => match *args {
|
||||
[LineArg::Register(target)] => (target, Kind::Jump),
|
||||
|
||||
@ -131,18 +131,27 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let kind = Kind::from_mnemonic(mnemonic).ok_or(ParseError::UnknownMnemonic)?;
|
||||
|
||||
let (value, addr, offset) = match *args {
|
||||
[LineArg::Register(value), LineArg::Register(addr)] => (value, addr, 0),
|
||||
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => {
|
||||
(value, addr, offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?)
|
||||
},
|
||||
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => (
|
||||
value,
|
||||
addr,
|
||||
offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
Ok(Self { value, addr, offset, kind })
|
||||
Ok(Self {
|
||||
value,
|
||||
addr,
|
||||
offset,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,7 +164,9 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
}
|
||||
|
||||
fn args<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Args {
|
||||
let &Self { value, addr, offset, .. } = self;
|
||||
let &Self {
|
||||
value, addr, offset, ..
|
||||
} = self;
|
||||
|
||||
array::IntoIter::new([InstFmtArg::Register(value), InstFmtArg::register_offset(addr, offset)])
|
||||
}
|
||||
|
||||
@ -47,7 +47,9 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
if mnemonic != "lui" {
|
||||
return Err(ParseError::UnknownMnemonic);
|
||||
}
|
||||
|
||||
@ -109,20 +109,21 @@ impl Decode for Inst {
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let reg = Register::new;
|
||||
Some(match f {
|
||||
// 00x0
|
||||
0x0 => Self::MoveFrom { dst: Register::new(d)?, src: MultReg::Hi },
|
||||
0x2 => Self::MoveFrom { dst: Register::new(d)?, src: MultReg::Lo },
|
||||
0x0 => Self::MoveFrom { dst: reg(d)?, src: MultReg::Hi },
|
||||
0x2 => Self::MoveFrom { dst: reg(d)?, src: MultReg::Lo },
|
||||
|
||||
// 00x1
|
||||
0x1 => Self::MoveTo { src: Register::new(s)?, dst: MultReg::Hi },
|
||||
0x3 => Self::MoveTo { src: Register::new(s)?, dst: MultReg::Lo },
|
||||
0x1 => Self::MoveTo { src: reg(s)?, dst: MultReg::Hi },
|
||||
0x3 => Self::MoveTo { src: reg(s)?, dst: MultReg::Lo },
|
||||
|
||||
// 10xx
|
||||
0x8 => Self::Mult { kind: MultKind::Mult, mode: MultMode:: Signed, lhs: Register::new(s)?, rhs: Register::new(t)? },
|
||||
0x9 => Self::Mult { kind: MultKind::Mult, mode: MultMode::Unsigned, lhs: Register::new(s)?, rhs: Register::new(t)? },
|
||||
0xa => Self::Mult { kind: MultKind::Div , mode: MultMode:: Signed, lhs: Register::new(s)?, rhs: Register::new(t)? },
|
||||
0xb => Self::Mult { kind: MultKind::Div , mode: MultMode::Unsigned, lhs: Register::new(s)?, rhs: Register::new(t)? },
|
||||
0x8 => Self::Mult { kind: MultKind::Mult, mode: MultMode:: Signed, lhs: reg(s)?, rhs: reg(t)? },
|
||||
0x9 => Self::Mult { kind: MultKind::Mult, mode: MultMode::Unsigned, lhs: reg(s)?, rhs: reg(t)? },
|
||||
0xa => Self::Mult { kind: MultKind::Div , mode: MultMode:: Signed, lhs: reg(s)?, rhs: reg(t)? },
|
||||
0xb => Self::Mult { kind: MultKind::Div , mode: MultMode::Unsigned, lhs: reg(s)?, rhs: reg(t)? },
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
@ -155,7 +156,9 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let inst = match mnemonic {
|
||||
"mflo" | "mfhi" | "mtlo" | "mthi" => {
|
||||
let reg = match *args {
|
||||
@ -170,8 +173,14 @@ impl<'a> Parsable<'a> for Inst {
|
||||
};
|
||||
|
||||
match &mnemonic[1..=1] {
|
||||
"f" => Inst::MoveFrom { dst: reg, src: mult_reg },
|
||||
"t" => Inst::MoveTo { dst: mult_reg, src: reg },
|
||||
"f" => Inst::MoveFrom {
|
||||
dst: reg,
|
||||
src: mult_reg,
|
||||
},
|
||||
"t" => Inst::MoveTo {
|
||||
dst: mult_reg,
|
||||
src: reg,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
@ -219,7 +228,9 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
fn args<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Args {
|
||||
match *self {
|
||||
Self::Mult { lhs, rhs, .. } => array::IntoIter::new([InstFmtArg::Register(lhs), InstFmtArg::Register(rhs)]),
|
||||
Self::MoveFrom { dst: arg, .. } | Self::MoveTo { src: arg, .. } => array::IntoIter::new([InstFmtArg::Register(arg)]),
|
||||
Self::MoveFrom { dst: arg, .. } | Self::MoveTo { src: arg, .. } => {
|
||||
array::IntoIter::new([InstFmtArg::Register(arg)])
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,9 @@ impl TryEncode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let kind = match mnemonic {
|
||||
"sll" => Kind::LeftLogical,
|
||||
"srl" => Kind::RightLogical,
|
||||
@ -115,14 +117,13 @@ impl<'a> Parsable<'a> for Inst {
|
||||
};
|
||||
|
||||
match *args {
|
||||
[LineArg::Register(lhs @ dst), LineArg::Literal(rhs)] | [LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(rhs)] => {
|
||||
Ok(Self {
|
||||
dst,
|
||||
lhs,
|
||||
rhs: rhs.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
kind,
|
||||
})
|
||||
},
|
||||
[LineArg::Register(lhs @ dst), LineArg::Literal(rhs)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Literal(rhs)] => Ok(Self {
|
||||
dst,
|
||||
lhs,
|
||||
rhs: rhs.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
kind,
|
||||
}),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
}
|
||||
@ -144,7 +145,11 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
// If `$dst` and `$lhs` are the same, only print one of them
|
||||
match dst == lhs {
|
||||
true => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::literal(rhs)]),
|
||||
false => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::Register(lhs), InstFmtArg::literal(rhs)]),
|
||||
false => array::IntoIter::new([
|
||||
InstFmtArg::Register(dst),
|
||||
InstFmtArg::Register(lhs),
|
||||
InstFmtArg::literal(rhs),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,9 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let kind = match mnemonic {
|
||||
"sllv" => Kind::LeftLogical,
|
||||
"srlv" => Kind::RightLogical,
|
||||
@ -100,12 +102,13 @@ impl<'a> Parsable<'a> for Inst {
|
||||
_ => return Err(ParseError::UnknownMnemonic),
|
||||
};
|
||||
|
||||
match *args {
|
||||
[LineArg::Register(lhs @ dst), LineArg::Register(rhs)] | [LineArg::Register(dst), LineArg::Register(lhs), LineArg::Register(rhs)] => {
|
||||
Ok(Self { dst, lhs, rhs, kind })
|
||||
},
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
let inst = match *args {
|
||||
[LineArg::Register(lhs @ dst), LineArg::Register(rhs)] |
|
||||
[LineArg::Register(dst), LineArg::Register(lhs), LineArg::Register(rhs)] => Self { dst, lhs, rhs, kind },
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
Ok(inst)
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +128,11 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
// If `$dst` and `$lhs` are the same, only print one of them
|
||||
match dst == lhs {
|
||||
true => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::Register(rhs)]),
|
||||
false => array::IntoIter::new([InstFmtArg::Register(dst), InstFmtArg::Register(lhs), InstFmtArg::Register(rhs)]),
|
||||
false => array::IntoIter::new([
|
||||
InstFmtArg::Register(dst),
|
||||
InstFmtArg::Register(lhs),
|
||||
InstFmtArg::Register(rhs),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,18 +119,27 @@ impl Encode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let kind = Kind::from_mnemonic(mnemonic).ok_or(ParseError::UnknownMnemonic)?;
|
||||
|
||||
let (value, addr, offset) = match *args {
|
||||
[LineArg::Register(value), LineArg::Register(addr)] => (value, addr, 0),
|
||||
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => {
|
||||
(value, addr, offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?)
|
||||
},
|
||||
[LineArg::Register(value), LineArg::RegisterOffset { register: addr, offset }] => (
|
||||
value,
|
||||
addr,
|
||||
offset.try_into().map_err(|_| ParseError::LiteralOutOfRange)?,
|
||||
),
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
};
|
||||
|
||||
Ok(Self { value, addr, offset, kind })
|
||||
Ok(Self {
|
||||
value,
|
||||
addr,
|
||||
offset,
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +152,9 @@ impl<'a> InstDisplay<'a> for Inst {
|
||||
}
|
||||
|
||||
fn args<Ctx: DisplayCtx>(&'a self, _ctx: &Ctx) -> Self::Args {
|
||||
let &Self { value, addr, offset, .. } = self;
|
||||
let &Self {
|
||||
value, addr, offset, ..
|
||||
} = self;
|
||||
|
||||
array::IntoIter::new([InstFmtArg::Register(value), InstFmtArg::register_offset(addr, offset)])
|
||||
}
|
||||
|
||||
@ -87,7 +87,9 @@ impl TryEncode for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
let kind = match mnemonic {
|
||||
"sys" => Kind::Sys,
|
||||
"break" => Kind::Break,
|
||||
|
||||
@ -57,7 +57,9 @@ pub enum DecodeWithDataError {
|
||||
|
||||
impl<'a> Directive<'a> {
|
||||
/// Decodes a directive with some data
|
||||
pub fn decode_with_data(pos: Pos, bytes: &'a [u8], ty: &DataType, data_pos: Pos) -> Result<Self, DecodeWithDataError> {
|
||||
pub fn decode_with_data(
|
||||
pos: Pos, bytes: &'a [u8], ty: &DataType, data_pos: Pos,
|
||||
) -> Result<Self, DecodeWithDataError> {
|
||||
// Make sure that this function is only called when the data contains `pos`
|
||||
assert!((data_pos..data_pos + ty.size()).contains(&pos));
|
||||
|
||||
@ -84,7 +86,9 @@ impl<'a> Directive<'a> {
|
||||
|
||||
// Then make sure there's nulls padding the string
|
||||
let nulls_len = 4 - len % 4;
|
||||
let nulls = bytes.get(len..len + nulls_len).ok_or(DecodeWithDataError::StrNullTerminator)?;
|
||||
let nulls = bytes
|
||||
.get(len..len + nulls_len)
|
||||
.ok_or(DecodeWithDataError::StrNullTerminator)?;
|
||||
if !nulls.iter().all(|&ch| ch == 0) {
|
||||
return Err(DecodeWithDataError::StrNullTerminator);
|
||||
}
|
||||
@ -173,7 +177,9 @@ impl<'a> Parsable<'a> for Directive<'a> {
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
".ascii" => match *args {
|
||||
[LineArg::String(ref s)] => Self::Ascii(AsciiStr::from_ascii(s).map_err(|_| ParseError::NonAsciiString)?),
|
||||
[LineArg::String(ref s)] => {
|
||||
Self::Ascii(AsciiStr::from_ascii(s).map_err(|_| ParseError::NonAsciiString)?)
|
||||
},
|
||||
_ => return Err(ParseError::InvalidArguments),
|
||||
},
|
||||
_ => return Err(ParseError::UnknownMnemonic),
|
||||
|
||||
@ -80,7 +80,9 @@ impl<'a> InstFmtArg<'a> {
|
||||
pub fn write<Ctx: DisplayCtx>(&self, f: &mut fmt::Formatter, ctx: &Ctx) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
// Register offsets with 0 offset are formatted like normal registers
|
||||
InstFmtArg::Register(register) | InstFmtArg::RegisterOffset { register, offset: 0 } => write!(f, "{register}"),
|
||||
InstFmtArg::Register(register) | InstFmtArg::RegisterOffset { register, offset: 0 } => {
|
||||
write!(f, "{register}")
|
||||
},
|
||||
InstFmtArg::RegisterOffset { register, offset } => write!(f, "{:#}({register})", SignedHex(offset)),
|
||||
// Note: Literals do not go through label lookup
|
||||
InstFmtArg::Literal(value) => write!(f, "{:#}", SignedHex(value)),
|
||||
|
||||
@ -31,7 +31,10 @@ pub trait ParseCtx {
|
||||
match *arg {
|
||||
LineArg::Literal(pos) => pos.try_into().map(Pos).map_err(|_| ParseError::LiteralOutOfRange),
|
||||
LineArg::Label(ref label) => self.label_pos(label).ok_or(ParseError::UnknownLabel),
|
||||
LineArg::LabelOffset { ref label, offset } => self.label_pos(label).map(|pos| pos + offset).ok_or(ParseError::UnknownLabel),
|
||||
LineArg::LabelOffset { ref label, offset } => self
|
||||
.label_pos(label)
|
||||
.map(|pos| pos + offset)
|
||||
.ok_or(ParseError::UnknownLabel),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
}
|
||||
@ -39,9 +42,18 @@ pub trait ParseCtx {
|
||||
/// Retrieves a position and offset from an argument
|
||||
fn arg_pos_offset(&self, arg: &LineArg) -> Result<(Pos, i64), ParseError> {
|
||||
match *arg {
|
||||
LineArg::Literal(pos) => pos.try_into().map(|pos| (Pos(pos), 0)).map_err(|_| ParseError::LiteralOutOfRange),
|
||||
LineArg::Label(ref label) => self.label_pos(label).map(|pos| (pos, 0)).ok_or(ParseError::UnknownLabel),
|
||||
LineArg::LabelOffset { ref label, offset } => self.label_pos(label).map(|pos| (pos, offset)).ok_or(ParseError::UnknownLabel),
|
||||
LineArg::Literal(pos) => pos
|
||||
.try_into()
|
||||
.map(|pos| (Pos(pos), 0))
|
||||
.map_err(|_| ParseError::LiteralOutOfRange),
|
||||
LineArg::Label(ref label) => self
|
||||
.label_pos(label)
|
||||
.map(|pos| (pos, 0))
|
||||
.ok_or(ParseError::UnknownLabel),
|
||||
LineArg::LabelOffset { ref label, offset } => self
|
||||
.label_pos(label)
|
||||
.map(|pos| (pos, offset))
|
||||
.ok_or(ParseError::UnknownLabel),
|
||||
_ => Err(ParseError::InvalidArguments),
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,10 @@ impl<R: io::BufRead> InstParser<R> {
|
||||
|
||||
// If it starts with a comment or it's empty, return an empty line
|
||||
if line.starts_with('#') || line.is_empty() {
|
||||
return Ok(Line { label: None, inst: None });
|
||||
return Ok(Line {
|
||||
label: None,
|
||||
inst: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Name character validator
|
||||
@ -146,7 +149,10 @@ impl<R: io::BufRead> InstParser<R> {
|
||||
|
||||
// Find the first non-escaped '"'
|
||||
// Note: The `+2` can never panic.
|
||||
let string = match args[1..].find(find_first_non_escaped_quotes).map(|idx| args.split_at(idx + 2)) {
|
||||
let string = match args[1..]
|
||||
.find(find_first_non_escaped_quotes)
|
||||
.map(|idx| args.split_at(idx + 2))
|
||||
{
|
||||
Some((string, rest)) => {
|
||||
args = rest.trim_start();
|
||||
string
|
||||
|
||||
@ -39,7 +39,9 @@ impl Decodable for Inst {
|
||||
basic::Inst::Lui(lui) => match insts.next()? {
|
||||
basic::Inst::Load(load) if load.value == lui.dst && load.addr == load.value => Self {
|
||||
value: load.value,
|
||||
target: Pos((u32::join(0, lui.value).as_signed() + load.offset.sign_extended::<i32>()).as_unsigned()),
|
||||
target: Pos(
|
||||
(u32::join(0, lui.value).as_signed() + load.offset.sign_extended::<i32>()).as_unsigned(),
|
||||
),
|
||||
kind: load.kind,
|
||||
},
|
||||
_ => return None,
|
||||
|
||||
@ -3,7 +3,9 @@
|
||||
// Imports
|
||||
use super::{Decodable, Encodable};
|
||||
use crate::{
|
||||
inst::{basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register},
|
||||
inst::{
|
||||
basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register,
|
||||
},
|
||||
Pos,
|
||||
};
|
||||
use int_conv::{Join, SignExtended, Signed, Split};
|
||||
@ -66,7 +68,9 @@ impl Decodable for Inst {
|
||||
dst: lui.dst,
|
||||
kind: match alu.kind {
|
||||
// lui << 16 + rhs
|
||||
AddUnsigned(rhs) => Kind::Address(Pos((u32::join(0, lui.value).as_signed() + rhs.sign_extended::<i32>()).as_unsigned())),
|
||||
AddUnsigned(rhs) => Kind::Address(Pos((u32::join(0, lui.value).as_signed() +
|
||||
rhs.sign_extended::<i32>())
|
||||
.as_unsigned())),
|
||||
Or(rhs) => Kind::Word(u32::join(rhs, lui.value)),
|
||||
_ => return None,
|
||||
},
|
||||
@ -105,7 +109,10 @@ impl Encodable for Inst {
|
||||
};
|
||||
|
||||
std::array::IntoIter::new([
|
||||
basic::Inst::Lui(basic::lui::Inst { dst: self.dst, value: hi }),
|
||||
basic::Inst::Lui(basic::lui::Inst {
|
||||
dst: self.dst,
|
||||
value: hi,
|
||||
}),
|
||||
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
|
||||
dst: self.dst,
|
||||
lhs: self.dst,
|
||||
@ -117,7 +124,10 @@ impl Encodable for Inst {
|
||||
let (lo, hi) = value.lo_hi();
|
||||
|
||||
std::array::IntoIter::new([
|
||||
basic::Inst::Lui(basic::lui::Inst { dst: self.dst, value: hi }),
|
||||
basic::Inst::Lui(basic::lui::Inst {
|
||||
dst: self.dst,
|
||||
value: hi,
|
||||
}),
|
||||
basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
|
||||
dst: self.dst,
|
||||
lhs: self.dst,
|
||||
@ -125,17 +135,21 @@ impl Encodable for Inst {
|
||||
})),
|
||||
])
|
||||
},
|
||||
Kind::HalfWordUnsigned(value) => std::iter::once(basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
|
||||
dst: self.dst,
|
||||
lhs: Register::Zr,
|
||||
kind: basic::alu::imm::Kind::Or(value),
|
||||
}))),
|
||||
Kind::HalfWordUnsigned(value) => {
|
||||
std::iter::once(basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
|
||||
dst: self.dst,
|
||||
lhs: Register::Zr,
|
||||
kind: basic::alu::imm::Kind::Or(value),
|
||||
})))
|
||||
},
|
||||
|
||||
Kind::HalfWordSigned(value) => std::iter::once(basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
|
||||
dst: self.dst,
|
||||
lhs: Register::Zr,
|
||||
kind: basic::alu::imm::Kind::AddUnsigned(value),
|
||||
}))),
|
||||
Kind::HalfWordSigned(value) => {
|
||||
std::iter::once(basic::Inst::Alu(basic::alu::Inst::Imm(basic::alu::imm::Inst {
|
||||
dst: self.dst,
|
||||
lhs: Register::Zr,
|
||||
kind: basic::alu::imm::Kind::AddUnsigned(value),
|
||||
})))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
// Imports
|
||||
use super::{Decodable, Encodable};
|
||||
use crate::inst::{basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register};
|
||||
use crate::inst::{
|
||||
basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use std::{array, convert::TryInto};
|
||||
|
||||
/// Move register instruction
|
||||
@ -49,7 +51,9 @@ impl Encodable for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
if mnemonic != "move" {
|
||||
return Err(ParseError::UnknownMnemonic);
|
||||
}
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
// Imports
|
||||
use super::{Decodable, Encodable};
|
||||
use crate::inst::{basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register};
|
||||
use crate::inst::{
|
||||
basic, parse::LineArg, DisplayCtx, InstDisplay, InstFmtArg, InstSize, Parsable, ParseCtx, ParseError, Register,
|
||||
};
|
||||
use std::{
|
||||
array,
|
||||
convert::{TryFrom, TryInto},
|
||||
@ -48,7 +50,9 @@ impl Encodable for Inst {
|
||||
}
|
||||
|
||||
impl<'a> Parsable<'a> for Inst {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx) -> Result<Self, ParseError> {
|
||||
fn parse<Ctx: ?Sized + ParseCtx>(
|
||||
mnemonic: &'a str, args: &'a [LineArg], _ctx: &'a Ctx,
|
||||
) -> Result<Self, ParseError> {
|
||||
if mnemonic != "nop" {
|
||||
return Err(ParseError::UnknownMnemonic);
|
||||
}
|
||||
|
||||
@ -38,7 +38,9 @@ impl Decodable for Inst {
|
||||
basic::Inst::Lui(lui) if lui.dst == Register::At => match insts.next()? {
|
||||
basic::Inst::Store(store) if store.addr == Register::At => Self {
|
||||
value: store.value,
|
||||
target: Pos((u32::join(0, lui.value).as_signed() + store.offset.sign_extended::<i32>()).as_unsigned()),
|
||||
target: Pos(
|
||||
(u32::join(0, lui.value).as_signed() + store.offset.sign_extended::<i32>()).as_unsigned(),
|
||||
),
|
||||
kind: store.kind,
|
||||
},
|
||||
_ => return None,
|
||||
|
||||
@ -89,7 +89,8 @@ impl ExeReader {
|
||||
pub fn deserialize<R: io::Read + io::Seek>(file: &mut R) -> Result<Self, DeserializeError> {
|
||||
// Read header
|
||||
let mut header_bytes = [0u8; <<Header as Bytes>::ByteArray as ByteArray>::SIZE];
|
||||
file.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
file.read_exact(&mut header_bytes)
|
||||
.map_err(DeserializeError::ReadHeader)?;
|
||||
let header = Header::from_bytes(&header_bytes).map_err(DeserializeError::ParseHeader)?;
|
||||
|
||||
// Get the instruction range
|
||||
@ -100,7 +101,8 @@ impl ExeReader {
|
||||
};
|
||||
|
||||
// Read all of the bytes
|
||||
let mut bytes = vec![0u8; usize::try_from(header.size).expect("Len didn't fit into `usize`")].into_boxed_slice();
|
||||
let mut bytes =
|
||||
vec![0u8; usize::try_from(header.size).expect("Len didn't fit into `usize`")].into_boxed_slice();
|
||||
file.read_exact(bytes.as_mut()).map_err(DeserializeError::ReadData)?;
|
||||
|
||||
// Read the known data and func table
|
||||
@ -112,7 +114,8 @@ impl ExeReader {
|
||||
|
||||
// Then parse all heuristic tables
|
||||
let heuristics_data = Data::search_instructions(insts_range.clone(), insts.clone());
|
||||
let heuristics_func_table = FuncTable::search_instructions(insts_range, insts, &known_func_table, &known_data_table);
|
||||
let heuristics_func_table =
|
||||
FuncTable::search_instructions(insts_range, insts, &known_func_table, &known_data_table);
|
||||
known_data_table.extend(heuristics_data);
|
||||
let func_table = known_func_table.merge_with(heuristics_func_table);
|
||||
|
||||
|
||||
@ -49,7 +49,11 @@ impl<'a, R: io::Read + io::Seek> GameFile<'a, R> {
|
||||
'a: 'b,
|
||||
{
|
||||
// Read the root directory
|
||||
let root_dir = self.filesystem.root_dir().read_dir(self.cdrom).map_err(ReadDrvError::ReadRoot)?;
|
||||
let root_dir = self
|
||||
.filesystem
|
||||
.root_dir()
|
||||
.read_dir(self.cdrom)
|
||||
.map_err(ReadDrvError::ReadRoot)?;
|
||||
|
||||
// Get the file
|
||||
let entry = root_dir.find(name).ok_or(ReadDrvError::FindFile)?;
|
||||
|
||||
@ -53,7 +53,9 @@ impl TimFile {
|
||||
pub fn deserialize<R: io::Read + io::Seek>(reader: &mut R) -> Result<Self, DeserializeError> {
|
||||
// Read the whole header
|
||||
let mut header_bytes = [0u8; Self::HEADER_SIZE];
|
||||
reader.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes)
|
||||
.map_err(DeserializeError::ReadHeader)?;
|
||||
|
||||
let header_bytes = array_split!(&header_bytes,
|
||||
magic : 0x1,
|
||||
|
||||
@ -41,7 +41,9 @@ impl Clut {
|
||||
pub fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, DeserializeError> {
|
||||
// Read the whole header
|
||||
let mut header_bytes = [0u8; Self::HEADER_SIZE];
|
||||
reader.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes)
|
||||
.map_err(DeserializeError::ReadHeader)?;
|
||||
|
||||
let header_bytes = array_split!(&header_bytes,
|
||||
length: [0x4],
|
||||
@ -65,10 +67,18 @@ impl Clut {
|
||||
|
||||
let mut colors = Vec::with_capacity(colors_len);
|
||||
for _ in 0..colors_len {
|
||||
let color = reader.read_u16::<LittleEndian>().map_err(DeserializeError::ReadColors)?;
|
||||
let color = reader
|
||||
.read_u16::<LittleEndian>()
|
||||
.map_err(DeserializeError::ReadColors)?;
|
||||
colors.push(color);
|
||||
}
|
||||
|
||||
Ok(Self { x, y, width, height, colors })
|
||||
Ok(Self {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
colors,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,9 +61,12 @@ impl Bytes for DecDateTime {
|
||||
Ok(Self {
|
||||
year: self::parse_decimal_string(bytes.year, [b'0', b'0', b'0', b'0'], [b'9', b'9', b'9', b'9'])
|
||||
.ok_or_else(|| FromBytesError::Year(*bytes.year))?,
|
||||
month: self::parse_decimal_string(bytes.month, [b'0', b'0'], [b'1', b'2']).ok_or_else(|| FromBytesError::Month(*bytes.month))?,
|
||||
day: self::parse_decimal_string(bytes.day, [b'0', b'0'], [b'3', b'1']).ok_or_else(|| FromBytesError::Day(*bytes.day))?,
|
||||
hour: self::parse_decimal_string(bytes.hour, [b'0', b'0'], [b'2', b'3']).ok_or_else(|| FromBytesError::Hour(*bytes.hour))?,
|
||||
month: self::parse_decimal_string(bytes.month, [b'0', b'0'], [b'1', b'2'])
|
||||
.ok_or_else(|| FromBytesError::Month(*bytes.month))?,
|
||||
day: self::parse_decimal_string(bytes.day, [b'0', b'0'], [b'3', b'1'])
|
||||
.ok_or_else(|| FromBytesError::Day(*bytes.day))?,
|
||||
hour: self::parse_decimal_string(bytes.hour, [b'0', b'0'], [b'2', b'3'])
|
||||
.ok_or_else(|| FromBytesError::Hour(*bytes.hour))?,
|
||||
minutes: self::parse_decimal_string(bytes.minutes, [b'0', b'0'], [b'5', b'9'])
|
||||
.ok_or_else(|| FromBytesError::Minute(*bytes.minutes))?,
|
||||
seconds: self::parse_decimal_string(bytes.seconds, [b'0', b'0'], [b'5', b'9'])
|
||||
@ -122,7 +125,8 @@ impl fmt::Debug for DecDateTime {
|
||||
|
||||
write!(
|
||||
f,
|
||||
"\"{year}-{month}-{day}T{hour}:{minutes}:{seconds}:{hundredths_sec}{time_zone_hours:+}:{time_zone_minutes}\""
|
||||
"\"{year}-{month}-{day}T{hour}:{minutes}:{seconds}:{hundredths_sec}{time_zone_hours:+}:\
|
||||
{time_zone_minutes}\""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,9 @@ impl DirEntry {
|
||||
}
|
||||
|
||||
/// Reads a file from this entry
|
||||
pub fn read_file<'a, R: io::Read + io::Seek>(&self, cdrom: &'a mut CdRomReader<R>) -> Result<FileReader<'a, R>, ReadFileError> {
|
||||
pub fn read_file<'a, R: io::Read + io::Seek>(
|
||||
&self, cdrom: &'a mut CdRomReader<R>,
|
||||
) -> Result<FileReader<'a, R>, ReadFileError> {
|
||||
// If this isn't a file, return Err
|
||||
if !self.is_file() {
|
||||
return Err(ReadFileError::NotAFile);
|
||||
@ -121,7 +123,9 @@ impl DirEntry {
|
||||
pub fn from_reader<R: io::Read>(reader: &mut R) -> Result<Self, FromReaderError> {
|
||||
// Get the header
|
||||
let mut header_bytes = [0; 0x21];
|
||||
reader.read_exact(&mut header_bytes).map_err(FromReaderError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes)
|
||||
.map_err(FromReaderError::ReadHeader)?;
|
||||
|
||||
let header_bytes = array_split!(&header_bytes,
|
||||
record_size : 0x1,
|
||||
@ -157,7 +161,9 @@ impl DirEntry {
|
||||
|
||||
// Then skip the remaining bytes
|
||||
let mut remaining = vec![0; usize::from(header_bytes.record_size - 0x21 - header_bytes.name_len)];
|
||||
reader.read_exact(&mut remaining).map_err(FromReaderError::ReadRemaining)?;
|
||||
reader
|
||||
.read_exact(&mut remaining)
|
||||
.map_err(FromReaderError::ReadRemaining)?;
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
@ -203,7 +209,9 @@ impl DirEntry {
|
||||
writer.write_all(&header_bytes).map_err(ToWriterError::WriteHeader)?;
|
||||
|
||||
// Then write the name
|
||||
writer.write_all(self.name.as_bytes()).map_err(ToWriterError::WriteName)?;
|
||||
writer
|
||||
.write_all(self.name.as_bytes())
|
||||
.map_err(ToWriterError::WriteName)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -69,7 +69,8 @@ impl<'a, R: io::Read> FileReader<'a, R> {
|
||||
impl<'a, R: io::Read> io::Read for FileReader<'a, R> {
|
||||
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
|
||||
// If buffer would go past the end of the file, cut it.
|
||||
let remaining_file_bytes = usize::try_from(self.remaining()).expect("Unable to get remaining file bytes as `usize`");
|
||||
let remaining_file_bytes =
|
||||
usize::try_from(self.remaining()).expect("Unable to get remaining file bytes as `usize`");
|
||||
if buf.len() > remaining_file_bytes {
|
||||
buf = &mut buf[..remaining_file_bytes];
|
||||
}
|
||||
@ -119,7 +120,9 @@ impl<'a, R: io::Seek> io::Seek for FileReader<'a, R> {
|
||||
// If we don't end up in the same sector, flush our sector and seek to the next sector
|
||||
if next_pos / 2048 != self.cur_pos / 2048 {
|
||||
self.cached = None;
|
||||
self.cdrom.seek_sector(self.sector_pos + next_pos / 2048).map_err(|err| err.err)?;
|
||||
self.cdrom
|
||||
.seek_sector(self.sector_pos + next_pos / 2048)
|
||||
.map_err(|err| err.err)?;
|
||||
}
|
||||
|
||||
// And set our position
|
||||
|
||||
@ -103,18 +103,28 @@ impl FilesystemReader {
|
||||
let mut sectors = cdrom.read_sectors();
|
||||
let primary_volume_descriptor = loop {
|
||||
match sectors.next() {
|
||||
Some(Ok(sector)) => match VolumeDescriptor::from_bytes(sector.data.as_form1().ok_or(NewError::PrimaryFormatWrongForm)?) {
|
||||
Ok(VolumeDescriptor::Primary(primary)) => break primary,
|
||||
Ok(VolumeDescriptor::SetTerminator) => return Err(NewError::MissingPrimaryVolumeBeforeSetTerminator),
|
||||
Ok(volume_descriptor) => log::debug!("Skipping {:?} volume descriptor before primary", volume_descriptor.kind()),
|
||||
Err(err) => return Err(NewError::InvalidVolumeDescriptor(err)),
|
||||
Some(Ok(sector)) => {
|
||||
match VolumeDescriptor::from_bytes(sector.data.as_form1().ok_or(NewError::PrimaryFormatWrongForm)?)
|
||||
{
|
||||
Ok(VolumeDescriptor::Primary(primary)) => break primary,
|
||||
Ok(VolumeDescriptor::SetTerminator) => {
|
||||
return Err(NewError::MissingPrimaryVolumeBeforeSetTerminator)
|
||||
},
|
||||
Ok(volume_descriptor) => log::debug!(
|
||||
"Skipping {:?} volume descriptor before primary",
|
||||
volume_descriptor.kind()
|
||||
),
|
||||
Err(err) => return Err(NewError::InvalidVolumeDescriptor(err)),
|
||||
}
|
||||
},
|
||||
Some(Err(err)) => return Err(NewError::InvalidSectorBeforeSetTerminator(err)),
|
||||
None => return Err(NewError::EofBeforeSetTerminator),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self { primary_volume_descriptor })
|
||||
Ok(Self {
|
||||
primary_volume_descriptor,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the primary volume descriptor
|
||||
|
||||
@ -72,9 +72,10 @@ pub struct AlphabetA;
|
||||
impl OnlyValidCharsAlphabet for AlphabetA {
|
||||
fn valid_chars() -> &'static [u8] {
|
||||
&[
|
||||
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
|
||||
b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'_', b'!', b'"', b'%', b'&', b'\'', b'(', b')', b'*',
|
||||
b'+', b',', b'-', b'.', b'/', b':', b';', b'<', b'=', b'>', b'?',
|
||||
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R',
|
||||
b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
|
||||
b'_', b'!', b'"', b'%', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'.', b'/', b':', b';', b'<',
|
||||
b'=', b'>', b'?',
|
||||
]
|
||||
}
|
||||
|
||||
@ -92,8 +93,9 @@ pub struct AlphabetD;
|
||||
impl OnlyValidCharsAlphabet for AlphabetD {
|
||||
fn valid_chars() -> &'static [u8] {
|
||||
&[
|
||||
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W',
|
||||
b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'_',
|
||||
b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R',
|
||||
b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
|
||||
b'_',
|
||||
]
|
||||
}
|
||||
|
||||
@ -129,12 +131,18 @@ impl Alphabet for FileAlphabet {
|
||||
// Separate into `<name>.<extension>;<version>`
|
||||
let (name, extension, version) = {
|
||||
// Separate into `<name>.<rest>` and ignore the `.` in `<rest>`
|
||||
let dot_idx = bytes.iter().position(|&b| b == b'.').ok_or(ValidateFileAlphabetError::MissingExtension)?;
|
||||
let dot_idx = bytes
|
||||
.iter()
|
||||
.position(|&b| b == b'.')
|
||||
.ok_or(ValidateFileAlphabetError::MissingExtension)?;
|
||||
let (name, rest) = bytes.split_at(dot_idx);
|
||||
let rest = &rest[1..];
|
||||
|
||||
// Then split at `<extension>;<version>` and ignore the `;`
|
||||
let version_idx = rest.iter().position(|&b| b == b';').ok_or(ValidateFileAlphabetError::MissingVersion)?;
|
||||
let version_idx = rest
|
||||
.iter()
|
||||
.position(|&b| b == b';')
|
||||
.ok_or(ValidateFileAlphabetError::MissingVersion)?;
|
||||
let (extension, version) = rest.split_at(version_idx);
|
||||
let version = &version[1..];
|
||||
|
||||
|
||||
@ -131,13 +131,20 @@ impl Bytes for PrimaryVolumeDescriptor {
|
||||
path_table_opt_location: LittleEndian::read_u32(bytes.path_table_lsb_opt_location),
|
||||
root_dir_entry: DirEntry::from_reader(&mut std::io::Cursor::new(bytes.root_dir_entry))
|
||||
.map_err(FromBytesError::RootDirEntry)?,
|
||||
volume_set_id: StrArrD::from_bytes(bytes.volume_set_id).map_err(FromBytesError::VolumeSetId)?,
|
||||
publisher_id: StrArrA::from_bytes(bytes.publisher_id).map_err(FromBytesError::PublisherId)?,
|
||||
data_preparer_id: StrArrA::from_bytes(bytes.data_preparer_id).map_err(FromBytesError::DataPreparerId)?,
|
||||
application_id: StrArrA::from_bytes(bytes.application_id).map_err(FromBytesError::ApplicationId)?,
|
||||
copyright_file_id: StrArrD::from_bytes(bytes.copyright_file_id).map_err(FromBytesError::CopyrightFileId)?,
|
||||
abstract_file_id: StrArrD::from_bytes(bytes.abstract_file_id).map_err(FromBytesError::AbstractFileId)?,
|
||||
bibliographic_file_id: StrArrD::from_bytes(bytes.bibliographic_file_id).map_err(FromBytesError::BibliographicFileId)?,
|
||||
volume_set_id: StrArrD::from_bytes(bytes.volume_set_id)
|
||||
.map_err(FromBytesError::VolumeSetId)?,
|
||||
publisher_id: StrArrA::from_bytes(bytes.publisher_id)
|
||||
.map_err(FromBytesError::PublisherId)?,
|
||||
data_preparer_id: StrArrA::from_bytes(bytes.data_preparer_id)
|
||||
.map_err(FromBytesError::DataPreparerId)?,
|
||||
application_id: StrArrA::from_bytes(bytes.application_id)
|
||||
.map_err(FromBytesError::ApplicationId)?,
|
||||
copyright_file_id: StrArrD::from_bytes(bytes.copyright_file_id)
|
||||
.map_err(FromBytesError::CopyrightFileId)?,
|
||||
abstract_file_id: StrArrD::from_bytes(bytes.abstract_file_id)
|
||||
.map_err(FromBytesError::AbstractFileId)?,
|
||||
bibliographic_file_id: StrArrD::from_bytes(bytes.bibliographic_file_id)
|
||||
.map_err(FromBytesError::BibliographicFileId)?,
|
||||
volume_creation_date_time: DecDateTime::from_bytes(bytes.volume_creation_date_time)
|
||||
.map_err(FromBytesError::VolumeCreationDateTime)?,
|
||||
volume_modification_date_time: DecDateTime::from_bytes(bytes.volume_modification_date_time)
|
||||
@ -206,10 +213,18 @@ impl Bytes for PrimaryVolumeDescriptor {
|
||||
self.copyright_file_id.to_bytes(bytes.copyright_file_id);
|
||||
self.abstract_file_id.to_bytes(bytes.abstract_file_id);
|
||||
self.bibliographic_file_id.to_bytes(bytes.bibliographic_file_id);
|
||||
self.volume_creation_date_time.to_bytes(bytes.volume_creation_date_time).into_ok();
|
||||
self.volume_modification_date_time.to_bytes(bytes.volume_modification_date_time).into_ok();
|
||||
self.volume_expiration_date_time.to_bytes(bytes.volume_expiration_date_time).into_ok();
|
||||
self.volume_effective_date_time.to_bytes(bytes.volume_effective_date_time).into_ok();
|
||||
self.volume_creation_date_time
|
||||
.to_bytes(bytes.volume_creation_date_time)
|
||||
.into_ok();
|
||||
self.volume_modification_date_time
|
||||
.to_bytes(bytes.volume_modification_date_time)
|
||||
.into_ok();
|
||||
self.volume_expiration_date_time
|
||||
.to_bytes(bytes.volume_expiration_date_time)
|
||||
.into_ok();
|
||||
self.volume_effective_date_time
|
||||
.to_bytes(bytes.volume_effective_date_time)
|
||||
.into_ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -42,7 +42,9 @@ impl<R: io::Read + io::Seek> PakFileReader<R> {
|
||||
/// Returns the next entry
|
||||
pub fn next_entry(&mut self) -> Result<Option<PakEntryReader<R>>, NextEntryError> {
|
||||
// Seek to our current position
|
||||
self.reader.seek(SeekFrom::Start(self.cur_pos)).map_err(NextEntryError::SeekNextEntry)?;
|
||||
self.reader
|
||||
.seek(SeekFrom::Start(self.cur_pos))
|
||||
.map_err(NextEntryError::SeekNextEntry)?;
|
||||
|
||||
// Try to read an entry
|
||||
let entry = match PakEntryReader::from_reader(&mut self.reader).map_err(NextEntryError::ReadEntry)? {
|
||||
|
||||
@ -35,7 +35,9 @@ impl<'a, R: io::Read> PakEntryReader<'a, R> {
|
||||
// Read the header
|
||||
// Note: We do a two-part read so we can quit early if we find `0xffffffff`
|
||||
let mut header_bytes = [0u8; 0x8];
|
||||
reader.read_exact(&mut header_bytes[..0x4]).map_err(FromReaderError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes[..0x4])
|
||||
.map_err(FromReaderError::ReadHeader)?;
|
||||
|
||||
// If we found `0xffffffff`, this is the final header, return
|
||||
if header_bytes[..0x4] == [0xff; 4] {
|
||||
@ -43,7 +45,9 @@ impl<'a, R: io::Read> PakEntryReader<'a, R> {
|
||||
}
|
||||
|
||||
// Then read the rest and parse the header
|
||||
reader.read_exact(&mut header_bytes[0x4..]).map_err(FromReaderError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes[0x4..])
|
||||
.map_err(FromReaderError::ReadHeader)?;
|
||||
let header = Header::from_bytes(&header_bytes).map_err(FromReaderError::ParseHeader)?;
|
||||
|
||||
Ok(Some(Self { header, reader }))
|
||||
|
||||
@ -58,7 +58,9 @@ impl Animation2d {
|
||||
pub fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, DeserializeError> {
|
||||
// Read the header
|
||||
let mut header_bytes = [0; Self::HEADER_SIZE];
|
||||
reader.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes)
|
||||
.map_err(DeserializeError::ReadHeader)?;
|
||||
|
||||
let header_bytes = array_split!(&header_bytes,
|
||||
file_name : [0xc],
|
||||
@ -72,13 +74,18 @@ impl Animation2d {
|
||||
unknown6 : [0x4],
|
||||
);
|
||||
|
||||
let file_name = header_bytes.file_name.read_string().map_err(DeserializeError::ParseName)?;
|
||||
let file_name = header_bytes
|
||||
.file_name
|
||||
.read_string()
|
||||
.map_err(DeserializeError::ParseName)?;
|
||||
let frames_len = LittleEndian::read_u32(header_bytes.frames_len);
|
||||
|
||||
let mut frames = vec![];
|
||||
for _ in 0..frames_len {
|
||||
let mut frame_bytes = [0; 0x14];
|
||||
reader.read_exact(&mut frame_bytes).map_err(DeserializeError::ReadFrame)?;
|
||||
reader
|
||||
.read_exact(&mut frame_bytes)
|
||||
.map_err(DeserializeError::ReadFrame)?;
|
||||
let frame = Frame::from_bytes(&frame_bytes).into_ok();
|
||||
|
||||
frames.push(frame);
|
||||
|
||||
@ -36,7 +36,9 @@ impl Model3dSet {
|
||||
pub fn from_reader<R: io::Read + io::Seek>(reader: &mut R) -> Result<Self, FromReaderError> {
|
||||
// Read the header
|
||||
let mut header_bytes = [0; Self::HEADER_SIZE];
|
||||
reader.read_exact(&mut header_bytes).map_err(FromReaderError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes)
|
||||
.map_err(FromReaderError::ReadHeader)?;
|
||||
|
||||
let header_bytes = array_split!(&header_bytes,
|
||||
file_name : [0x10],
|
||||
@ -44,12 +46,18 @@ impl Model3dSet {
|
||||
unknown0 : [0x20],
|
||||
);
|
||||
|
||||
let file_name = header_bytes.file_name.read_string().map_err(FromReaderError::ParseName)?;
|
||||
let models_len = usize::try_from(LittleEndian::read_u32(header_bytes.models_len)).expect("Model length didn't fit into `usize`");
|
||||
let file_name = header_bytes
|
||||
.file_name
|
||||
.read_string()
|
||||
.map_err(FromReaderError::ParseName)?;
|
||||
let models_len = usize::try_from(LittleEndian::read_u32(header_bytes.models_len))
|
||||
.expect("Model length didn't fit into `usize`");
|
||||
|
||||
// Read the unknown 1
|
||||
let mut unknown1_bytes = vec![0; models_len * 3 * 4];
|
||||
reader.read_exact(&mut unknown1_bytes).map_err(FromReaderError::ReadUnknown1)?;
|
||||
reader
|
||||
.read_exact(&mut unknown1_bytes)
|
||||
.map_err(FromReaderError::ReadUnknown1)?;
|
||||
|
||||
// Read the models
|
||||
let mut models = vec![];
|
||||
|
||||
@ -33,7 +33,9 @@ impl TmdModel {
|
||||
pub fn from_reader<R: io::Read + io::Seek>(reader: &mut R) -> Result<Self, FromReaderError> {
|
||||
// Read the header
|
||||
let mut header_bytes = [0; Self::HEADER_SIZE];
|
||||
reader.read_exact(&mut header_bytes).map_err(FromReaderError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes)
|
||||
.map_err(FromReaderError::ReadHeader)?;
|
||||
let header_bytes = array_split!(&header_bytes,
|
||||
magic : [0x4],
|
||||
flags : [0x4],
|
||||
@ -51,7 +53,8 @@ impl TmdModel {
|
||||
todo!("Flags other than `0x0` ({:#x}) are not supported", flags);
|
||||
}
|
||||
|
||||
let objs_len = usize::try_from(LittleEndian::read_u32(header_bytes.objs_len)).expect("Unable to get object number as `usize`");
|
||||
let objs_len = usize::try_from(LittleEndian::read_u32(header_bytes.objs_len))
|
||||
.expect("Unable to get object number as `usize`");
|
||||
|
||||
// TODO: Support more than 1 object
|
||||
if objs_len != 1 {
|
||||
|
||||
@ -33,14 +33,19 @@ use std::{
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli = cli::CliData::new();
|
||||
|
||||
// Get the header
|
||||
let header_file = fs::File::open(&cli.header_path).with_context(|| format!("Unable to open header file {}", cli.header_path.display()))?;
|
||||
let header_file = fs::File::open(&cli.header_path)
|
||||
.with_context(|| format!("Unable to open header file {}", cli.header_path.display()))?;
|
||||
let header: Header = serde_yaml::from_reader(header_file).context("Unable to read header file")?;
|
||||
|
||||
// Open the input and output file
|
||||
@ -70,7 +75,12 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
.range(..=cur_pos)
|
||||
.filter_map(|(_, label)| label.as_global())
|
||||
.next_back()
|
||||
.ok_or_else(|| (n, anyhow::anyhow!("Cannot define a local label before any global labels")))?;
|
||||
.ok_or_else(|| {
|
||||
(
|
||||
n,
|
||||
anyhow::anyhow!("Cannot define a local label before any global labels"),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Then insert it
|
||||
let mut name = label.name;
|
||||
@ -79,7 +89,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
Label::Local { name: LabelName(name) }
|
||||
},
|
||||
// It's global
|
||||
false => Label::Global { name: LabelName(label.name) },
|
||||
false => Label::Global {
|
||||
name: LabelName(label.name),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -102,7 +114,12 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
.range(..=cur_pos)
|
||||
.filter_map(|(_, label)| label.as_global())
|
||||
.next_back()
|
||||
.ok_or_else(|| (n, anyhow::anyhow!("Cannot define a local label before any global labels")))?;
|
||||
.ok_or_else(|| {
|
||||
(
|
||||
n,
|
||||
anyhow::anyhow!("Cannot define a local label before any global labels"),
|
||||
)
|
||||
})?;
|
||||
|
||||
// Then insert it
|
||||
name.insert_str(0, prev_label_name);
|
||||
@ -129,8 +146,10 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
};
|
||||
|
||||
// Read all foreign data as labels.
|
||||
let foreign_data_file = std::fs::File::open("resources/foreign_data.yaml").context("Unable to open foreign data file")?;
|
||||
let foreign_data: Vec<Data> = serde_yaml::from_reader(foreign_data_file).context("Unable to read foreign data file")?;
|
||||
let foreign_data_file =
|
||||
std::fs::File::open("resources/foreign_data.yaml").context("Unable to open foreign data file")?;
|
||||
let foreign_data: Vec<Data> =
|
||||
serde_yaml::from_reader(foreign_data_file).context("Unable to read foreign data file")?;
|
||||
for data in foreign_data {
|
||||
let (pos, name) = data.into_label();
|
||||
labels_by_name.insert(name, pos);
|
||||
@ -149,8 +168,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
labels_by_name: &labels_by_name,
|
||||
};
|
||||
|
||||
let inst =
|
||||
Inst::parse(&inst.mnemonic, &inst.args, &ctx).with_context(|| format!("Unable to compile instruction at {} in line {}", pos, n + 1))?;
|
||||
let inst = Inst::parse(&inst.mnemonic, &inst.args, &ctx)
|
||||
.with_context(|| format!("Unable to compile instruction at {} in line {}", pos, n + 1))?;
|
||||
|
||||
inst.write(&mut output_file).context("Unable to write to file")?;
|
||||
}
|
||||
@ -172,10 +191,14 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
initial_sp_offset: header.initial_sp_offset,
|
||||
marker: header.marker,
|
||||
};
|
||||
output_file.seek(SeekFrom::Start(0)).context("Unable to seek stream to beginning")?;
|
||||
output_file
|
||||
.seek(SeekFrom::Start(0))
|
||||
.context("Unable to seek stream to beginning")?;
|
||||
let mut header_bytes = [0; 0x800];
|
||||
header.to_bytes(&mut header_bytes).into_ok();
|
||||
output_file.write_all(&header_bytes).context("Unable to write header to output file")?;
|
||||
output_file
|
||||
.write_all(&header_bytes)
|
||||
.context("Unable to write header to output file")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -25,8 +25,12 @@ use std::{collections::BTreeMap, fmt, path::PathBuf};
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli = cli::CliData::new();
|
||||
@ -208,7 +212,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
|
||||
/// Returns a display-able for an instruction inside a possible function
|
||||
#[must_use]
|
||||
pub fn inst_display<'a>(inst: &'a Inst, exe: &'a ExeReader, func: Option<&'a Func>, pos: Pos) -> impl fmt::Display + 'a {
|
||||
pub fn inst_display<'a>(
|
||||
inst: &'a Inst, exe: &'a ExeReader, func: Option<&'a Func>, pos: Pos,
|
||||
) -> impl fmt::Display + 'a {
|
||||
// Overload the target of as many as possible using `inst_target`.
|
||||
dcb_util::DisplayWrapper::new(move |f| {
|
||||
// Build the context and get the mnemonic + args
|
||||
@ -284,7 +290,13 @@ impl<'a> DisplayCtx for Ctx<'a> {
|
||||
if let Some(func) = self.exe.func_table().get_containing(pos) {
|
||||
// And then one of it's labels
|
||||
if let Some(label) = func.labels.get(&pos) {
|
||||
return Some((LabelDisplay::OtherFuncLabel { func: &func.name, label }, 0));
|
||||
return Some((
|
||||
LabelDisplay::OtherFuncLabel {
|
||||
func: &func.name,
|
||||
label,
|
||||
},
|
||||
0,
|
||||
));
|
||||
}
|
||||
|
||||
// Else just any position in it
|
||||
|
||||
@ -22,11 +22,19 @@ impl CliData {
|
||||
.version("0.1")
|
||||
.author("Filipe [...] <[...]@gmail.com>")
|
||||
.about("Coverts an ISO 9660 filesystem file to a `.bin` file in the CdRom/XA format")
|
||||
.arg(ClapArg::with_name("INPUT_FILE").help("The input file to use").required(true).index(1))
|
||||
.arg(
|
||||
ClapArg::with_name("INPUT_FILE")
|
||||
.help("The input file to use")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
ClapArg::with_name("OUTPUT")
|
||||
.help("The file to output to")
|
||||
.long_help("The file to output to. If not specified, a file with the input's name appended by `.bin` will be used")
|
||||
.long_help(
|
||||
"The file to output to. If not specified, a file with the input's name appended by `.bin` \
|
||||
will be used",
|
||||
)
|
||||
.short("o")
|
||||
.long("output")
|
||||
.takes_value(true)
|
||||
@ -56,6 +64,9 @@ impl CliData {
|
||||
};
|
||||
|
||||
// Return the data
|
||||
Self { input_file, output_file }
|
||||
Self {
|
||||
input_file,
|
||||
output_file,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,11 +18,18 @@ use std::{
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli::CliData { input_file, output_file } = cli::CliData::new();
|
||||
let cli::CliData {
|
||||
input_file,
|
||||
output_file,
|
||||
} = cli::CliData::new();
|
||||
|
||||
// Try to pack it into a `CdRom/XA`
|
||||
self::pack_cdrom_xa(&input_file, &output_file).context("Unable to pack file")?;
|
||||
|
||||
@ -22,11 +22,19 @@ impl CliData {
|
||||
.version("0.1")
|
||||
.author("Filipe [...] <[...]@gmail.com>")
|
||||
.about("Packs a folder into a `.drv` file")
|
||||
.arg(ClapArg::with_name("INPUT_DIR").help("The input directory to use").required(true).index(1))
|
||||
.arg(
|
||||
ClapArg::with_name("INPUT_DIR")
|
||||
.help("The input directory to use")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
ClapArg::with_name("OUTPUT")
|
||||
.help("The file to output to")
|
||||
.long_help("The file to output to. If not specified, a file with the directory's name appended by `.drv` will be used")
|
||||
.long_help(
|
||||
"The file to output to. If not specified, a file with the directory's name appended by `.drv` \
|
||||
will be used",
|
||||
)
|
||||
.short("o")
|
||||
.long("output")
|
||||
.takes_value(true)
|
||||
|
||||
@ -107,7 +107,10 @@ impl IntoIterator for DirLister {
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map_err(NextError::EntryDateSinceEpoch)?
|
||||
.as_secs();
|
||||
let date = chrono::NaiveDateTime::from_timestamp(i64::try_from(secs_since_epoch).map_err(|_err| NextError::EntryDateI64Secs)?, 0);
|
||||
let date = chrono::NaiveDateTime::from_timestamp(
|
||||
i64::try_from(secs_since_epoch).map_err(|_err| NextError::EntryDateI64Secs)?,
|
||||
0,
|
||||
);
|
||||
|
||||
// Check if it's a directory or file
|
||||
let kind = match entry.metadata.is_dir() {
|
||||
|
||||
@ -15,8 +15,12 @@ use std::{fs, path::Path};
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli::CliData { input_dir, output_file } = cli::CliData::new();
|
||||
@ -33,13 +37,19 @@ fn pack_filesystem(input_dir: &Path, output_file: &Path) -> Result<(), anyhow::E
|
||||
let mut output_file = fs::File::create(output_file).context("Unable to create output file")?;
|
||||
|
||||
// Create the filesystem writer
|
||||
let root_entries = dir_lister::DirLister::new(input_dir).context("Unable to create new dir lister for root directory")?;
|
||||
let root_entries =
|
||||
dir_lister::DirLister::new(input_dir).context("Unable to create new dir lister for root directory")?;
|
||||
DrvFsWriter::write_fs(&mut output_file, root_entries).context("Unable to write filesystem")?;
|
||||
|
||||
// Then pad the file to a sector `2048` if it isn't already
|
||||
let len = output_file.metadata().context("Unable to get output file metadata")?.len();
|
||||
let len = output_file
|
||||
.metadata()
|
||||
.context("Unable to get output file metadata")?
|
||||
.len();
|
||||
if len % 2048 != 0 {
|
||||
output_file.set_len(2048 * ((len + 2047) / 2048)).context("Unable to set file length")?;
|
||||
output_file
|
||||
.set_len(2048 * ((len + 2047) / 2048))
|
||||
.context("Unable to set file length")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -13,8 +13,12 @@ use std::{fs, path::PathBuf};
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli_data = CliData::new();
|
||||
|
||||
@ -22,11 +22,19 @@ impl CliData {
|
||||
.version("0.1")
|
||||
.author("Filipe [...] <[...]@gmail.com>")
|
||||
.about("Coverts a `.bin` in the CdRom/XA format to a ISO 9660 filesystem file")
|
||||
.arg(ClapArg::with_name("INPUT_FILE").help("The input file to use").required(true).index(1))
|
||||
.arg(
|
||||
ClapArg::with_name("INPUT_FILE")
|
||||
.help("The input file to use")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
ClapArg::with_name("OUTPUT")
|
||||
.help("The file to output to")
|
||||
.long_help("The file to output to. If not specified, a file with the input's name changed by `.iso` will be used")
|
||||
.long_help(
|
||||
"The file to output to. If not specified, a file with the input's name changed by `.iso` will \
|
||||
be used",
|
||||
)
|
||||
.short("o")
|
||||
.long("output")
|
||||
.takes_value(true)
|
||||
@ -50,6 +58,9 @@ impl CliData {
|
||||
};
|
||||
|
||||
// Return the data
|
||||
Self { input_file, output_file }
|
||||
Self {
|
||||
input_file,
|
||||
output_file,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,11 +11,18 @@ use std::{fs, io::Write, path::Path};
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli::CliData { input_file, output_file } = cli::CliData::new();
|
||||
let cli::CliData {
|
||||
input_file,
|
||||
output_file,
|
||||
} = cli::CliData::new();
|
||||
|
||||
// Try to extract it into a `iso`
|
||||
self::extract_cdrom_xa(&input_file, &output_file).context("Unable to extract file")?;
|
||||
@ -36,7 +43,9 @@ fn extract_cdrom_xa(input_file: &Path, output_file: &Path) -> Result<(), anyhow:
|
||||
for sector in input_file.read_sectors() {
|
||||
let sector = sector.context("Unable to read sector")?;
|
||||
|
||||
output_file.write_all(sector.data.as_ref()).context("Unable to write data")?;
|
||||
output_file
|
||||
.write_all(sector.data.as_ref())
|
||||
.context("Unable to write data")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -42,8 +42,8 @@ impl CliData {
|
||||
ClapArg::with_name(OUTPUT_DIR_STR)
|
||||
.help("The directory to output to")
|
||||
.long_help(
|
||||
"The directory to output to. If not specified, the parent of the input file is used. If it doesn't exist, the current \
|
||||
directory is used",
|
||||
"The directory to output to. If not specified, the parent of the input file is used. If it \
|
||||
doesn't exist, the current directory is used",
|
||||
)
|
||||
.short("o")
|
||||
.long("output-dir")
|
||||
@ -53,7 +53,8 @@ impl CliData {
|
||||
ClapArg::with_name(QUIET_STR)
|
||||
.help("Suppresses listing of extracted files")
|
||||
.long_help(
|
||||
"Suppresses printing on `stdout` of this program. If any errors or warnings occur, they will still be printed to stderr",
|
||||
"Suppresses printing on `stdout` of this program. If any errors or warnings occur, they will \
|
||||
still be printed to stderr",
|
||||
)
|
||||
.short("q")
|
||||
.long("quiet"),
|
||||
@ -61,7 +62,9 @@ impl CliData {
|
||||
.arg(
|
||||
ClapArg::with_name(WARN_ON_OVERRIDE_STR)
|
||||
.help("Warns when overriding files that already exist.")
|
||||
.long_help("Warns if this program would override existing files. By default no warnings are produced.")
|
||||
.long_help(
|
||||
"Warns if this program would override existing files. By default no warnings are produced.",
|
||||
)
|
||||
.long("warn-on-override"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
@ -14,8 +14,12 @@ use std::{
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli_data = CliData::new();
|
||||
@ -37,7 +41,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let mut input_file = io::BufReader::new(input_file);
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
dcb_util::try_create_folder(&output_dir).with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
dcb_util::try_create_folder(&output_dir)
|
||||
.with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
|
||||
// Then extract the tree
|
||||
if let Err(err) = self::extract_tree(&mut input_file, &DrvFsReader::root(), &output_dir, &cli_data) {
|
||||
@ -59,7 +64,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
}
|
||||
|
||||
/// Extracts a `.drv` file from a reader and starting directory
|
||||
fn extract_tree<R: io::Read + io::Seek>(reader: &mut R, dir: &DirReader, path: &Path, cli_data: &CliData) -> Result<(), anyhow::Error> {
|
||||
fn extract_tree<R: io::Read + io::Seek>(
|
||||
reader: &mut R, dir: &DirReader, path: &Path, cli_data: &CliData,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
// Get all entries
|
||||
// Note: We need to collect to free the reader so it can seek to the next files.
|
||||
let entries: Vec<_> = dir
|
||||
@ -89,7 +96,11 @@ fn extract_tree<R: io::Read + io::Seek>(reader: &mut R, dir: &DirReader, path: &
|
||||
DirEntryReaderKind::File(file) => {
|
||||
// Log the file and it's size
|
||||
if !cli_data.quiet {
|
||||
println!("{} ({}B)", path.display(), size_format::SizeFormatterSI::new(u64::from(file.size())));
|
||||
println!(
|
||||
"{} ({}B)",
|
||||
path.display(),
|
||||
size_format::SizeFormatterSI::new(u64::from(file.size()))
|
||||
);
|
||||
}
|
||||
|
||||
// If the output file already exists, log a warning
|
||||
@ -98,15 +109,23 @@ fn extract_tree<R: io::Read + io::Seek>(reader: &mut R, dir: &DirReader, path: &
|
||||
}
|
||||
|
||||
// Get the file's reader.
|
||||
let mut file_reader = file.reader(reader).with_context(|| format!("Unable to read file {}", path.display()))?;
|
||||
let mut file_reader = file
|
||||
.reader(reader)
|
||||
.with_context(|| format!("Unable to read file {}", path.display()))?;
|
||||
|
||||
// Then create the output file and copy.
|
||||
let mut output_file = fs::File::create(&path).with_context(|| format!("Unable to create file {}", path.display()))?;
|
||||
std::io::copy(&mut file_reader, &mut output_file).with_context(|| format!("Unable to write file {}", path.display()))?;
|
||||
let mut output_file =
|
||||
fs::File::create(&path).with_context(|| format!("Unable to create file {}", path.display()))?;
|
||||
std::io::copy(&mut file_reader, &mut output_file)
|
||||
.with_context(|| format!("Unable to write file {}", path.display()))?;
|
||||
|
||||
// And set the file's modification time
|
||||
if let Err(err) = filetime::set_file_handle_times(&output_file, None, Some(time)) {
|
||||
log::warn!("Unable to write date for file {}: {}", path.display(), dcb_util::fmt_err_wrapper(&err));
|
||||
log::warn!(
|
||||
"Unable to write date for file {}: {}",
|
||||
path.display(),
|
||||
dcb_util::fmt_err_wrapper(&err)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@ -118,8 +137,10 @@ fn extract_tree<R: io::Read + io::Seek>(reader: &mut R, dir: &DirReader, path: &
|
||||
}
|
||||
|
||||
// Create the directory and recurse over it
|
||||
dcb_util::try_create_folder(&path).with_context(|| format!("Unable to create directory {}", path.display()))?;
|
||||
self::extract_tree(reader, dir, &path, &cli_data).with_context(|| format!("Unable to extract directory {}", path.display()))?;
|
||||
dcb_util::try_create_folder(&path)
|
||||
.with_context(|| format!("Unable to create directory {}", path.display()))?;
|
||||
self::extract_tree(reader, dir, &path, &cli_data)
|
||||
.with_context(|| format!("Unable to extract directory {}", path.display()))?;
|
||||
|
||||
// Then set it's date
|
||||
// Note: We must do this *after* extracting the tree, else the time
|
||||
|
||||
@ -36,8 +36,8 @@ impl CliData {
|
||||
ClapArg::with_name(OUTPUT_DIR_STR)
|
||||
.help("The directory to output to")
|
||||
.long_help(
|
||||
"The directory to output to. If not specified, the parent of the input file is used. If it doesn't exist, the current \
|
||||
directory is used",
|
||||
"The directory to output to. If not specified, the parent of the input file is used. If it \
|
||||
doesn't exist, the current directory is used",
|
||||
)
|
||||
.short("o")
|
||||
.long("output-dir")
|
||||
|
||||
@ -16,8 +16,12 @@ use std::{fs, io, path::PathBuf};
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli_data = CliData::new();
|
||||
@ -32,7 +36,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
};
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
dcb_util::try_create_folder(&output_dir).with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
dcb_util::try_create_folder(&output_dir)
|
||||
.with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
|
||||
// Open the file.
|
||||
let input_file = fs::File::open(&cli_data.input_file).context("Unable to open input file")?;
|
||||
@ -51,7 +56,9 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let date_time_to_string = |date_time: DecDateTime| {
|
||||
let mut bytes = [0; 0x11];
|
||||
date_time.to_bytes(&mut bytes).into_ok();
|
||||
std::str::from_utf8(&bytes).expect("Date time was invalid utf8").to_owned()
|
||||
std::str::from_utf8(&bytes)
|
||||
.expect("Date time was invalid utf8")
|
||||
.to_owned()
|
||||
};
|
||||
|
||||
let volume = fs_reader.primary_volume_descriptor();
|
||||
|
||||
@ -44,8 +44,8 @@ impl CliData {
|
||||
ClapArg::with_name(OUTPUT_DIR_STR)
|
||||
.help("The directory to output to")
|
||||
.long_help(
|
||||
"The directory to output to. If not specified, the parent of the input file is used. If it doesn't exist, the current \
|
||||
directory is used",
|
||||
"The directory to output to. If not specified, the parent of the input file is used. If it \
|
||||
doesn't exist, the current directory is used",
|
||||
)
|
||||
.short("o")
|
||||
.long("output-dir")
|
||||
@ -55,7 +55,8 @@ impl CliData {
|
||||
ClapArg::with_name(QUIET_STR)
|
||||
.help("Suppresses listing of extracted files")
|
||||
.long_help(
|
||||
"Suppresses printing on `stdout` of this program. If any errors or warnings occur, they will still be printed to stderr",
|
||||
"Suppresses printing on `stdout` of this program. If any errors or warnings occur, they will \
|
||||
still be printed to stderr",
|
||||
)
|
||||
.short("q")
|
||||
.long("quiet"),
|
||||
@ -63,7 +64,9 @@ impl CliData {
|
||||
.arg(
|
||||
ClapArg::with_name(WARN_ON_OVERRIDE_STR)
|
||||
.help("Warns when overriding files that already exist.")
|
||||
.long_help("Warns if this program would override existing files. By default no warnings are produced.")
|
||||
.long_help(
|
||||
"Warns if this program would override existing files. By default no warnings are produced.",
|
||||
)
|
||||
.long("warn-on-override"),
|
||||
)
|
||||
.arg(
|
||||
|
||||
@ -15,8 +15,12 @@ use std::{
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(log::LevelFilter::Info, simplelog::Config::default(), simplelog::TerminalMode::Stderr)
|
||||
.expect("Unable to initialize logger");
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Info,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Get all data from cli
|
||||
let cli_data = CliData::new();
|
||||
@ -50,7 +54,8 @@ fn extract_file(input_file_path: &Path, output_dir: &Path, cli_data: &CliData) -
|
||||
|
||||
// Try to create the output directory if we're not just listing
|
||||
if !cli_data.only_list {
|
||||
dcb_util::try_create_folder(output_dir).with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
dcb_util::try_create_folder(output_dir)
|
||||
.with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
}
|
||||
|
||||
// Then read all entries
|
||||
@ -97,8 +102,10 @@ fn extract_file(input_file_path: &Path, output_dir: &Path, cli_data: &CliData) -
|
||||
log::warn!("Overriding file {}", path.display());
|
||||
}
|
||||
|
||||
let mut output_file = fs::File::create(&path).with_context(|| format!("Unable to create file {}", path.display()))?;
|
||||
io::copy(&mut contents, &mut output_file).with_context(|| format!("Unable to write file {}", path.display()))?;
|
||||
let mut output_file =
|
||||
fs::File::create(&path).with_context(|| format!("Unable to create file {}", path.display()))?;
|
||||
io::copy(&mut contents, &mut output_file)
|
||||
.with_context(|| format!("Unable to write file {}", path.display()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -11,7 +11,12 @@ type BoxedLogger = Box<dyn SharedLogger>;
|
||||
pub fn init() {
|
||||
// All loggers to try and initialize
|
||||
let loggers = [
|
||||
Some(TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Stderr)).map(|logger| BoxedLogger::from(logger)),
|
||||
Some(TermLogger::new(
|
||||
LevelFilter::Info,
|
||||
Config::default(),
|
||||
TerminalMode::Stderr,
|
||||
))
|
||||
.map(|logger| BoxedLogger::from(logger)),
|
||||
std::fs::File::create("latest.log")
|
||||
.ok()
|
||||
.map(|file| WriteLogger::new(LevelFilter::Debug, Config::default(), file))
|
||||
|
||||
@ -33,7 +33,8 @@ fn extract_file(input_file: &Path, output_dir: &Path) -> Result<(), anyhow::Erro
|
||||
|
||||
let model_set = Model3dSet::from_reader(&mut input_file).context("Unable to parse file")?;
|
||||
|
||||
dcb_util::try_create_folder(output_dir).with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
dcb_util::try_create_folder(output_dir)
|
||||
.with_context(|| format!("Unable to create directory {}", output_dir.display()))?;
|
||||
for (idx, (pos, size, ..)) in model_set.models.iter().enumerate() {
|
||||
// Get the filename
|
||||
let path = output_dir.join(format!("{}.TMD", idx));
|
||||
@ -50,8 +51,10 @@ fn extract_file(input_file: &Path, output_dir: &Path) -> Result<(), anyhow::Erro
|
||||
if path.exists() {
|
||||
log::warn!("Overriding file {}", path.display());
|
||||
}
|
||||
let mut output_file = std::fs::File::create(&path).with_context(|| format!("Unable to create file {}", path.display()))?;
|
||||
std::io::copy(&mut input_file, &mut output_file).with_context(|| format!("Unable to write file {}", path.display()))?;
|
||||
let mut output_file =
|
||||
std::fs::File::create(&path).with_context(|| format!("Unable to create file {}", path.display()))?;
|
||||
std::io::copy(&mut input_file, &mut output_file)
|
||||
.with_context(|| format!("Unable to write file {}", path.display()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -131,7 +131,10 @@ impl<const N: usize> AsciiStrArr<N> {
|
||||
*uninit = ascii;
|
||||
}
|
||||
|
||||
Ok(Self { chars, len: ascii.len() })
|
||||
Ok(Self {
|
||||
chars,
|
||||
len: ascii.len(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a string from bytes
|
||||
@ -312,7 +315,10 @@ impl<const N: usize> TryFrom<&[u8; N]> for AsciiStrArr<N> {
|
||||
*ascii = AsciiChar::from_ascii(byte).map_err(|_err| NotAsciiError { pos })?;
|
||||
}
|
||||
|
||||
Ok(Self { chars, len: byte_str.len() })
|
||||
Ok(Self {
|
||||
chars,
|
||||
len: byte_str.len(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -76,7 +76,9 @@ where
|
||||
(None, Some((key, right))) => Some((key, ParIterValue::Right(right))),
|
||||
|
||||
// If we got both with equal keys, return them both
|
||||
(Some((left_key, left)), Some((right_key, right))) if left_key == right_key => Some((left_key, ParIterValue::Both(left, right))),
|
||||
(Some((left_key, left)), Some((right_key, right))) if left_key == right_key => {
|
||||
Some((left_key, ParIterValue::Both(left, right)))
|
||||
},
|
||||
|
||||
// If we got both, but without equal keys, emit the first and store the other.
|
||||
// Note: In all of these branches, `self.value` is empty, as we call both `self.next_{left, right}` functions.
|
||||
|
||||
@ -8,8 +8,8 @@ pub use error::{FromBytesError, ToBytesError};
|
||||
|
||||
// Imports
|
||||
use crate::card::property::{
|
||||
ArrowColor, CrossMoveEffect, Effect, EffectCondition, Level, MaybeArrowColor, MaybeCrossMoveEffect, MaybeEffect, MaybeEffectCondition, Move,
|
||||
Speciality,
|
||||
ArrowColor, CrossMoveEffect, Effect, EffectCondition, Level, MaybeArrowColor, MaybeCrossMoveEffect, MaybeEffect,
|
||||
MaybeEffectCondition, Move, Speciality,
|
||||
};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use dcb_bytes::Bytes;
|
||||
@ -124,7 +124,8 @@ impl Bytes for Digimon {
|
||||
Ok(Self {
|
||||
name: NullAsciiString::read_string(bytes.name).map_err(FromBytesError::Name)?,
|
||||
|
||||
speciality: Speciality::from_bytes(&((bytes.speciality_level & 0xF0) >> 4u8)).map_err(FromBytesError::Speciality)?,
|
||||
speciality: Speciality::from_bytes(&((bytes.speciality_level & 0xF0) >> 4u8))
|
||||
.map_err(FromBytesError::Speciality)?,
|
||||
|
||||
level: Level::from_bytes(&(bytes.speciality_level & 0x0F)).map_err(FromBytesError::Level)?,
|
||||
|
||||
@ -149,9 +150,15 @@ impl Bytes for Digimon {
|
||||
],
|
||||
|
||||
effects: [
|
||||
MaybeEffect::from_bytes(bytes.effect_first).map_err(FromBytesError::EffectFirst)?.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_second).map_err(FromBytesError::EffectSecond)?.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_third).map_err(FromBytesError::EffectThird)?.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_first)
|
||||
.map_err(FromBytesError::EffectFirst)?
|
||||
.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_second)
|
||||
.map_err(FromBytesError::EffectSecond)?
|
||||
.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_third)
|
||||
.map_err(FromBytesError::EffectThird)?
|
||||
.into(),
|
||||
],
|
||||
|
||||
cross_move_effect: MaybeCrossMoveEffect::from_bytes(bytes.cross_move_effect)
|
||||
@ -163,10 +170,22 @@ impl Bytes for Digimon {
|
||||
.into(),
|
||||
|
||||
effect_description: [
|
||||
bytes.effect_description_0.read_string().map_err(FromBytesError::EffectDescription1)?,
|
||||
bytes.effect_description_1.read_string().map_err(FromBytesError::EffectDescription2)?,
|
||||
bytes.effect_description_2.read_string().map_err(FromBytesError::EffectDescription3)?,
|
||||
bytes.effect_description_3.read_string().map_err(FromBytesError::EffectDescription4)?,
|
||||
bytes
|
||||
.effect_description_0
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription1)?,
|
||||
bytes
|
||||
.effect_description_1
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription2)?,
|
||||
bytes
|
||||
.effect_description_2
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription3)?,
|
||||
bytes
|
||||
.effect_description_3
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription4)?,
|
||||
],
|
||||
|
||||
// Unknown
|
||||
|
||||
@ -72,10 +72,22 @@ impl Bytes for Digivolve {
|
||||
|
||||
// Effect
|
||||
effect_description: [
|
||||
bytes.effect_description_0.read_string().map_err(FromBytesError::EffectDescription1)?,
|
||||
bytes.effect_description_1.read_string().map_err(FromBytesError::EffectDescription2)?,
|
||||
bytes.effect_description_2.read_string().map_err(FromBytesError::EffectDescription3)?,
|
||||
bytes.effect_description_3.read_string().map_err(FromBytesError::EffectDescription4)?,
|
||||
bytes
|
||||
.effect_description_0
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription1)?,
|
||||
bytes
|
||||
.effect_description_1
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription2)?,
|
||||
bytes
|
||||
.effect_description_2
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription3)?,
|
||||
bytes
|
||||
.effect_description_3
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription4)?,
|
||||
],
|
||||
|
||||
// Unknown
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
#![doc(include = "item.md")]
|
||||
|
||||
// Imports
|
||||
use crate::card::property::{self, ArrowColor, Effect, EffectCondition, MaybeArrowColor, MaybeEffect, MaybeEffectCondition};
|
||||
use crate::card::property::{
|
||||
self, ArrowColor, Effect, EffectCondition, MaybeArrowColor, MaybeEffect, MaybeEffectCondition,
|
||||
};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use dcb_bytes::Bytes;
|
||||
use dcb_util::{
|
||||
@ -146,9 +148,15 @@ impl Bytes for Item {
|
||||
],
|
||||
|
||||
effects: [
|
||||
MaybeEffect::from_bytes(bytes.effect_first).map_err(FromBytesError::EffectFirst)?.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_second).map_err(FromBytesError::EffectSecond)?.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_third).map_err(FromBytesError::EffectThird)?.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_first)
|
||||
.map_err(FromBytesError::EffectFirst)?
|
||||
.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_second)
|
||||
.map_err(FromBytesError::EffectSecond)?
|
||||
.into(),
|
||||
MaybeEffect::from_bytes(bytes.effect_third)
|
||||
.map_err(FromBytesError::EffectThird)?
|
||||
.into(),
|
||||
],
|
||||
|
||||
effect_arrow_color: MaybeArrowColor::from_bytes(bytes.effect_arrow_color)
|
||||
@ -156,10 +164,22 @@ impl Bytes for Item {
|
||||
.into(),
|
||||
|
||||
effect_description: [
|
||||
bytes.effect_description_0.read_string().map_err(FromBytesError::EffectDescription1)?,
|
||||
bytes.effect_description_1.read_string().map_err(FromBytesError::EffectDescription2)?,
|
||||
bytes.effect_description_2.read_string().map_err(FromBytesError::EffectDescription3)?,
|
||||
bytes.effect_description_3.read_string().map_err(FromBytesError::EffectDescription4)?,
|
||||
bytes
|
||||
.effect_description_0
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription1)?,
|
||||
bytes
|
||||
.effect_description_1
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription2)?,
|
||||
bytes
|
||||
.effect_description_2
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription3)?,
|
||||
bytes
|
||||
.effect_description_3
|
||||
.read_string()
|
||||
.map_err(FromBytesError::EffectDescription4)?,
|
||||
],
|
||||
|
||||
// Unknown
|
||||
|
||||
@ -253,7 +253,8 @@ impl Bytes for Effect {
|
||||
|
||||
// Attack type
|
||||
// Lower byte of `y`
|
||||
let get_attack_type = || AttackType::from_bytes(&y.to_le_bytes()[0]).map_err(FromBytesError::UseAttackAttackType);
|
||||
let get_attack_type =
|
||||
|| AttackType::from_bytes(&y.to_le_bytes()[0]).map_err(FromBytesError::UseAttackAttackType);
|
||||
|
||||
// The operation argument
|
||||
let get_op = || EffectOperation::from_bytes(bytes.op).map_err(FromBytesError::Operation);
|
||||
|
||||
@ -118,7 +118,9 @@ impl Bytes for EffectCondition {
|
||||
self.property_cmp.to_bytes(bytes.property_cmp).into_ok();
|
||||
|
||||
// Arguments
|
||||
MaybeDigimonProperty::ref_cast(&self.arg_property).to_bytes(bytes.arg_property).into_ok();
|
||||
MaybeDigimonProperty::ref_cast(&self.arg_property)
|
||||
.to_bytes(bytes.arg_property)
|
||||
.into_ok();
|
||||
LittleEndian::write_u16(bytes.arg_num, self.arg_num);
|
||||
self.operation.to_bytes(bytes.operation).into_ok();
|
||||
|
||||
|
||||
@ -42,7 +42,10 @@ fn valid_bytes() {
|
||||
|
||||
for (mov, bytes) in valid_moves {
|
||||
// Check that we can create the move from bytes
|
||||
assert_eq!(&Move::from_bytes(bytes).expect("Unable to convert move from bytes"), mov);
|
||||
assert_eq!(
|
||||
&Move::from_bytes(bytes).expect("Unable to convert move from bytes"),
|
||||
mov
|
||||
);
|
||||
|
||||
// Make sure the validation succeeds
|
||||
let validation = mov.validate();
|
||||
|
||||
@ -49,7 +49,9 @@ impl Table {
|
||||
pub fn deserialize<R: io::Read>(reader: &mut R) -> Result<Self, DeserializeError> {
|
||||
// Read header
|
||||
let mut header_bytes = <Header as Bytes>::ByteArray::default();
|
||||
reader.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
reader
|
||||
.read_exact(&mut header_bytes)
|
||||
.map_err(DeserializeError::ReadHeader)?;
|
||||
let header = Header::from_bytes(&header_bytes).map_err(DeserializeError::ParseHeader)?;
|
||||
|
||||
// Then check the number of each card
|
||||
@ -75,7 +77,8 @@ impl Table {
|
||||
.map_err(|err| DeserializeError::ReadCardHeader { id: card_id, err })?;
|
||||
|
||||
// Read the header
|
||||
let card_header = CardHeader::from_bytes(&card_header_bytes).map_err(|err| DeserializeError::ParseCardHeader { id: card_id, err })?;
|
||||
let card_header = CardHeader::from_bytes(&card_header_bytes)
|
||||
.map_err(|err| DeserializeError::ParseCardHeader { id: card_id, err })?;
|
||||
|
||||
log::trace!("Found #{}: {}", card_id, card_header.ty);
|
||||
|
||||
@ -87,33 +90,41 @@ impl Table {
|
||||
match card_header.ty {
|
||||
CardType::Digimon => {
|
||||
let mut digimon_bytes = [0; std::mem::size_of::<<Digimon as Bytes>::ByteArray>()];
|
||||
reader.read_exact(&mut digimon_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let digimon = Digimon::from_bytes(&digimon_bytes).map_err(|err| DeserializeError::ParseDigimonCard { id: card_id, err })?;
|
||||
reader
|
||||
.read_exact(&mut digimon_bytes)
|
||||
.map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let digimon = Digimon::from_bytes(&digimon_bytes)
|
||||
.map_err(|err| DeserializeError::ParseDigimonCard { id: card_id, err })?;
|
||||
digimons.push(digimon);
|
||||
},
|
||||
CardType::Item => {
|
||||
let mut item_bytes = [0; std::mem::size_of::<<Item as Bytes>::ByteArray>()];
|
||||
reader.read_exact(&mut item_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let item = Item::from_bytes(&item_bytes).map_err(|err| DeserializeError::ParseItemCard { id: card_id, err })?;
|
||||
reader
|
||||
.read_exact(&mut item_bytes)
|
||||
.map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let item = Item::from_bytes(&item_bytes)
|
||||
.map_err(|err| DeserializeError::ParseItemCard { id: card_id, err })?;
|
||||
items.push(item);
|
||||
},
|
||||
CardType::Digivolve => {
|
||||
let mut digivolve_bytes = [0; std::mem::size_of::<<Digivolve as Bytes>::ByteArray>()];
|
||||
reader.read_exact(&mut digivolve_bytes).map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let digivolve =
|
||||
Digivolve::from_bytes(&digivolve_bytes).map_err(|err| DeserializeError::ParseDigivolveCard { id: card_id, err })?;
|
||||
reader
|
||||
.read_exact(&mut digivolve_bytes)
|
||||
.map_err(|err| DeserializeError::ReadCard {
|
||||
id: card_id,
|
||||
card_type: card_header.ty,
|
||||
err,
|
||||
})?;
|
||||
let digivolve = Digivolve::from_bytes(&digivolve_bytes)
|
||||
.map_err(|err| DeserializeError::ParseDigivolveCard { id: card_id, err })?;
|
||||
digivolves.push(digivolve);
|
||||
},
|
||||
}
|
||||
@ -124,12 +135,20 @@ impl Table {
|
||||
.read_exact(&mut null_terminator)
|
||||
.map_err(|err| DeserializeError::ReadCardFooter { id: card_id, err })?;
|
||||
if null_terminator[0] != 0 {
|
||||
log::warn!("Card with id {}'s null terminator was {} instead of 0", card_id, null_terminator[0]);
|
||||
log::warn!(
|
||||
"Card with id {}'s null terminator was {} instead of 0",
|
||||
card_id,
|
||||
null_terminator[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the table
|
||||
Ok(Self { digimons, items, digivolves })
|
||||
Ok(Self {
|
||||
digimons,
|
||||
items,
|
||||
digivolves,
|
||||
})
|
||||
}
|
||||
|
||||
/// Serializes this card table to a file
|
||||
|
||||
@ -111,9 +111,15 @@ impl Bytes for Deck {
|
||||
owner: bytes.owner.read_string().map_err(FromBytesError::Owner)?,
|
||||
cards: cards.map(CardId),
|
||||
city: MaybeCity::from_bytes(bytes.city).map_err(FromBytesError::City)?.into(),
|
||||
armor_evo: MaybeArmorEvo::from_bytes(bytes.armor_evo).map_err(FromBytesError::ArmorEvo)?.into(),
|
||||
battle_music: MaybeMusic::from_bytes(bytes.battle_music).map_err(FromBytesError::BattleMusic)?.into(),
|
||||
polygon_music: MaybeMusic::from_bytes(bytes.polygon_music).map_err(FromBytesError::PolygonMusic)?.into(),
|
||||
armor_evo: MaybeArmorEvo::from_bytes(bytes.armor_evo)
|
||||
.map_err(FromBytesError::ArmorEvo)?
|
||||
.into(),
|
||||
battle_music: MaybeMusic::from_bytes(bytes.battle_music)
|
||||
.map_err(FromBytesError::BattleMusic)?
|
||||
.into(),
|
||||
polygon_music: MaybeMusic::from_bytes(bytes.polygon_music)
|
||||
.map_err(FromBytesError::PolygonMusic)?
|
||||
.into(),
|
||||
experience: *bytes.experience,
|
||||
unknown_64: *bytes.unknown_64,
|
||||
unknown_6a: *bytes.unknown_6a,
|
||||
@ -151,11 +157,17 @@ impl Bytes for Deck {
|
||||
MaybeCity::ref_cast(&self.city).to_bytes(bytes.city).into_ok();
|
||||
|
||||
// Armor evo
|
||||
MaybeArmorEvo::ref_cast(&self.armor_evo).to_bytes(bytes.armor_evo).into_ok();
|
||||
MaybeArmorEvo::ref_cast(&self.armor_evo)
|
||||
.to_bytes(bytes.armor_evo)
|
||||
.into_ok();
|
||||
|
||||
// Music
|
||||
MaybeMusic::ref_cast(&self.battle_music).to_bytes(bytes.battle_music).into_ok();
|
||||
MaybeMusic::ref_cast(&self.polygon_music).to_bytes(bytes.polygon_music).into_ok();
|
||||
MaybeMusic::ref_cast(&self.battle_music)
|
||||
.to_bytes(bytes.battle_music)
|
||||
.into_ok();
|
||||
MaybeMusic::ref_cast(&self.polygon_music)
|
||||
.to_bytes(bytes.polygon_music)
|
||||
.into_ok();
|
||||
|
||||
// Unknown
|
||||
*bytes.unknown_64 = self.unknown_64;
|
||||
|
||||
@ -16,7 +16,11 @@ pub enum DeserializeError {
|
||||
ReadHeader(#[source] std::io::Error),
|
||||
|
||||
/// The magic of the table was wrong
|
||||
#[error("Found wrong table header magic (expected {:#}, found {:#x})", Table::HEADER_MAGIC, magic)]
|
||||
#[error(
|
||||
"Found wrong table header magic (expected {:#}, found {:#x})",
|
||||
Table::HEADER_MAGIC,
|
||||
magic
|
||||
)]
|
||||
HeaderMagic {
|
||||
/// Magic we found
|
||||
magic: u32,
|
||||
|
||||
@ -15,12 +15,12 @@ enum_discrim_align_threshold = 100
|
||||
fn_args_layout = "Compressed"
|
||||
merge_derives = false
|
||||
imports_granularity="Crate"
|
||||
max_width = 150
|
||||
max_width = 120
|
||||
use_small_heuristics = "Default"
|
||||
indent_style = "Block"
|
||||
wrap_comments = false
|
||||
format_code_in_doc_comments = false
|
||||
comment_width = 150
|
||||
comment_width = 120
|
||||
normalize_comments = false
|
||||
normalize_doc_attributes = false
|
||||
license_template_path = ""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user