Skip to content
This repository has been archived by the owner on Jun 26, 2019. It is now read-only.

Error parsing region files? #8

Open
TealNerd opened this issue Jan 1, 2018 · 3 comments
Open

Error parsing region files? #8

TealNerd opened this issue Jan 1, 2018 · 3 comments

Comments

@TealNerd
Copy link

TealNerd commented Jan 1, 2018

Tried using this to parse region files and I'm getting an error right away.

new NBTInputStream(file, false).readTag()

causes an IOException with the message "TAG_End found without a TAG_Compound/TAG_List tag preceding it."

Not really sure what's happening there

@TealNerd
Copy link
Author

TealNerd commented Jan 1, 2018

Okay you literally have a region file reading class im an idiot

@TealNerd TealNerd closed this as completed Jan 1, 2018
@TealNerd
Copy link
Author

TealNerd commented Jan 1, 2018

Okay reopening because the SimpleRegionFileReader readFile method is returning null

@TealNerd TealNerd reopened this Jan 1, 2018
@natemort
Copy link

natemort commented Apr 24, 2018

The region parser doesn't work at all. I'm not really sure what format it's intended to parse. The first thing the SimpleRegionFileReader does is read an int to check the version, but the Region file format doesn't have a version field. It doesn't seem to match the old file format or the schematic file format either.

Here's the version I created, since everything else in the library works fine. I'm using this to do some offline computations on Minecraft worlds, and can verify that it works correctly.

import com.flowpowered.nbt.Tag;
import com.flowpowered.nbt.stream.NBTInputStream;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;

public class RegionReader {
    private static final int SECTOR_BYTES = 4096;

    public static List<Tag<?>> readFile(File f) {
        try (RandomAccessFile raf = new RandomAccessFile(f, "r")) {
            // If there isn't a header and at least one chunk sector, the region is empty
            if (raf.length() < SECTOR_BYTES * 3) {
                return Collections.emptyList();
            }
            // Each chunk can use 1 or more sectors, and the first two sectors
            // are the header, so this is the maximum number of chunks
            int maxChunks = ((int) raf.length() / SECTOR_BYTES) - 2;
            int[] chunkLocation = new int[maxChunks];
            int entries = 0;
            for (int i = 0; i < (SECTOR_BYTES / 4); i++) {
                int offset = raf.readInt();
                if (offset != 0) {
                    // The rest of the offset is the number of sectors that the chunk
                    // occupies.  We don't care about that as each chunk stores its length
                    chunkLocation[entries++] = (offset >> 8) * SECTOR_BYTES;
                }
            }
            List<Tag<?>> list = new ArrayList<>();

            for (int i = 0; i < entries; i++) {
                list.add(readChunk(raf, chunkLocation[i]));
            }

            return list;
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return null;
        }
    }

    private static Tag<?> readChunk(RandomAccessFile raf, int location) throws IOException {
        raf.seek(location);
        int length = raf.readInt();
        byte compressionType = raf.readByte();
        byte[] data = new byte[length-1];
        raf.readFully(data);
        return readTag(decompress(compressionType, data));
    }

    private static InputStream decompress(int type, byte[] data) throws IOException {
        switch (type) {
            case 1: return new GZIPInputStream(new ByteArrayInputStream(data));
            case 2: return new InflaterInputStream(new ByteArrayInputStream(data));
            default: throw new IllegalArgumentException("Unknown type");
        }
    }

    private static Tag<?> readTag(InputStream in) throws IOException {
        return new NBTInputStream(in, false).readTag();
    }
}

EDIT: I've modified the file slightly since I originally uploaded it to account for empty sectors. My revised unit tests pass.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants