diff --git a/app/src/main/java/school_project/MapParser.java b/app/src/main/java/school_project/MapParser.java index 2f0ac7a..adac1a4 100644 --- a/app/src/main/java/school_project/MapParser.java +++ b/app/src/main/java/school_project/MapParser.java @@ -1,16 +1,28 @@ package school_project; +import school_project.Utils.Bitwise; + import java.io.*; -import java.lang.reflect.Array; import java.util.Arrays; public class MapParser { + + /** + * Parse the file and create a Map with its shape and pieces setup + * @param file file to parse + * @return Map Object parsed with file data + * @see "TODO: Add Specification when done" + */ public static Map ParseMapFile(File file) throws IllegalArgumentException, IllegalAccessException, IOException { - System.out.println(file.getAbsolutePath()); + Map ret; + + // Get the file and check that this file exists and can be read FileInputStream fileStream = new FileInputStream(file); if(!file.isFile()) throw new IllegalArgumentException("The argument should be a file"); if(!file.canRead()) throw new IllegalAccessException("This file can't be read"); + // Read the file an array of byte, then look for the HEADER (SMS) and then the FOOTER (SME) + // Then Put everything that is in between in level_data[] byte[] bytes = fileStream.readAllBytes(); int start_position = 0, end_position = 0; for (int i = 0; i < bytes.length; i++) { @@ -26,32 +38,64 @@ public class MapParser { break; } } - byte[] level_data = Arrays.copyOfRange(bytes, start_position, end_position); - int width_map = level_data[0], height_map = level_data[1]; - byte[] map_data = Arrays.copyOfRange(level_data, 2, 2 + width_map * height_map / 8 + (height_map * width_map % 8 != 0 ? 1 : 0)); - byte piece_count = level_data[3 + width_map * height_map / 8 + (height_map * width_map % 8 != 0 ? 1 : 0)]; - byte[] pieces_datas = Arrays.copyOfRange(level_data, 4 + width_map * height_map / 8 + (height_map * width_map % 8 != 0 ? 1 : 0), level_data.length-1); + + // Get the 2 first byte as the map width and height + // Then get the map data. This map data is every bit of the byte that represent either a true if it is a 1 or false if it is a 0 + int map_width = level_data[0], map_height = level_data[1]; + byte[] map_data = Arrays.copyOfRange(level_data, 2, 2 + map_width * map_height / 8 + (map_height * map_width % 8 != 0 ? 1 : 0)); + boolean[][] map_matrix = BuildMatrixFromBytes(map_width, map_height, map_data); + ret = new Map(map_matrix); + + // Get the amount of pieces + // For each of these pieces, get the size of the piece on 1 octet, 4 byte are for width then 4 byte are for height + // the same method as the map is used to get the piece shape. + byte piece_count = level_data[3 + map_width * map_height / 8 + (map_height * map_width % 8 != 0 ? 1 : 0)]; + byte[] pieces_data = Arrays.copyOfRange(level_data, 4 + map_width * map_height / 8 + (map_height * map_width % 8 != 0 ? 1 : 0), level_data.length-1); for (int piece_index = 0; piece_index < piece_count; piece_index++) { - - - + byte[] _piece_size = Bitwise.ByteToNible(pieces_data[piece_index]); + byte _piece_width = _piece_size[0], _piece_height = _piece_size[1]; + byte[] _piece_data = Arrays.copyOfRange(pieces_data, piece_index + 1, 1 + _piece_width * _piece_height / 8 + (_piece_height * _piece_width % 8 != 0 ? 1 : 0)); + boolean[][] _piece_matrix = BuildMatrixFromBytes(_piece_width, _piece_height, _piece_data); + Piece _piece = new Piece(_piece_matrix); + ret.AddShape(_piece); + piece_index = piece_index + _piece_width * _piece_height / 8 + (_piece_height * _piece_width % 8 != 0 ? 1 : 0); } + + // Close the file and return the generated map fileStream.close(); - return new Map(); //TODO: Send the parsed map + return ret; } -// public static void SaveMapFile(File file){ -// } + /** + * Build a boolean Matrix From a byte array + * Each Byte is composed of 8 bit, each bit is 1 or 0 + * if the bit is 0 then it's a false for this cell + * else it's true for this cell + * @param matrix_width width of the matrix + * @param matrix_height height of the matrix + * @param matrix_data byte array of the data to export + * @return boolean Matrix of the data decompiled + */ + private static boolean[][] BuildMatrixFromBytes(int matrix_width, int matrix_height, byte[] matrix_data){ + boolean[][] ret = new boolean[matrix_height][matrix_width]; + // Transforming the bit from matrix_data's byte into boolean array for better manipulation + boolean[] b_array = new boolean[matrix_height * matrix_width]; + int index = 0; + for(byte b: matrix_data){ + for (int i = 0; i < 8; i++) { // because 8 bit in a byte + b_array[index] = Bitwise.IsBitSetAt(b, i); + index++; + } + } - private static boolean[][] BuildMatrixFromBytes(int map_width, int map_height, byte[] map_data){ - boolean[][] ret = new boolean[map_height][map_width]; - //TODO tonitch: cursor 2 - + // Transforming b_array to a 2D matrix + for (int x = 0; x < matrix_height; x++) { + for (int y = 0; y < matrix_width; y++) { + ret[x][y] = b_array[y + x * 8]; + } + } + return ret; } - public static void main(String[] args) throws IOException, IllegalAccessException { - ParseMapFile(new File("test.smap")); - } - } diff --git a/app/src/main/java/school_project/Utils/Bitwise.java b/app/src/main/java/school_project/Utils/Bitwise.java new file mode 100644 index 0000000..bc774c7 --- /dev/null +++ b/app/src/main/java/school_project/Utils/Bitwise.java @@ -0,0 +1,30 @@ +package school_project.Utils; + +public class Bitwise { + + /** + * Check if the bit at pos is 1 or 0 + * @param b byte to test + * @param pos position in b to check + * @return true if the bit at pos is 1 or false if it is 0 + */ + public static boolean IsBitSetAt(byte b, int pos){ + pos = 7 - pos; + return (b & (1 << pos))!= 0; + } + + /** + * Transform a byte (8 bit) to two Nible (4 bit) with a split in the middle + * Exemple: + * in = 01000101 (=69) + * out = { 00000100, 00000101 } (={4, 5}) + * @param in the byte to split + * @return an arrya of 2 byte ret[0] = left part; ret[1] = right part + */ + public static byte[] ByteToNible(byte in){ + byte[] ret = new byte[2]; + ret[0] = (byte) (in >> 4); + ret[1] = (byte) (in & 15); // apply the mask '00001111' + return ret; + } +}