package org.mitallast.ds1; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; public class DsReader { // public static final int FLOOR_MAX_LAYER=2; // public static final int SHADOW_MAX_LAYER=1; // public static final int TAG_MAX_LAYER=1; // public static final int WALL_MAX_LAYER=4; public static void main(String... args) throws IOException { DsInfo dsInfo = read(new RandomAccessFile("tiles/ACT1/BARRACKS/barE.ds1", "r")); // for(DsLayerInfo[][] dsLayerInfo: dsInfo.wall_buff){ // for (DsLayerInfo[] layerInfoList: dsLayerInfo){ // for(DsLayerInfo layerInfo: layerInfoList){ // System.out.println(layerInfo); // } // } // break; // } System.out.println(dsInfo.obj_num); for(ObjectInfo objectInfo: dsInfo.obj){ System.out.println(objectInfo); } } public static DsInfo read(RandomAccessFile in) throws IOException { byte [] dir_lookup = new byte[]{ 0x00, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14}; in.seek(0); FileByteBuffer buffer = new FileByteBuffer(in); DsInfo info = new DsInfo(); info.version = buffer.getInt(); info.width = buffer.getInt() + 1; info.height = buffer.getInt() + 1; if (info.version >= 8) { info.act = buffer.getInt() + 1; } else { info.act = 1; } if (info.version >= 10) { info.tag_type = buffer.getInt() + 1; } else { info.tag_type = 1; } info.file_num = 0; if (info.version >= 3) { info.file_num = buffer.getInt(); info.files = new String[info.file_num]; for (int i = 0; i < info.file_num; i++) { StringBuilder fileName = new StringBuilder(); int symbol; do { symbol = buffer.get() & 0xFF; fileName.append(charSequence.charAt(symbol)); } while (symbol != 0); info.files[i] = fileName.toString(); } } // skip 2 bytes ? if ((info.version >= 9) && (info.version <= 13)){ buffer.get(new byte[2]); } // number of wall, floor and tag layers if (info.version >= 4) { // number of wall (and orientation) layers info.wall_num = buffer.getInt(); // number of floor layers if (info.version >= 16) { info.floor_num = buffer.getInt(); } else info.floor_num = 1; // default # of floor layer } else // in version < 4 { // # of layers hardcoded info.wall_num = 1; info.floor_num = 1; info.tag_num = 1; } int nb_layer; int[] lay_stream = new int[14]; // which order ? if (info.version < 4) { lay_stream[0] = 1; // wall 1 lay_stream[1] = 9; // floor 1 lay_stream[2] = 5; // orientation 1 lay_stream[3] = 12; // tag lay_stream[4] = 11; // shadow nb_layer = 5; } else { nb_layer = 0; for (int i=0; i<info.wall_num; i++) { lay_stream[nb_layer++] = 1 + i; // wall x lay_stream[nb_layer++] = 5 + i; // orientation x } for (int i=0; i<info.floor_num; i++) lay_stream[nb_layer++] = 9 + i; // floor x if (info.shadow_num >0) lay_stream[nb_layer++] = 11; // shadow if (info.tag_num >0) lay_stream[nb_layer++] = 12; // tag } System.out.printf("layers : (2 * %d walls) + %d floors + %d shadow + %d tag\n", info.wall_num, info.floor_num, info.shadow_num, info.tag_num); int MAX_LAYER = ( 2 * info.wall_num) + info.floor_num + info.shadow_num + info.tag_num; // info.layerInfo=new DsLayerInfo[MAX_LAYER][info.width][info.height]; // for(int n=0;n<MAX_LAYER;n++){ // for(int x=0;x<info.width;x++){ // for (int y=0;y<info.height;y++){ // info.layerInfo[n][x][y]=new DsLayerInfo(); // } // } // } info.wall_buff=new DsLayerInfo[info.wall_num][info.width][info.height]; for(int n=0;n<info.wall_num;n++){ for(int x=0;x<info.width;x++){ for (int y=0;y<info.height;y++){ info.wall_buff[n][x][y]=new DsLayerInfo(); } } } info.floor_buff=new DsLayerInfo[info.floor_num][info.width][info.height]; for(int n=0;n<info.floor_num;n++){ for(int x=0;x<info.width;x++){ for (int y=0;y<info.height;y++){ info.floor_buff[n][x][y]=new DsLayerInfo(); } } } info.shadow_buff=new DsLayerInfo[info.shadow_num][info.width][info.height]; for(int n=0;n<info.shadow_num;n++){ for(int x=0;x<info.width;x++){ for (int y=0;y<info.height;y++){ info.shadow_buff[n][x][y]=new DsLayerInfo(); } } } info.tag_buff=new DsLayerInfo[info.tag_num][info.width][info.height]; for(int n=0;n<info.tag_num;n++){ for(int x=0;x<info.width;x++){ for (int y=0;y<info.height;y++){ info.shadow_buff[n][x][y]=new DsLayerInfo(); } } } // set pointers int p; for (int n=0; n < nb_layer; n++) { for (int y=0; y < info.height; y++) { for (int x=0; x < info.width; x++) { switch (lay_stream[n]) { // walls case 1: case 2: case 3: case 4: p = lay_stream[n] - 1; info.wall_buff[p][x][y].prop1 = buffer.get(); info.wall_buff[p][x][y].prop2 = buffer.get(); info.wall_buff[p][x][y].prop3 = buffer.get(); info.wall_buff[p][x][y].prop4 = buffer.get(); break; // orientations case 5: case 6: case 7: case 8: p = lay_stream[n] - 5; if (info.version < 7) info.wall_buff[p][x][y].orientation = dir_lookup[buffer.get()&0xFF]; else{ info.wall_buff[p][x][y].orientation = buffer.get(); } buffer.get(new byte[3]); break; // floors case 9: case 10: p = lay_stream[n] - 9; info.floor_buff[p][x][y].prop1 = buffer.get(); info.floor_buff[p][x][y].prop2 = buffer.get(); info.floor_buff[p][x][y].prop3 = buffer.get(); info.floor_buff[p][x][y].prop4 = buffer.get(); break; // shadow case 11: p = lay_stream[n] - 11; info.shadow_buff[p][x][y].prop1 = buffer.get(); info.shadow_buff[p][x][y].prop2 = buffer.get(); info.shadow_buff[p][x][y].prop3 = buffer.get(); info.shadow_buff[p][x][y].prop4 = buffer.get(); break; // tag case 12: p = lay_stream[n] - 12; info.tag_buff[p][x][y].num = buffer.get(); buffer.get(new byte[3]); break; } } } } info.obj_num = 0; if (info.version >= 2) { info.obj_num = buffer.getInt(); //System.out.println("objects: "+info.obj_num); info.obj=new ObjectInfo[info.obj_num]; for(int i=0;i<info.obj_num;i++){ info.obj[i]=new ObjectInfo(); } int current_valid_obj_idx = 0; int max_subtile_width = info.width * 5; int max_subtile_height = info.height * 5; for (int n=0; n < info.obj_num; n++) { info.obj[current_valid_obj_idx].type = buffer.getInt(); info.obj[current_valid_obj_idx].id = buffer.getInt(); int x = info.obj[current_valid_obj_idx].x = buffer.getInt(); int y = info.obj[current_valid_obj_idx].y = buffer.getInt(); if (info.version > 5) { // flags info.obj[current_valid_obj_idx].ds1_flags = buffer.getInt(); } // integrity check (not done by the game I believe) if ((x >= 0) && (x < max_subtile_width) && (y >= 0) && (y < max_subtile_height)) { // some init for the paths of this object info.obj[current_valid_obj_idx].path_num = 0; info.obj[current_valid_obj_idx].desc_idx = -1; info.obj[current_valid_obj_idx].flags = 0; //info.obj[current_valid_obj_idx].frame_delta = rand()%256; //label = & info.obj[current_valid_obj_idx].label; //label->rx = label->ry = label->w = label->h = label->flags = 0; //System.out.println(info.obj[current_valid_obj_idx]); current_valid_obj_idx++; } } } /* groups for tag layer warning : in fact there can be less groups than expected like in data\global\tiles\act1\outdoors\trees.ds1, where the file stop right after the last tile_x group data, leaving the other datas unknown (tile_y, width, height), and npc paths unknown */ if ( (info.version >= 12)&&((info.tag_type == 1) || (info.tag_type == 2))) { // skip 1 dword ? if (info.version >= 18){ buffer.get(new byte[4]); } int n = info.group_num = buffer.getInt(); System.out.println("Groups: "+n); // malloc info.group = new GroupInfo[info.group_num]; for (int i=0;i<info.group_num;i++){ info.group[i] = new GroupInfo(); } // fill it for (int x=0; x<n; x++) { info.group[x].tile_x = buffer.getInt(); info.group[x].tile_y = buffer.getInt(); info.group[x].width = buffer.getInt(); info.group[x].height = buffer.getInt(); if (info.version >= 13) { info.group[x].unk = buffer.getInt(); } } } boolean obj_path_warn_wrote=false; // now we're on the npc's paths datas if (info.version >= 14) { int npc = buffer.getInt(); //System.out.println("Npc: "+npc); for (int n=0; n<npc; n++) { int path = buffer.getInt(); int x = buffer.getInt(); int y = buffer.getInt(); // search of which object are these paths datas int o, last_o, nb; o = last_o = nb = 0; boolean done = false; while (! done) { if (o < info.obj_num) { if ((info.obj[o].x == x) && (info.obj[o].y == y)) { last_o = o; nb++; if (nb >= 2) done = true; } o++; // next object } else done = true; } if (nb >= 2) { // there are a least 2 objects at the same coordinates // put a warning if (!obj_path_warn_wrote) { obj_path_warn_wrote = true; System.out.println("WARNING, there are at least 2 objects at the same coordinates for some paths datas"); } System.out.printf("Removing %d paths points of 1 object at coordinates (%d, %d)\n", path, x, y); // first, delete already assigned paths for (o=0; o < info.obj_num; o++) { if ((info.obj[o].x == x) && (info.obj[o].y == y) && (info.obj[o].path_num != 0)) { for (p=0; p < info.obj[o].path_num; p++) { info.obj[o].path[p].x = 0; info.obj[o].path[p].y = 0; info.obj[o].path[p].action = 0; info.obj[o].path[p].flags = 0; } info.obj[o].path_num = 0; } } // now, skip these paths if (info.version >= 15) { for (p=0; p < path; p++) buffer.get(new byte[3]); } else { for (p=0; p < path; p++) buffer.get(new byte[2]); } } else { // only 1 object at these coordinates for paths, it's ok o = last_o; // does these paths are pointing to a valid object position ? if (o >= info.obj_num) { // nope // the game don't alert the user, so why me ? // but we'll skip them if (info.version >= 15) { for (p=0; p < path; p++) buffer.get(new byte[3]); } else { for (p=0; p < path; p++) buffer.get(new byte[2]); } } else { // yep, valid object // all ok for assigning the paths to this object info.obj[o].path_num = path; for (p=0; p < path; p++) { info.obj[o].path[p].x = buffer.getInt(); info.obj[o].path[p].y = buffer.getInt(); if (info.version >= 15) { info.obj[o].path[p].action = buffer.getInt(); } else info.obj[o].path[p].action = 1; // default action } } } } } return info; } private static class FileByteBuffer{ private final RandomAccessFile file; private final ByteBuffer buffer; public FileByteBuffer(RandomAccessFile in){ file = in; buffer = ByteBuffer.allocate(4); buffer.order(ByteOrder.LITTLE_ENDIAN); } public void get(byte[] bytes) throws IOException { file.read(bytes); } public byte get() throws IOException { return file.readByte(); } public int getInt() throws IOException { buffer.clear(); file.read(buffer.array()); return buffer.getInt(); } } private static final CharSequence charSequence = new CharSequence() { private final char[] charSequence = new char[256]; { try { RandomAccessFile charTable = new RandomAccessFile("chartable.txt", "rw"); for (int i = 0; i < 255; i++) { charSequence[i] = (char) charTable.readByte(); } charTable.close(); } catch (IOException e) { e.printStackTrace(); System.exit(-1); } } @Override public int length() { return charSequence.length; } @Override public char charAt(int i) { return charSequence[i]; } @Override public CharSequence subSequence(int i, int i2) { return null; } }; }