Changed way asset builder works.

This commit is contained in:
Ulysse Cura 2026-08-21 22:57:55 +02:00
parent 53e5e82976
commit 419a247584
16 changed files with 544 additions and 692 deletions

@ -1 +1 @@
Subproject commit 8d021e517a6ccbdce9021deffe1f7179f27057ca
Subproject commit 2a0803d4c4e15048fced7d3c402b76fc782e0d85

View File

@ -11,20 +11,36 @@ ASSETS := \
ASSETS := $(addprefix $(ASSETS_DIR)/,$(ASSETS))
ASSETS_OBJECTS = $(ASSETS:%=$(BUILD_DIR)/%.o)
ASSET_BUILDER_DIR = $(ASSETS_DIR)/asset_builder/src
ASSET_BUILDER_OUTPUT = $(BUILD_DIR)/$(ASSETS_DIR)/asset_builder/asset_builder
build_assets: setup_asset build_asset_builder asset_end
include $(ASSET_BUILDER_DIR)/Makefile
build_assets: setup_asset build_asset_builder count_assets_build asset_end
# Current file nb to process
CURRENT_ASSET_FILE := 0
# Count nb of files to build
count_assets_build:
$(eval export TOTAL_ASSETS_FILES := $(shell echo $$(($$(make -n $(ASSETS_OBJECTS) 2>/dev/null | grep -c "Building") + 1))))
setup_asset:
@echo "Building $(notdir $(ASSET_OUTPUT))"
$(Q)mkdir -p $(dir $(ASSETS_OBJECTS))
$(ASSET_OUTPUT): $(ASSET_BUILDER_OUTPUT) $(ASSETS)
$(ASSET_OUTPUT): $(ASSETS_OBJECTS) $(ASSET_BUILDER_OUTPUT)
@echo -e "[100%] $(YELLOW)Building $(notdir $(ASSET_OUTPUT))$(RESET)"
$(Q)$(ASSET_BUILDER_OUTPUT) $@ $(ASSETS)
$(Q)$(ASSET_BUILDER_OUTPUT) $@ $(ASSETS_OBJECTS)
# Build .o files
$(BUILD_DIR)/$(ASSETS_DIR)/%.o: $(ASSETS_DIR)/% $(ASSET_BUILDER_OUTPUT)
$(eval CURRENT_ASSET_FILE := $(shell echo $$(($(CURRENT_ASSET_FILE)+1))))
$(eval PERCENTAGE := $(shell echo $$(($(CURRENT_ASSET_FILE)*100/$(TOTAL_ASSETS_FILES)))))
@echo -e "[$(PERCENTAGE)%] $(GREEN)Building ASSET object $@$(RESET)"
$(Q)$(ASSET_BUILDER_OUTPUT) $@ -c $<
asset_end: $(ASSET_OUTPUT)
@echo "Built target $(notdir $(ASSET_OUTPUT))"

View File

@ -7,42 +7,35 @@ The supported file formats are :
The different files format are deduced from the extensions only.
The code has some optimisation problems but here is the main idea :
--------
First calculate header wich consists of :
To build objects files, they are analised and written in an output file wich content depend on asset type.
Their structure will be :
| Description | Length |
| :---------------- | :------------------------------------------------ |
| Assets number | unsigned int (32 bits) |
| ... | |
| Asset name | const char [] (variable length, ends with '\00') |
| Asset start index | unsigned int (32 bits) |
| ... | |
| :---------------- | :-----------------------|
| Asset size | unsigned int (32 bits) |
| Asset data | depends on asset type |
This calculation needs to analse the content of inputs files to calculate the start offsets in the file.
Then write the content of each file in the same order as they were analysed for header calculations.
For the moment each input file is openend and analized 2 times.
## Image (.png)
#### Image (.png)
Convert png's RGBA8888 to custom RGB565 with #0001 in RGB565 as the transparent color.
If png's alpha is null then the color of the pixel will be replaced with this.
### Image asset data structure
##### Image asset data structure
| Description | Length |
| :---------------- | :------------------------------------------ |
| Image width | unsigned int (32 bits) |
| Image height | unsigned int (32 bits) |
| Image width | unsigned short (16 bits) |
| Image height | unsigned short (16 bits) |
| Image data | depend on image size, pixel short (16 bits) |
## Maps (.map)
#### Maps (.map)
Convert from json to custom format.
### Maps asset data structure
##### Maps asset data structure
| Description | Length |
| :-------------------------- | :---------------------------------------------------- |
@ -58,7 +51,7 @@ Convert from json to custom format.
| Entity ID | unsigned int (32 bits) |
| Entity components number | unsigned short (16 bits) |
| ... | |
| Component type | unsigned int (32 bits) |
| Component type | unsigned short (16 bits) |
| ... | |
| Component attributes | depend on attribute type |
| int | unsigned int (32 bits) |
@ -67,3 +60,19 @@ Convert from json to custom format.
| str | const char [] (variable length, ends with '\00') |
| ... | |
| ... | |
--------
To build asset file :
First calculate header wich consists of :
| Description | Length |
| :---------------- | :------------------------------------------------ |
| Assets number | unsigned int (32 bits) |
| ... | |
| Asset name | const char [] (variable length, ends with '\00') |
| Asset start index | unsigned int (32 bits) |
| ... | |
And then write content of each input files.

View File

@ -1,8 +1,9 @@
# Source files
ASSET_BUILDER_SOURCES := \
asset_file.c \
object.c \
image.c \
map.c \
utils.c \
main.c
# Compiler and flags

View File

