300 lines
8.3 KiB
C
300 lines
8.3 KiB
C
/* *****************************************************************************
|
|
* manage/mcs.c -- manage an MCS archive handle.
|
|
* Copyright (C) 2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
|
|
*
|
|
* This file is part of libg1m.
|
|
* libg1m is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 3.0 of the License,
|
|
* or (at your option) any later version.
|
|
*
|
|
* libg1m is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with libg1m; if not, see <http://www.gnu.org/licenses/>.
|
|
* ************************************************************************** */
|
|
#include <libg1m/internals.h>
|
|
#define MCS_CHUNK_SIZE 16
|
|
|
|
/* ************************************************************************** */
|
|
/* Allocate files and content */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* g1m_make_mcsfile:
|
|
* Make an MCS file out of a head.
|
|
*
|
|
* Will allocate all of the required parts of the MCS file for it to be
|
|
* filled later.
|
|
*
|
|
* @arg handle the file to allocate.
|
|
* @arg rawhead the head to use.
|
|
* @return the error (if any).
|
|
*/
|
|
|
|
int g1m_make_mcsfile(g1m_mcsfile_t **handle, const g1m_mcshead_t *rawhead)
|
|
{
|
|
/* allocate the handle */
|
|
*handle = malloc(sizeof(g1m_mcsfile_t));
|
|
if (!handle) return (g1m_error_alloc);
|
|
g1m_mcsfile_t *h = *handle;
|
|
memset(h, 0, sizeof(g1m_mcsfile_t));
|
|
|
|
/* copy the head */
|
|
memcpy(&h->head, rawhead, sizeof(g1m_mcshead_t));
|
|
g1m_mcshead_t *head = &h->head;
|
|
|
|
/* act differently according to the context */
|
|
switch (head->type) {
|
|
case g1m_mcstype_list: case g1m_mcstype_mat: case g1m_mcstype_vct:
|
|
case g1m_mcstype_ssheet:
|
|
log_info("Preparing %d*%d matrix", head->width, head->height);
|
|
unsigned int wd = head->width, ht = head->height;
|
|
if (wd && ht) {
|
|
h->cells = malloc(sizeof(g1m_mcs_cell_t*) * ht);
|
|
if (!h->cells) goto fail;
|
|
h->cells[0] = malloc(sizeof(g1m_mcs_cell_t) * wd * ht);
|
|
if (!h->cells[0]) { free(h->cells); goto fail; }
|
|
|
|
for (unsigned int y = 1; y < ht; y++)
|
|
h->cells[y] = &h->cells[0][h->head.width * y];
|
|
}
|
|
break;
|
|
|
|
case g1m_mcstype_var:
|
|
if (head->count <= 1) h->vars = &h->var;
|
|
else {
|
|
h->vars = malloc(sizeof(g1m_mcs_cell_t) * head->count);
|
|
if (!h->vars) goto fail;
|
|
memset(h->vars, 0, sizeof(g1m_mcs_cell_t) * head->count);
|
|
}
|
|
break;
|
|
|
|
case g1m_mcstype_pict: case g1m_mcstype_capt:
|
|
/* set count */
|
|
head->count = (head->type == g1m_mcstype_pict) ? 2 : 1;
|
|
|
|
/* allocate directory */
|
|
if (head->count <= 1) h->pics = &h->pic;
|
|
else {
|
|
h->pics = malloc(sizeof(uint32_t**) * head->count);
|
|
if (!h->pics) goto fail;
|
|
}
|
|
|
|
/* allocate */
|
|
for (int i = 0; i < head->count; i++) {
|
|
h->pics[i] = alloc_pixels(head->width, head->height);
|
|
if (!h->pics[i]) {
|
|
for (int j = 0; j < i; j++)
|
|
free(h->pics[j]);
|
|
if (h->pics != &h->pic)
|
|
free(h->pics);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/* prepare */
|
|
for (int i = 0; i < head->count; i++)
|
|
prepare_pixels(h->pics[i], head->width, head->height)
|
|
break;
|
|
|
|
case g1m_mcstype_end:
|
|
break;
|
|
|
|
/* those are TEMPORARY XXX */
|
|
case g1m_mcstype_setup: case g1m_mcstype_string:
|
|
break;
|
|
|
|
default:
|
|
h->content = malloc(head->size);
|
|
if (!h->content) goto fail;
|
|
break;
|
|
}
|
|
|
|
/* finish */
|
|
return (0);
|
|
fail:
|
|
free(h);
|
|
*handle = NULL;
|
|
return (g1m_error_alloc);
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Internal functions */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* find_space_for_file:
|
|
* Find space for some element function.
|
|
*
|
|
* @arg handle the handle.
|
|
* @arg head the head.
|
|
* @return the mcs file (NULL if allocation error).
|
|
*/
|
|
|
|
static g1m_mcsfile_t *find_space_for_file(g1m_t *handle, g1m_mcshead_t *head)
|
|
{
|
|
g1m_mcsfile_t **pfile = NULL;
|
|
|
|
/* look if this file isn't already in the tab */
|
|
int istyp = g1m_mcshead_uses_id(head);
|
|
for (int i = 0; i < handle->count; i++) {
|
|
g1m_mcsfile_t *file = handle->files[i];
|
|
if (!file) {
|
|
pfile = &handle->files[i];
|
|
continue;
|
|
}
|
|
|
|
if ((file->head.type == head->type) && (istyp ?
|
|
file->head.id == head->id : !strcmp(file->head.name, head->name))) {
|
|
g1m_free_mcsfile(file);
|
|
pfile = &handle->files[i];
|
|
goto found;
|
|
}
|
|
}
|
|
|
|
/* if no empty space was found, take one at the end (allocate if needed) */
|
|
if (!pfile) {
|
|
if (handle->count >= handle->_size) {
|
|
handle->_size = handle->count + MCS_CHUNK_SIZE;
|
|
handle->files = realloc(handle->files,
|
|
handle->_size * sizeof(g1m_mcsfile_t**));
|
|
if (!handle->files) return (NULL);
|
|
}
|
|
pfile = &handle->files[handle->count++];
|
|
}
|
|
|
|
found:;
|
|
/* make the file */
|
|
int err = g1m_make_mcsfile(pfile, head);
|
|
if (err) return (NULL);
|
|
|
|
/* return it */
|
|
return (*pfile);
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Create files in archive */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* g1m_putmcs_program:
|
|
* Put a program in a MCS handle.
|
|
*
|
|
* @arg handle the handle.
|
|
* @arg pfile pointer to the file pointer to set.
|
|
* @arg name the name of the program.
|
|
* @arg password the password (NULL if none).
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
int g1m_putmcs_program(g1m_t *handle, g1m_mcsfile_t **pfile,
|
|
char *name, char *password, char *content)
|
|
{
|
|
/* check if the operation is supported */
|
|
if (~handle->type & g1m_type_mcs) return (g1m_error_op);
|
|
|
|
/* find the file */
|
|
g1m_mcshead_t head = {
|
|
.type = g1m_mcstype_program,
|
|
.size = strlen(content)
|
|
};
|
|
strncpy(head.name, name, 8); head.name[8] = 0;
|
|
if (password) strncpy(head.password, password, 8);
|
|
else head.password[0] = 0;
|
|
g1m_mcsfile_t *file = find_space_for_file(handle, &head);
|
|
if (!file) return (g1m_error_alloc);
|
|
|
|
/* set content */
|
|
memcpy(file->content, content, file->head.size);
|
|
|
|
/* no error */
|
|
if (pfile) *pfile = file;
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
* g1m_putmcs_capture:
|
|
* Put a capture in an MCS handle.
|
|
*
|
|
* @arg handle the handle.
|
|
* @arg pfile the file pointer to get file.
|
|
* @arg id the capture ID (1 to 20).
|
|
* @arg width the capture width.
|
|
* @arg height the capture height.
|
|
* @arg pic the pixels to copy.
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
int g1m_putmcs_capture(g1m_t *handle, g1m_mcsfile_t **pfile,
|
|
int id, int width, int height, uint32_t **pic)
|
|
{
|
|
/* check if the operation is supported */
|
|
if (~handle->type & g1m_type_mcs || id < 1 || id > 20)
|
|
return (g1m_error_op);
|
|
|
|
/* find the file */
|
|
g1m_mcshead_t head = {
|
|
.type = g1m_mcstype_capture, .id = id,
|
|
.width = width, .height = height
|
|
};
|
|
g1m_mcsfile_t *file = find_space_for_file(handle, &head);
|
|
if (!file) return (g1m_error_alloc);
|
|
|
|
/* copy pixels */
|
|
uint32_t *pix = &file->pic[0][0];
|
|
for (int y = 0; y < height; y++) {
|
|
uint32_t *line = *pic++;
|
|
for (int x = 0; x < width; x++)
|
|
*pix++ = *line++;
|
|
}
|
|
|
|
/* no error */
|
|
if (pfile) *pfile = file;
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
* g1m_putmcs_picture:
|
|
* Put a picture in an MCS handle.
|
|
*
|
|
* @arg handle the handle.
|
|
* @arg pfile the file pointer to get.
|
|
* @arg id the picture ID (1 to 20).
|
|
* @arg pic_one pixels of the first 128x64 image to copy.
|
|
* @arg pic_two pixels of the second 128x64 image to copy.
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
int g1m_putmcs_picture(g1m_t *handle, g1m_mcsfile_t **pfile,
|
|
int id, uint32_t **pic_one, uint32_t **pic_two)
|
|
{
|
|
/* check if the operation is supported */
|
|
if (~handle->type & g1m_type_mcs || id < 1 || id > 20)
|
|
return (g1m_error_op);
|
|
|
|
/* find the file */
|
|
g1m_mcshead_t head = {
|
|
.type = g1m_mcstype_picture, .id = id,
|
|
.width = 128, .height = 64
|
|
};
|
|
g1m_mcsfile_t *file = find_space_for_file(handle, &head);
|
|
if (!file) return (g1m_error_alloc);
|
|
|
|
/* copy pixels */
|
|
uint32_t *one = &file->pics[0][0][0];
|
|
uint32_t *two = &file->pics[1][0][0];
|
|
for (int y = 0; y < 64; y++) {
|
|
uint32_t *line_one = *pic_one++;
|
|
uint32_t *line_two = *pic_two++;
|
|
for (int x = 0; x < 128; x++) {
|
|
*one++ = *line_one++;
|
|
*two++ = *line_two++;
|
|
}
|
|
}
|
|
|
|
/* no error */
|
|
if (pfile) *pfile = file;
|
|
return (0);
|
|
}
|