Skip to content

Commit

Permalink
fix(unreal): Support the UE5 crash reporter (#449)
Browse files Browse the repository at this point in the history
UE5 introduced a "CR1" marker at the beginning of crash archives. It indicates
that the header at the beginning of the file is valid. Previously, that header
was merely a placeholder and a second header at the end of the file contained
valid information. Unreal4Crash::parse now supports both versions by checking
for "CR1".
  • Loading branch information
jan-auer authored Nov 18, 2021
1 parent 6be9aa5 commit 0e405a8
Showing 1 changed file with 37 additions and 12 deletions.
49 changes: 37 additions & 12 deletions symbolic-unreal/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl TryFromCtx<'_, usize> for Unreal4FileMeta {

fn try_from_ctx(data: &[u8], file_offset: usize) -> Result<(Self, usize), Self::Error> {
let mut offset = 0;

let index = data.gread_with::<i32>(&mut offset, scroll::LE)? as usize;
let file_name = data.gread_with(&mut offset, scroll::LE)?;
let len = data.gread_with::<i32>(&mut offset, scroll::LE)? as usize;
Expand All @@ -107,6 +108,19 @@ impl TryFromCtx<'_, usize> for Unreal4FileMeta {
}
}

fn gread_files(
bytes: &[u8],
count: usize,
offset: &mut usize,
) -> Result<Vec<Unreal4FileMeta>, Unreal4Error> {
let mut files = Vec::with_capacity(count);
for _ in 0..count {
let file_offset = *offset;
files.push(bytes.gread_with(offset, file_offset)?);
}
Ok(files)
}

/// Unreal Engine 4 crash file.
#[derive(Debug)]
pub struct Unreal4Crash {
Expand All @@ -119,21 +133,32 @@ impl Unreal4Crash {
fn from_bytes(bytes: Bytes) -> Result<Self, Unreal4Error> {
let mut offset = 0;

// The header is repeated at the beginning and the end of the file. The first one is merely
// a placeholder, the second contains actual information. However, it's not possible to
// parse it right away, so we only read the file count and parse the rest progressively.
let file_count = bytes.pread_with::<i32>(bytes.len() - 4, scroll::LE)? as usize;
let (header, files) = if bytes.starts_with(b"CR1") {
// https://github.com/EpicGames/UnrealEngine/commit/a0471b76577a64e5c4dad89a38dfe7d9611a65ef
// The 'CR1' marker marks a new version of the file format. There is a single correct
// header at the start of the file. Start parsing after the 3 byte marker.
offset = 3;

// Ignore the initial header and use the one at the end of the file instead.
bytes.gread_with::<Unreal4Header>(&mut offset, scroll::LE)?;
let header = bytes.gread_with::<Unreal4Header>(&mut offset, scroll::LE)?;
let files = gread_files(&bytes, header.file_count as usize, &mut offset)?;

let mut files = Vec::with_capacity(file_count);
for _ in 0..file_count {
let file_offset = offset;
files.push(bytes.gread_with(&mut offset, file_offset)?);
}
(header, files)
} else {
// The header is repeated at the beginning and the end of the file. The first one is
// merely a placeholder, the second contains actual information. However, it's not
// possible to parse it right away, so we only read the file count and parse the rest
// progressively.
let file_count = bytes.pread_with::<i32>(bytes.len() - 4, scroll::LE)? as usize;

// Ignore the initial header and use the one at the end of the file instead.
bytes.gread_with::<Unreal4Header>(&mut offset, scroll::LE)?;

let files = gread_files(&bytes, file_count, &mut offset)?;
let header = bytes.gread_with(&mut offset, scroll::LE)?;

(header, files)
};

let header = bytes.gread_with(&mut offset, scroll::LE)?;
if offset != bytes.len() {
return Err(Unreal4ErrorKind::TrailingData.into());
}
Expand Down

0 comments on commit 0e405a8

Please sign in to comment.