cake
/
libg1m
Archived
1
0
Fork 0
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libg1m/src/manage/mcs.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);
}