Skip to content

solve issue #1928 (OutOfMemoryError in FBX importer) #1929

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* Copyright (c) 2009-2023 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -39,6 +39,35 @@ public class FbxFile {
public List<FbxElement> rootElements = new ArrayList<>();
public long version;

/**
* Between file versions 7400 and 7500, the "endOffset", "propCount", and
* "propsLength" fields in an FBX element were extended, from 4 bytes to 8
* bytes each.
*
* @return true for 8-byte offsets, otherwise false
*/
public boolean hasExtendedOffsets() {
if (version >= 7500L) {
return true;
} else {
return false;
}
}

/**
* Between file versions 7400 and 7500, the FBX block sentinel was reduced,
* from 13 bytes to 9 bytes.
*
* @return the number of bytes in the block sentinel (&ge;0)
*/
public int numSentinelBytes() {
if (version >= 7500L) {
return 9;
} else {
return 13;
}
}

@Override
public String toString() {
return "FBXFile[version=" + version + ",numElements=" + rootElements.size() + "]";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* Copyright (c) 2009-2023 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -42,8 +42,6 @@

public class FbxReader {

public static final int BLOCK_SENTINEL_LENGTH = 13;
public static final byte[] BLOCK_SENTINEL_DATA = new byte[BLOCK_SENTINEL_LENGTH];
/**
* magic string at start:
* "Kaydara FBX Binary\x20\x20\x00\x1a\x00"
Expand Down Expand Up @@ -74,21 +72,45 @@ public static FbxFile readFBX(InputStream stream) throws IOException {
fbxFile.version = getUInt(byteBuffer);
// Read root elements
while(true) {
FbxElement e = readFBXElement(byteBuffer);
FbxElement e = readFBXElement(byteBuffer, fbxFile);
if(e == null)
break;
fbxFile.rootElements.add(e);
}
return fbxFile;
}

private static FbxElement readFBXElement(ByteBuffer byteBuffer) throws IOException {
private static FbxElement readFBXElement(ByteBuffer byteBuffer, FbxFile file)
throws IOException {
long endOffset = getUInt(byteBuffer);
if (file.hasExtendedOffsets()) {
long upper = getUInt(byteBuffer);
if (upper != 0L) {
throw new IOException(
"Non-zero upper bytes: 0x" + Long.toHexString(upper));
}
}
if(endOffset == 0)
return null;

long propCount = getUInt(byteBuffer);
if (file.hasExtendedOffsets()) {
long upper = getUInt(byteBuffer);
if (upper != 0L) {
throw new IOException(
"Non-zero upper bytes: 0x" + Long.toHexString(upper));
}
}

getUInt(byteBuffer); // Properties length unused

if (file.hasExtendedOffsets()) {
long upper = getUInt(byteBuffer);
if (upper != 0L) {
throw new IOException(
"Non-zero upper bytes: 0x" + Long.toHexString(upper));
}
}

FbxElement element = new FbxElement((int) propCount);
element.id = new String(getBytes(byteBuffer, getUByte(byteBuffer)));

Expand All @@ -98,17 +120,39 @@ private static FbxElement readFBXElement(ByteBuffer byteBuffer) throws IOExcepti
element.propertiesTypes[i] = dataType;
}
if(byteBuffer.position() < endOffset) {
while(byteBuffer.position() < (endOffset - BLOCK_SENTINEL_LENGTH))
element.children.add(readFBXElement(byteBuffer));

if(!Arrays.equals(BLOCK_SENTINEL_DATA, getBytes(byteBuffer, BLOCK_SENTINEL_LENGTH)))
throw new IOException("Failed to read block sentinel, expected 13 zero bytes");
int blockSentinelLength = file.numSentinelBytes();
while (byteBuffer.position() < (endOffset - blockSentinelLength)) {
FbxElement child = readFBXElement(byteBuffer, file);
if (child != null) {
element.children.add(child);
}
}

if (!allZero(getBytes(byteBuffer, blockSentinelLength))) {
throw new IOException("Block sentinel is corrupt: expected all zeros.");
}
}
if(byteBuffer.position() != endOffset)
throw new IOException("Data length not equal to expected");
return element;
}

/**
* Tests whether all bytes in the specified array are zero.
*
* @param array the array to test (not null, unaffected)
* @return true if all zeroes, otherwise false
*/
private static boolean allZero(byte[] array) {
for (byte b : array) {
if (b != 0) {
return false;
}
}

return true;
}

private static Object readData(ByteBuffer byteBuffer, char dataType) throws IOException {
switch(dataType) {
case 'Y':
Expand Down