@ -1,92 +0,0 @@
#include "asset_file.h"
#include <string.h>
#include <stdlib.h>
#include "image.h"
#include "map.h"
#include "errors.h"
int parse_file_for_header(const char *input_file_name, asset_header_def_t *asset_header_def)
{
const char *file_name_dot = strrchr(input_file_name, '.');
if(!file_name_dot)
{
error_printf("File does not have an extension.");
return EXIT_FAILURE;
}
const char *file_name_ext = file_name_dot + 1;
if(!strcmp(file_name_ext, "png"))
{
if(parse_png_for_header(input_file_name, asset_header_def)) return_failure_int;
}
else if(!strcmp(file_name_ext, "map"))
{
if(parse_map_for_header(input_file_name, asset_header_def)) return_failure_int;
}
else
{
error_printf("Extension unsupported : %s", file_name_dot);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int write_header_to_file(asset_file_header_t *asset_file_header, FILE *output_file)
{
if(fwrite(&asset_file_header->assets_number, sizeof(asset_file_header->assets_number), 1, output_file) != 1)
{
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
}
for(uint32_t i = 0; i < asset_file_header->assets_number; i++)
{
size_t name_length = strlen(asset_file_header->assets_header_def[i].name) + 1;
if(fwrite(asset_file_header->assets_header_def[i].name, sizeof(unsigned char), name_length, output_file) != name_length)
{
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
}
if(fwrite(&asset_file_header->assets_header_def[i].start, sizeof(asset_file_header->assets_header_def[i].start), 1, output_file) != 1)
{
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int parse_file_for_content_and_write(const char *input_file_name, FILE *output_file)
{
const char *file_name_dot = strrchr(input_file_name, '.');
if(!file_name_dot)
{
error_printf("File does not have an extension.");
return EXIT_FAILURE;
}
const char *file_name_ext = file_name_dot + 1;
if(!strcmp(file_name_ext, "png"))
{
if(parse_png_for_content_and_write(input_file_name, output_file)) return_failure_int;
}
else if(!strcmp(file_name_ext, "map"))
{
if(parse_map_for_content_and_write(input_file_name, output_file)) return_failure_int;
}
else
{
error_printf("Extension unsupported : %s", file_name_dot);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -1,25 +0,0 @@
#ifndef ASSET_FILE_H
#define ASSET_FILE_H
#include <stdint.h>
#include <stdio.h>
typedef struct asset_header_def_t {
char *name;
uint32_t start, end;
} asset_header_def_t;
typedef struct asset_file_header_t {
uint32_t assets_number;
asset_header_def_t *assets_header_def;
size_t header_size;
} asset_file_header_t;
int parse_file_for_header(const char *input_file_name, asset_header_def_t *asset_header_def);
int write_header_to_file(asset_file_header_t *asset_file_header, FILE *output_file);
int parse_file_for_content_and_write(const char *input_file_name, FILE *output_file);
#endif // ASSET_FILE_H

View File

@ -15,8 +15,23 @@
fprintf(stderr, "\033[32mINFO\033[m: " __VA_ARGS__); \
fputc('\n', stderr); \
} while (0)
#define return_failure_int \
do { \
fprintf(stderr, "\tin %s:%d:%s()\n", __FILE__, __LINE__, __func__); \
return EXIT_FAILURE; \
} while (0);
#define return_failure_ptr \
do { \
fprintf(stderr, "\tin %s:%d:%s()\n", __FILE__, __LINE__, __func__); \
return NULL; \
} while (0);
#else
#define debug_printf(...)
#define return_failure_int return EXIT_FAILURE;
#define return_failure_ptr return NULL;
#endif
#define warning_printf(...) \
@ -32,16 +47,4 @@
fputc('\n', stderr); \
} while (0)
#define return_failure_int \
do { \
fprintf(stderr, " in %s:%d:%s()\n", __FILE__, __LINE__, __func__); \
return EXIT_FAILURE; \
} while (0);
#define return_failure_ptr \
do { \
fprintf(stderr, " in %s:%d:%s()\n", __FILE__, __LINE__, __func__); \
return NULL; \
} while (0);
#endif // ERRORS_H

View File

@ -1,9 +1,9 @@
#ifndef IMAGE_H
#define IMAGE_H
#include "asset_file.h"
#include <stdio.h>
#include <stdint.h>
int parse_png_for_header(const char *input_file_name, asset_header_def_t *asset_header_def);
int parse_png_for_content_and_write(const char *input_file_name, FILE *output_file);
int build_image(const char *input_file_name, FILE *output_file, uint32_t *asset_size);
#endif // IMAGE_H

View File

@ -1,9 +1,9 @@
#ifndef MAP_H
#define MAP_H
#include "asset_file.h"
#include <stdio.h>
#include <stdint.h>
int parse_map_for_header(const char *input_file_name, asset_header_def_t *asset_header_def);
int parse_map_for_content_and_write(const char *input_file_name, FILE *output_file);
int build_map(const char *input_file_name, FILE *output_file, uint32_t *asset_size);
#endif // MAP_H

View File

@ -0,0 +1,8 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <stdio.h>
int build_object(const char *input_file_path, FILE *output_file);
#endif // OBJECT_H

View File

@ -0,0 +1,10 @@
#ifndef UTILS_H
#define UTILS_H
#include <stdio.h>
int file_read(void *ptr, size_t size, size_t nb, FILE *file);
int file_write(const void *ptr, size_t size, size_t nb, FILE *file);
#endif // UTILS_H

View File

@ -2,96 +2,10 @@
#include <png.h>
#include <stdlib.h>
#include "asset_file.h"
#include "utils.h"
#include "errors.h"
int parse_png_for_header(const char *file_name, asset_header_def_t *asset_header_def)
{
FILE *input_file = fopen(file_name, "rb");
if(!input_file)
{
error_printf("Failed to open input file.");
return EXIT_FAILURE;
}
// Check png autenticity.
unsigned char sig[8];
if(fread(sig, sizeof(unsigned char), 8, input_file) != 8)
{
fclose(input_file);
error_printf("Failed to read input file.");
return EXIT_FAILURE;
}
if(!png_check_sig(sig, 8))
{
fclose(input_file);
error_printf("Png file is not valid.");
return EXIT_FAILURE;
}
// PNG structs.
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!png_ptr)
{
fclose(input_file);
error_printf("Failed to allocate png ptr.");
return EXIT_FAILURE;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if(!info_ptr)
{
png_destroy_read_struct(&png_ptr, NULL, NULL);
fclose(input_file);
error_printf("Failed to allocate png info.");
return EXIT_FAILURE;
}
// Fill structs and load data.
png_init_io(png_ptr, input_file);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
uint32_t width = png_get_image_width(png_ptr, info_ptr);
uint32_t height = png_get_image_height(png_ptr, info_ptr);
uint8_t bit_depth = png_get_bit_depth(png_ptr, info_ptr);
uint8_t color_type = png_get_color_type(png_ptr, info_ptr);
// Verify png attributes.
if(bit_depth != 8)
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(input_file);
error_printf("Png bit depth not supported.");
return EXIT_FAILURE;
}
switch(color_type)
{
case PNG_COLOR_TYPE_RGB:
case PNG_COLOR_TYPE_RGB_ALPHA:
break;
default:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(input_file);
error_printf("Png color type not supported.");
return EXIT_FAILURE;
}
// Fill asset def.
asset_header_def->start = 0;
asset_header_def->end = sizeof(width) + sizeof(height) + height * width * sizeof(uint16_t);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(input_file);
return EXIT_SUCCESS;
}
int parse_png_for_content_and_write(const char *input_file_name, FILE *output_file)
int build_image(const char *input_file_name, FILE *output_file, uint32_t *asset_size)
{
FILE *input_file = fopen(input_file_name, "rb");
@ -102,18 +16,13 @@ int parse_png_for_content_and_write(const char *input_file_name, FILE *output_fi
}
// Check png autenticity.
unsigned char sig[8];
if(fread(sig, sizeof(unsigned char), 8, input_file) != 8)
{
fclose(input_file);
error_printf("Failed to read input file.");
return EXIT_FAILURE;
}
png_byte sig[8];
if(file_read(sig, sizeof(png_byte), sizeof(sig), input_file)) return_failure_int;
if(!png_check_sig(sig, 8))
{
fclose(input_file);
error_printf("Png file is not valid.");
if(fclose(input_file))
error_printf("Failed to close input file : %d", errno);
return EXIT_FAILURE;
}
@ -121,17 +30,19 @@ int parse_png_for_content_and_write(const char *input_file_name, FILE *output_fi
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!png_ptr)
{
fclose(input_file);
error_printf("Failed to allocate png ptr.");
if(fclose(input_file))
error_printf("Failed to close input file : %d", errno);
return EXIT_FAILURE;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if(!info_ptr)
{
png_destroy_read_struct(&png_ptr, NULL, NULL);
fclose(input_file);
error_printf("Failed to allocate png info.");
png_destroy_read_struct(&png_ptr, NULL, NULL);
if(fclose(input_file))
error_printf("Failed to close input file : %d", errno);
return EXIT_FAILURE;
}
@ -140,27 +51,31 @@ int parse_png_for_content_and_write(const char *input_file_name, FILE *output_fi
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
uint32_t width = png_get_image_width(png_ptr, info_ptr);
uint32_t height = png_get_image_height(png_ptr, info_ptr);
uint16_t width = png_get_image_width(png_ptr, info_ptr);
uint16_t height = png_get_image_height(png_ptr, info_ptr);
size_t row_length = png_get_rowbytes(png_ptr, info_ptr);
// Write image size.
if(fwrite(&width, sizeof(width), 1, output_file) != 1)
if(file_write(&width, sizeof(width), 1, output_file))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(input_file);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
if(fclose(input_file))
error_printf("Failed to close input file : %d", errno);
return_failure_int;
}
if(fwrite(&height, sizeof(height), 1, output_file) != 1)
*asset_size += sizeof(width);
if(file_write(&height, sizeof(height), 1, output_file))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(input_file);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
if(fclose(input_file))
error_printf("Failed to close input file : %d", errno);
return_failure_int;
}
*asset_size += sizeof(height);
// Prepare storage.
png_byte src_pixels[height * row_length];
png_bytep src_row_ptrs[height];
@ -170,46 +85,43 @@ int parse_png_for_content_and_write(const char *input_file_name, FILE *output_fi
// Read pixels.
png_read_image(png_ptr, src_row_ptrs);
uint16_t dst_pixels[width * height * sizeof(uint16_t)];
uint16_t dst_pixels[width * height];
for(uint32_t i = 0; i < height; i++)
for(uint16_t i = 0; i < height; i++)
{
for(uint32_t j = 0; j < width; j += 1)
for(uint16_t j = 0; j < width; j += 1)
{
uint8_t r, g, b;
if(src_pixels[i * row_length + j * 4 + 3])
{
r = src_pixels[i * row_length + j * 4] >> 3;
g = src_pixels[i * row_length + j * 4 + 1] >> 2;
b = src_pixels[i * row_length + j * 4 + 2] >> 3;
uint8_t r = src_pixels[i * row_length + j * 4] >> 3;
uint8_t g = src_pixels[i * row_length + j * 4 + 1] >> 2;
uint8_t b = src_pixels[i * row_length + j * 4 + 2] >> 3;
dst_pixels[i * width + j] = ((r) << 11) | ((g) << 5) | b;
}
else
{
r = 0x00;
g = 0x00;
b = 0x01;
dst_pixels[i * width + j] = 0x0001;
}
}
}
uint16_t pixel = ((r) << 11) | ((g) << 5) | b;
dst_pixels[i * width + j] = pixel;
// printf("\033[48;2;%d;%d;%dm ", r << 3, g << 2, b << 3);
}
// puts("\033[m");
}
if(fwrite(&dst_pixels, sizeof(uint16_t), width * height, output_file) != width * height)
if(file_write(&dst_pixels, sizeof(*dst_pixels), width * height, output_file))
{
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(input_file);
error_printf("Failed to write to output file : %d", errno);
if(fclose(input_file))
error_printf("Failed to close input file : %d", errno);
return_failure_int;
}
*asset_size += sizeof(dst_pixels);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if(fclose(input_file))
{
error_printf("Failed to close input file : %d", errno);
return EXIT_FAILURE;
}
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fclose(input_file);
return EXIT_SUCCESS;
}

View File

@ -1,101 +1,212 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset_file.h"
#include <stdint.h>
#include "object.h"
#include "utils.h"
#include "errors.h"
typedef enum build_function_t {
BUILD_OBJECT,
BUILD_ASSET_FILE
} build_function_t;
void print_usage(void)
{
puts("\033[33musage\033[m:");
puts("\tasset_builder output_object_file -c input_file");
puts("\tasset_builder output_asset_file input_object_file [input_object_file2 ...]");
}
int main(int argc, char *argv[])
{
int exit_status = EXIT_FAILURE;
FILE *output_asset_file = NULL;
// Verify arguments minimum number.
// Parse args
if(argc < 3)
{
puts("\033[33musage\033[m: asset_builder output_file input_file [input_file_2 ...]");
goto end;
print_usage();
return EXIT_FAILURE;
}
// Header size and content.
asset_file_header_t asset_file_header;
asset_file_header.assets_number = argc - 2;
asset_file_header.header_size = sizeof(asset_file_header.assets_number) + sizeof(uint32_t) * asset_file_header.assets_number;
asset_file_header.assets_header_def = calloc(asset_file_header.assets_number, sizeof(asset_header_def_t));
if(!asset_file_header.assets_header_def)
const build_function_t build_function = strcmp(argv[2], "-c") ? BUILD_ASSET_FILE : BUILD_OBJECT;
const char *output_file_path = argv[1];
FILE *output_file = fopen(output_file_path, "wb");
if(!output_file)
{
error_printf("Failed to allocate memory.");
goto end;
error_printf("Failed to open output file : %d", errno);
return EXIT_FAILURE;
}
// Set assets name.
for(uint32_t i = 0; i < asset_file_header.assets_number; i++)
if(build_function == BUILD_OBJECT)
{
const char *file_name = argv[i + 2];
const char *file_name_last_slash = strrchr(file_name, '/');
const char *file_base_name = file_name_last_slash ? file_name_last_slash + 1 : file_name;
const char *file_name_dot = strrchr(file_name, '.');
const char *file_name_ext = file_name_dot ? file_name_dot + 1 : file_name;
size_t asset_name_length = strlen(file_base_name) - strlen(file_name_ext);
asset_file_header.assets_header_def[i].name = malloc(asset_name_length);
asset_file_header.assets_header_def[i].name[asset_name_length - 1] = '\0';
strncpy(asset_file_header.assets_header_def[i].name, file_base_name, asset_name_length - 1);
// Update header size.
asset_file_header.header_size += asset_name_length;
}
// Parse input files to create header structure and fill output file.
output_asset_file = fopen(argv[1], "wb");
if(!output_asset_file)
if(argc != 4)
{
error_printf("could not create output file.");
goto end;
print_usage();
return EXIT_FAILURE;
}
for(uint32_t i = 0; i < asset_file_header.assets_number; i++)
const char *input_file_path = argv[3];
if(build_object(input_file_path, output_file))
{
if(parse_file_for_header(argv[i + 2], &asset_file_header.assets_header_def[i])) return_failure_int;
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
// Calculate absolute position of assets in the file.
if(i) asset_file_header.assets_header_def[i].start = asset_file_header.assets_header_def[i - 1].end;
else asset_file_header.assets_header_def[0].start = asset_file_header.header_size;
asset_file_header.assets_header_def[i].end += asset_file_header.assets_header_def[i].start;
return_failure_int;
}
if(write_header_to_file(&asset_file_header, output_asset_file))
}
else if(build_function == BUILD_ASSET_FILE)
{
error_printf("Failed to write to output file : %d", errno);
goto end;
}
uint32_t header_size = 0;
for(uint32_t i = 0; i < asset_file_header.assets_number; i++)
const int nb_input_object_files = argc - 2;
if(file_write(&nb_input_object_files, sizeof(nb_input_object_files), 1, output_file))
{
if(parse_file_for_content_and_write(argv[i + 2], output_asset_file)) return_failure_int;
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return_failure_int;
}
header_size += sizeof(nb_input_object_files);
exit_status = EXIT_SUCCESS;
FILE *input_object_files[nb_input_object_files];
end:
if(asset_file_header.assets_header_def)
uint32_t input_object_files_sizes[nb_input_object_files];
uint32_t input_object_files_offsets[nb_input_object_files];
for(int i = 0; i < nb_input_object_files; i++)
{
for(uint32_t i = 0; i < asset_file_header.assets_number; i++)
const char *input_object_file_path = argv[i + 2];
input_object_files[i] = fopen(input_object_file_path, "rb");
if(!input_object_files[i])
{
if(asset_file_header.assets_header_def[i].name)
error_printf("Failed to open input object file : %d", errno);
for(; i >= 0; i--)
if(fclose(input_object_files[i]))
error_printf("Failed to close input object file : %d", errno);
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return EXIT_FAILURE;
}
if(file_read(&input_object_files_sizes[i], sizeof(*input_object_files_sizes), 1, input_object_files[i]))
{
free(asset_file_header.assets_header_def[i].name);
for(; i >= 0; i--)
if(fclose(input_object_files[i]))
error_printf("Failed to close input object file : %d", errno);
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return_failure_int
}
const char *input_object_file_slash = strrchr(input_object_file_path, '/');
const char *input_object_file_name = input_object_file_slash ? input_object_file_slash + 1 : input_object_file_path;
const char *input_object_file_ext = strchr(input_object_file_path, '.');
uint32_t input_object_file_base_name_length = strlen(input_object_file_name) - strlen(input_object_file_ext) + 1;
header_size += input_object_file_base_name_length * sizeof(*input_object_file_name);
header_size += sizeof(*input_object_files_offsets);
}
input_object_files_offsets[0] = header_size;
for(int i = 0; i < nb_input_object_files; i++)
{
if(i) input_object_files_offsets[i] = input_object_files_offsets[i - 1] + input_object_files_sizes[i - 1];
char *input_object_file_path = argv[i + 2];
const char *input_object_file_slash = strrchr(input_object_file_path, '/');
const char *input_object_file_name = input_object_file_slash ? input_object_file_slash + 1 : input_object_file_path;
char *input_object_file_ext = strchr(input_object_file_path, '.');
input_object_file_ext[0] = '\00';
uint32_t input_object_file_base_name_length = strlen(input_object_file_name) - strlen(input_object_file_ext) + 1;
if(file_write(input_object_file_name, sizeof(*input_object_file_name), input_object_file_base_name_length, output_file))
{
for(i = 0; i < nb_input_object_files; i++)
if(fclose(input_object_files[i]))
error_printf("Failed to close input object file : %d", errno);
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return_failure_int;
}
if(file_write(&input_object_files_offsets[i], sizeof(*input_object_files_offsets), 1, output_file))
{
for(i = 0; i < nb_input_object_files; i++)
if(fclose(input_object_files[i]))
error_printf("Failed to close input object file : %d", errno);
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return_failure_int;
}
}
free(asset_file_header.assets_header_def);
for(int i = 0; i < nb_input_object_files; i++)
{
char buffer[input_object_files_sizes[i]];
if(file_read(buffer, sizeof(*buffer), input_object_files_sizes[i], input_object_files[i]))
{
for(i = 0; i < nb_input_object_files; i++)
if(fclose(input_object_files[i]))
error_printf("Failed to close input object file : %d", errno);
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return_failure_int;
}
if(output_asset_file)
fclose(output_asset_file);
if(file_write(buffer, sizeof(*buffer), input_object_files_sizes[i], output_file))
{
for(i = 0; i < nb_input_object_files; i++)
if(fclose(input_object_files[i]))
error_printf("Failed to close input object file : %d", errno);
return exit_status;
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return_failure_int;
}
}
for(int i = 0; i < nb_input_object_files; i++)
{
if(fclose(input_object_files[i]))
{
error_printf("Failed to close input object file : %d", errno);
for(i++; i < nb_input_object_files; i++)
if(fclose(input_object_files[i]))
error_printf("Failed to close input object file : %d", errno);
if(fclose(output_file))
error_printf("Failed to close output file : %d", errno);
return EXIT_FAILURE;
}
}
}
if(fclose(output_file))
{
error_printf("Failed to close output file : %d", errno);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -4,171 +4,42 @@
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "asset_file.h"
#include "utils.h"
#include "errors.h"
int parse_map_for_header(const char *input_file_name, asset_header_def_t *asset_header_def)
static int json_get_object_of_type(const json_object *obj, const char *key, json_type type, json_object **result)
{
json_object *root = json_object_from_file(input_file_name);
if (!root)
if(!json_object_object_get_ex(obj, key, result))
{
error_printf("Failed to load input file.");
error_printf("Object doesn't contain key \"%s\".", key);
return EXIT_FAILURE;
}
if(json_object_object_length(root) != 8)
if(!json_object_is_type(*result, type))
{
json_object_put(root);
error_printf("Map file doesn't contain 8 elements at its root.");
error_printf("Key \"%s\" is not of type %s.", key, json_type_to_name(type));
return EXIT_FAILURE;
}
json_object *tmp_obj;
return EXIT_SUCCESS;
}
if(!json_object_object_get_ex(root, "MapWidth", &tmp_obj))
static inline int json_get_idx_of_type(const json_object *obj, size_t idx, json_type type, json_object **result)
{
json_object_put(root);
error_printf("Map file doesn't contain key \"MapWidth\".");
*result = json_object_array_get_idx(obj, idx);
if(!*result)
{
error_printf("Array index out of range : %lu.", idx);
return EXIT_FAILURE;
}
if(!json_object_is_type(tmp_obj, json_type_int))
if(!json_object_is_type(*result, type))
{
error_printf("Map key \"MapWidth\" is not of type int.");
error_printf("Result is not of type %s.", json_type_to_name(type));
return EXIT_FAILURE;
}
int map_width = json_object_get_int(tmp_obj);
if(!json_object_object_get_ex(root, "MapHeight", &tmp_obj))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"MapHeight\".");
return EXIT_FAILURE;
}
if(!json_object_is_type(tmp_obj, json_type_int))
{
error_printf("Map key \"MapHeight\" is not of type int.");
return EXIT_FAILURE;
}
int map_height = json_object_get_int(tmp_obj);
json_object *entities;
if(!json_object_object_get_ex(root, "Entities", &entities))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"Entities\".");
return EXIT_FAILURE;
}
if(!json_object_is_type(entities, json_type_array))
{
json_object_put(root);
error_printf("Map object \"Entities\" is not an array.");
return EXIT_FAILURE;
}
uint32_t entities_nb = json_object_array_length(entities);
asset_header_def->start = 0;
asset_header_def->end = sizeof(map_width) + sizeof(map_height)
+ map_width * map_height * sizeof(uint16_t) * 4
+ map_width * map_height * sizeof(uint8_t)
+ sizeof(entities_nb);
for(uint32_t i = 0; i < entities_nb; i++)
{
json_object *entity = json_object_array_get_idx(entities, i);
if(!json_object_is_type(entity, json_type_object))
{
error_printf("Map entity is not of type object.");
return EXIT_FAILURE;
}
asset_header_def->end += sizeof(uint32_t);
json_object *components;
if(!json_object_object_get_ex(entity, "Components", &components))
{
error_printf("Entity doesn't have key \"Components\".");
return EXIT_FAILURE;
}
if(!json_object_is_type(components, json_type_array))
{
error_printf("Component is not of type array.");
return EXIT_FAILURE;
}
uint32_t components_nb = json_object_array_length(components);
asset_header_def->end += sizeof(components_nb);
for(uint32_t j = 0; j < components_nb; j++)
{
json_object *component = json_object_array_get_idx(components, j);
if(!json_object_is_type(component, json_type_object))
{
error_printf("Entity component is not of type object.");
return EXIT_FAILURE;
}
asset_header_def->end += sizeof(uint32_t);
json_object *attributes;
if(!json_object_object_get_ex(component, "Attributes", &attributes))
{
error_printf("Component doesn't have key \"Attributes\".");
return EXIT_FAILURE;
}
if(!json_object_is_type(attributes, json_type_array))
{
error_printf("Attributes is not of type array.");
return EXIT_FAILURE;
}
uint32_t attributes_nb = json_object_array_length(attributes);
for(uint32_t k = 0; k < attributes_nb; k++)
{
json_object *attribute = json_object_array_get_idx(attributes, k);
switch(json_object_get_type(attribute))
{
case json_type_int:
asset_header_def->end += sizeof(uint32_t);
break;
case json_type_double:
asset_header_def->end += sizeof(float);
break;
case json_type_boolean:
asset_header_def->end += sizeof(bool);
break;
case json_type_string:
asset_header_def->end += json_object_get_string_len(attribute) + 1;
break;
default:
error_printf("Attribute type is not supported : %s", json_type_to_name(json_object_get_type(attribute)));
return EXIT_FAILURE;
}
}
}
}
json_object_put(root);
return EXIT_SUCCESS;
}
@ -177,7 +48,7 @@ typedef enum component_type_t {
#include "components.def"
} component_type_t;
int parse_map_for_content_and_write(const char *input_file_name, FILE *output_file)
int build_map(const char *input_file_name, FILE *output_file, uint32_t *asset_size)
{
json_object *root = json_object_from_file(input_file_name);
if (!root)
@ -188,283 +59,233 @@ int parse_map_for_content_and_write(const char *input_file_name, FILE *output_fi
json_object *tmp_obj;
if(!json_object_object_get_ex(root, "MapWidth", &tmp_obj))
if(json_get_object_of_type(root, "MapWidth", json_type_int, &tmp_obj))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"MapWidth\".");
return EXIT_FAILURE;
return_failure_int;
}
int map_width = json_object_get_int(tmp_obj);
uint16_t map_width = json_object_get_int(tmp_obj);
if(fwrite(&map_width, sizeof(map_width), 1, output_file) != 1)
if(json_get_object_of_type(root, "MapHeight", json_type_int, &tmp_obj))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
if(!json_object_object_get_ex(root, "MapHeight", &tmp_obj))
uint16_t map_height = json_object_get_int(tmp_obj);
if(file_write(&map_width, sizeof(map_width), 1, output_file))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"MapHeight\".");
return EXIT_FAILURE;
return_failure_int;
}
int map_height = json_object_get_int(tmp_obj);
*asset_size += sizeof(map_width);
if(fwrite(&map_height, sizeof(map_height), 1, output_file) != 1)
if(file_write(&map_height, sizeof(map_height), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
// Background Layer 1
if(!json_object_object_get_ex(root, "BackgroundLayer1", &tmp_obj))
*asset_size += sizeof(map_height);
// BackgroundLayers
json_object *backgroundlayers;
if(json_get_object_of_type(root, "BackgroundLayers", json_type_array, &backgroundlayers))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"BackgroundLayer1\".");
return EXIT_FAILURE;
return_failure_int;
}
if(!json_object_is_type(tmp_obj, json_type_array))
uint8_t layer_count = json_object_array_length(backgroundlayers);
if(file_write(&layer_count, sizeof(layer_count), 1, output_file))
{
json_object_put(root);
error_printf("Map background layer 1 is not of type array.");
return EXIT_FAILURE;
return_failure_int;
}
uint16_t layer_data[map_width * map_height];
*asset_size += sizeof(layer_count);
for(size_t i = 0; i < (size_t)map_width * (size_t)map_height; i++)
uint16_t layer_data[(size_t)map_width * (size_t)map_height];
for(uint8_t i = 0; i < layer_count; i++)
{
json_object *tile = json_object_array_get_idx(tmp_obj, i);
layer_data[i] = json_object_get_int(tile);
}
if(fwrite(&layer_data, sizeof(uint16_t), map_width * map_height, output_file) != (size_t)map_width * (size_t)map_height)
json_object *backgroundlayer;
if(json_get_idx_of_type(backgroundlayers, i, json_type_array, &backgroundlayer))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
// Background Layer 2
if(!json_object_object_get_ex(root, "BackgroundLayer2", &tmp_obj))
for(size_t j = 0; j < (size_t)map_width * (size_t)map_height; j++)
{
if(json_get_idx_of_type(backgroundlayer, j, json_type_int, &tmp_obj))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"BackgroundLayer2\".");
return EXIT_FAILURE;
return_failure_int;
}
layer_data[j] = json_object_get_int(tmp_obj);
}
if(!json_object_is_type(tmp_obj, json_type_array))
if(file_write(&layer_data, sizeof(*layer_data), (size_t)map_width * (size_t)map_height, output_file))
{
json_object_put(root);
error_printf("Map background layer 1 is not of type array.");
return EXIT_FAILURE;
return_failure_int;
}
for(size_t i = 0; i < (size_t)map_width * (size_t)map_height; i++)
{
json_object *tile = json_object_array_get_idx(tmp_obj, i);
layer_data[i] = json_object_get_int(tile);
}
if(fwrite(&layer_data, sizeof(uint16_t), map_width * map_height, output_file) != (size_t)map_width * (size_t)map_height)
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
}
// Background Layer 3
if(!json_object_object_get_ex(root, "BackgroundLayer3", &tmp_obj))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"BackgroundLayer3\".");
return EXIT_FAILURE;
}
if(!json_object_is_type(tmp_obj, json_type_array))
{
json_object_put(root);
error_printf("Map background layer 1 is not of type array.");
return EXIT_FAILURE;
}
for(size_t i = 0; i < (size_t)map_width * (size_t)map_height; i++)
{
json_object *tile = json_object_array_get_idx(tmp_obj, i);
layer_data[i] = json_object_get_int(tile);
}
if(fwrite(&layer_data, sizeof(uint16_t), map_width * map_height, output_file) != (size_t)map_width * (size_t)map_height)
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
*asset_size += sizeof(layer_data);
}
// Foreground
if(!json_object_object_get_ex(root, "Foreground", &tmp_obj))
json_object *foregroundlayer;
if(json_get_object_of_type(root, "ForegroundLayer", json_type_array, &foregroundlayer))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"Foreground\".");
return EXIT_FAILURE;
}
if(!json_object_is_type(tmp_obj, json_type_array))
{
json_object_put(root);
error_printf("Map background layer 1 is not of type array.");
return EXIT_FAILURE;
return_failure_int;
}
for(size_t i = 0; i < (size_t)map_width * (size_t)map_height; i++)
{
json_object *tile = json_object_array_get_idx(tmp_obj, i);
layer_data[i] = json_object_get_int(tile);
}
if(fwrite(&layer_data, sizeof(uint16_t), map_width * map_height, output_file) != (size_t)map_width * (size_t)map_height)
if(json_get_idx_of_type(foregroundlayer, i, json_type_int, &tmp_obj))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
layer_data[i] = json_object_get_int(tmp_obj);
}
if(file_write(&layer_data, sizeof(*layer_data), (size_t)map_width * (size_t)map_height, output_file))
{
json_object_put(root);
return_failure_int;
}
*asset_size += sizeof(layer_data);
// Hitboxes
if(!json_object_object_get_ex(root, "Hitboxes", &tmp_obj))
json_object *hitboxes;
if(json_get_object_of_type(root, "Hitboxes", json_type_array, &hitboxes))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"Hitboxes\".");
return EXIT_FAILURE;
return_failure_int;
}
if(!json_object_is_type(tmp_obj, json_type_array))
{
json_object_put(root);
error_printf("Map background layer 1 is not of type array.");
return EXIT_FAILURE;
}
uint8_t hitboxes_layer[map_width * map_height];
uint8_t hitboxes_layer_data[(size_t)map_width * (size_t)map_height];
for(size_t i = 0; i < (size_t)map_width * (size_t)map_height; i++)
{
json_object *tile = json_object_array_get_idx(tmp_obj, i);
hitboxes_layer[i] = json_object_get_int(tile);
}
if(fwrite(&hitboxes_layer, sizeof(uint8_t), map_width * map_height, output_file) != (size_t)map_width * (size_t)map_height)
if(json_get_idx_of_type(hitboxes, i, json_type_int, &tmp_obj))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
hitboxes_layer_data[i] = json_object_get_int(tmp_obj);
}
if(file_write(&hitboxes_layer_data, sizeof(*hitboxes_layer_data), (size_t)map_width * (size_t)map_height, output_file))
{
json_object_put(root);
return_failure_int;
}
*asset_size += sizeof(hitboxes_layer_data);
// Entities
json_object *entities;
if(!json_object_object_get_ex(root, "Entities", &entities))
if(json_get_object_of_type(root, "Entities", json_type_array, &entities))
{
json_object_put(root);
error_printf("Map file doesn't contain key \"Entities\".");
return EXIT_FAILURE;
return_failure_int;
}
uint32_t entities_nb = json_object_array_length(entities);
uint16_t entities_nb = json_object_array_length(entities);
if(fwrite(&entities_nb, sizeof(entities_nb), 1, output_file) != 1)
if(file_write(&entities_nb, sizeof(entities_nb), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
*asset_size += sizeof(entities_nb);
for(uint32_t i = 0; i < entities_nb; i++)
{
json_object *entity = json_object_array_get_idx(entities, i);
if(!json_object_object_get_ex(entity, "ID", &tmp_obj))
if(json_get_object_of_type(entity, "ID", json_type_int, &tmp_obj))
{
json_object_put(root);
error_printf("Entity doesn't have key \"ID\".");
return EXIT_FAILURE;
}
if(!json_object_is_type(tmp_obj, json_type_int))
{
json_object_put(root);
error_printf("Entity ID is not of type int.");
return EXIT_FAILURE;
return_failure_int;
}
uint32_t entity_id = json_object_get_int(tmp_obj);
if(fwrite(&entity_id, sizeof(entity_id), 1, output_file) != 1)
if(file_write(&entity_id, sizeof(entity_id), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
*asset_size += sizeof(entity_id);
json_object *components;
if(!json_object_object_get_ex(entity, "Components", &components))
if(json_get_object_of_type(entity, "Components", json_type_array, &components))
{
json_object_put(root);
error_printf("Entity doesn't have key \"Components\".");
return EXIT_FAILURE;
return_failure_int;
}
uint32_t components_nb = json_object_array_length(components);
uint16_t components_nb = json_object_array_length(components);
if(fwrite(&components_nb, sizeof(components_nb), 1, output_file) != 1)
if(file_write(&components_nb, sizeof(components_nb), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
*asset_size += sizeof(components_nb);
for(uint32_t j = 0; j < components_nb; j++)
{
json_object *component = json_object_array_get_idx(components, j);
json_object *component;
if(json_get_idx_of_type(components, j, json_type_object, &component))
{
json_object_put(root);
return_failure_int;
}
json_object *attributes;
if(!json_object_object_get_ex(component, "Type", &tmp_obj))
if(json_get_object_of_type(component, "Type", json_type_string, &tmp_obj))
{
json_object_put(root);
error_printf("Component doesn't have key \"Type\".");
return EXIT_FAILURE;
return_failure_int;
}
if(!json_object_is_type(tmp_obj, json_type_string))
{
json_object_put(root);
error_printf("Component type is not of type string.");
return EXIT_FAILURE;
}
uint16_t component_type;
component_type_t component_type;
#define COMPONENT(NAME, ...) \
if(!strcmp(#NAME, json_object_get_string(tmp_obj))) \
component_type = NAME;
#include "components.def"
if(fwrite(&component_type, sizeof(uint32_t), 1, output_file) != 1)
if(file_write(&component_type, sizeof(component_type), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
if(!json_object_object_get_ex(component, "Attributes", &attributes))
*asset_size += sizeof(component_type);
if(json_get_object_of_type(component, "Attributes", json_type_array, &attributes))
{
json_object_put(root);
error_printf("Component doesn't have key \"Attributes\".");
return EXIT_FAILURE;
return_failure_int;
}
uint32_t attributes_nb = json_object_array_length(attributes);
@ -477,53 +298,54 @@ int parse_map_for_content_and_write(const char *input_file_name, FILE *output_fi
{
case json_type_int:
{
int value = json_object_get_int(attribute);
if(fwrite(&value, sizeof(value), 1, output_file) != 1)
uint32_t value = json_object_get_int(attribute);
if(file_write(&value, sizeof(value), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
}
*asset_size += sizeof(value);
break;
}
case json_type_double:
{
float value = json_object_get_double(attribute);
if(fwrite(&value, sizeof(value), 1, output_file) != 1)
if(file_write(&value, sizeof(value), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
*asset_size += sizeof(value);
break;
}
case json_type_boolean:
{
bool value = json_object_get_boolean(attribute);
if(fwrite(&value, sizeof(value), 1, output_file) != 1)
if(file_write(&value, sizeof(value), 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
*asset_size += sizeof(value);
break;
}
case json_type_string:
{
const char *str = json_object_get_string(attribute);
if(fwrite(str, sizeof(char), strlen(str) + 1, output_file) != strlen(str) + 1)
if(file_write(str, sizeof(*str), strlen(str) + 1, output_file))
{
json_object_put(root);
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
return_failure_int;
}
*asset_size += (strlen(str) + 1) * sizeof(*str);
break;
}
default:
error_printf("Component attribute of type %s is not handled.", json_type_to_name(json_object_get_type(attribute)));
return EXIT_FAILURE;
}
}

View File

@ -0,0 +1,50 @@
#include "object.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "utils.h"
#include "image.h"
#include "map.h"
#include "errors.h"
int build_object(const char *input_file_path, FILE *output_file)
{
// Reserve space to write asset_size
uint32_t asset_size = 0;
if(file_write(&asset_size, sizeof(asset_size), 1, output_file)) return_failure_int;
const char *input_file_dot = strrchr(input_file_path, '.');
if(!input_file_dot)
{
error_printf("Input file \"%s\" doesn't have an extension.", input_file_path);
return EXIT_FAILURE;
}
const char *input_file_ext = input_file_dot + 1;
if(!strcmp(input_file_ext, "png"))
{
if(build_image(input_file_path, output_file, &asset_size)) return_failure_int;
}
else if(!strcmp(input_file_ext, "map"))
{
if(build_map(input_file_path, output_file, &asset_size)) return_failure_int;
}
else
{
error_printf("Extension \"%s\" not supported.", input_file_ext);
return EXIT_FAILURE;
}
if(fseek(output_file, 0, SEEK_SET))
{
error_printf("Failed to seek in output file : %d", errno);
return EXIT_FAILURE;
}
if(file_write(&asset_size, sizeof(asset_size), 1, output_file)) return_failure_int;
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,27 @@
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include "errors.h"
inline int file_read(void *ptr, size_t size, size_t nb, FILE *file)
{
if(fread(ptr, size, nb, file) != nb)
{
error_printf("Failed to read output file : %d", errno);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
inline int file_write(const void *ptr, size_t size, size_t nb, FILE *file)
{
if(fwrite(ptr, size, nb, file) != nb)
{
error_printf("Failed to write to output file : %d", errno);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}