/* Module implements an output to sprite
   Created 07.06.2003 T. Milius
   Changed 05.09.2004 T. Milius */
/* (c) Copyright 2003-2004 by Thomas Milius Stade, Germany
   Source must not be altered without agreement of the owner.
   The owner of the source is allowed to use this code inside programs without
   publishing the code of this programs. These programs may be commercial.
   Other developers can use this source freely inside own software if the source
   code of this programs is made public to same conditions like valid to this code
   and no commercial profit is taken from the programs based on this code

   Code or parts of it are not allowed to be used within GPL code or
   similar licenses which are "infecting" other code and trying to "supersede"
   other licenses. */
/* RISCOS */

#ifndef sprite_output_h
#define sprite_output_h

/* !!!!!!!!!! libraries !!!!!!!!!! */
/* ---------- ANSI-C ---------- */
#include <stdlib.h>
#include <stdio.h>

/* ---------- RISCOS ---------- */
#include <kernel.h>
#include <swis.h>

/* !!!!!!!!!! definitions !!!!!!!!!! */
/* to clarify reading */
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

//#define SPRITE_TRACE_MODE

/* !!!!!!!!!! data structures !!!!!!!!!! */
struct sprite_area_struct {
unsigned long number_of_sprites;
/* Must be 4 Bytes larger than real
   because of unsaved first word */
unsigned long offset_to_first_sprite;
/* Offset to first free byte. Logically
   the same as total size. Note both values
   must be 4 bytes bigger because of unsaved
   first word */
unsigned long total_size;
};

struct sprite_header_struct {
unsigned long offset_to_next_sprite;
char name[12];
unsigned long width;
unsigned long height;
unsigned long first_bit_used;
unsigned long last_bit_used;
unsigned long offset_to_sprite_image;
unsigned long offset_to_sprite_mask;
unsigned long mode;
};

struct sprite_pallette_struct {
unsigned long first_flush_colour;
unsigned long second_flush_colour;
};

/* Sprite File */
struct sprite_output_struct {
struct sprite_area_struct sprite_area;
struct sprite_header_struct sprite_header;
int area_flag;
int error_flag;
long area_start;
long header_start;
struct sprite_pallette_struct pallette_information[256];
int max_pallette_entry;
int bits_per_pixel;
unsigned long pixel_width;
};

/* !!!!!!!!!! support functions !!!!!!!!!! */

/* !!!!!!!!!! functions !!!!!!!!!! */
int initialize_sprite_output(struct sprite_output_struct *sprite_output)
{

sprite_output->error_flag=FALSE;
sprite_output->area_flag=TRUE;
/* Sprite Area */
sprite_output->sprite_area.number_of_sprites=0;
/* Attention: Even not written first 4 bytes are counted but only inside area! */
sprite_output->sprite_area.offset_to_first_sprite=sizeof(struct sprite_area_struct) + 4;
sprite_output->sprite_area.total_size=sprite_output->sprite_area.offset_to_first_sprite;
return TRUE;
}

int initialize_sprite_pallette(struct sprite_output_struct *sprite_output)
{
int i;

sprite_output->max_pallette_entry=-1;
for (i=0; i <= 255; i++) {
  sprite_output->pallette_information[i].first_flush_colour=0xFFFFFF00;
  sprite_output->pallette_information[i].second_flush_colour=sprite_output->pallette_information[i].first_flush_colour;
  }
return TRUE;
}

int register_sprite_pallette_entry(struct sprite_output_struct *sprite_output,
                                   int entry,
                                   unsigned long entry_colour)
{

if ((entry < 0) ||
    (entry > 255)) {
  return FALSE;
  }
else {
  if (sprite_output->max_pallette_entry < entry) {
    sprite_output->max_pallette_entry=entry;
    }
  sprite_output->pallette_information[entry].first_flush_colour=entry_colour<<8;
  sprite_output->pallette_information[entry].second_flush_colour=sprite_output->pallette_information[entry].first_flush_colour;
  }
return TRUE;
}

int initialize_sprite_page(struct sprite_output_struct *sprite_output,
                           int output_file,
                           unsigned long width,
                           int pallette_mode_flag)
{
int i;
int new_sprite_modeflag;
_kernel_swi_regs regs;

if (sprite_output->error_flag) return FALSE;
regs.r[0]=129;
regs.r[1]=0;
regs.r[2]=0xFF;
_kernel_swi(OS_Byte, &regs, &regs);
if (regs.r[1] >= 0xA6) {
  new_sprite_modeflag=TRUE;
  }
else {
  new_sprite_modeflag=FALSE;
  }
/* ??? until some mor programs are supporting new
   aprite formats */
new_sprite_modeflag=FALSE;
if (pallette_mode_flag) {
  if (sprite_output->max_pallette_entry > 15) {
    sprite_output->bits_per_pixel=8;
    sprite_output->max_pallette_entry=255;
    }
  else if (sprite_output->max_pallette_entry > 3) {
    sprite_output->bits_per_pixel=4;
    sprite_output->max_pallette_entry=15;
    }
  else if (sprite_output->max_pallette_entry > 1) {
    sprite_output->bits_per_pixel=2;
    sprite_output->max_pallette_entry=3;
    }
  else {
    sprite_output->bits_per_pixel=1;
    sprite_output->max_pallette_entry=1;
    }
  }
else {
  sprite_output->bits_per_pixel=32;
  }
if (sprite_output->area_flag) {
  sprite_output->area_flag=FALSE;
  /* Note actual position for later usage */
  regs.r[0]=0;
  regs.r[1]=output_file;
  _kernel_swi(OS_Args, &regs, &regs);
  sprite_output->area_start=regs.r[2];
  /* Write Sprite area */
#ifndef SPRITE_TRACE_MODE
  regs.r[0]=2;
  regs.r[1]=output_file;
  regs.r[2]=(int) &sprite_output->sprite_area;
  regs.r[3]=sizeof(struct sprite_area_struct);
  _kernel_swi(OS_GBPB, &regs, &regs);
#endif
  }
/* Correction */
sprite_output->sprite_area.number_of_sprites++;
/* Sprite Header */
for(i=0; i < 12; i++) {
  sprite_output->sprite_header.name[i]='\0';
  }
sprintf(sprite_output->sprite_header.name,
        "page_%ld",
        sprite_output->sprite_area.number_of_sprites);
sprite_output->pixel_width=width;
sprite_output->sprite_header.width=(width*sprite_output->bits_per_pixel)/32;
if (((width*sprite_output->bits_per_pixel)%32) > 0) {
  sprite_output->sprite_header.width++;
  sprite_output->sprite_header.last_bit_used=((width*sprite_output->bits_per_pixel)%32) - 1;
  }
else {
  sprite_output->sprite_header.last_bit_used=31;
  }
sprite_output->sprite_header.height=0;
sprite_output->sprite_header.first_bit_used=0;
sprite_output->sprite_header.offset_to_sprite_image=sizeof(struct sprite_header_struct);
if (sprite_output->bits_per_pixel != 32) {
  /* Pallettte */
  sprite_output->sprite_header.offset_to_sprite_image+=(sprite_output->max_pallette_entry + 1)*sizeof(struct sprite_pallette_struct);
  }
sprite_output->sprite_header.offset_to_sprite_mask=sprite_output->sprite_header.offset_to_sprite_image;
sprite_output->sprite_header.offset_to_next_sprite=sprite_output->sprite_header.offset_to_sprite_image;
switch(sprite_output->bits_per_pixel) {
  case 1: {
    if (new_sprite_modeflag) {
      sprite_output->sprite_header.mode=0x082D0169;
      }
    else {
      sprite_output->sprite_header.mode=18;
      }
    }
  break;
  case 2: {
    if (new_sprite_modeflag) {
      sprite_output->sprite_header.mode=0x102D0169;
      }
    else {
      sprite_output->sprite_header.mode=19;
      }
    }
  break;
  case 4: {
    if (new_sprite_modeflag) {
      sprite_output->sprite_header.mode=0x182D0169;
      }
    else {
      sprite_output->sprite_header.mode=20;
      }
    }
  break;
  case 8: {
    if (new_sprite_modeflag) {
      sprite_output->sprite_header.mode=0x202D0169;
      }
    else {
      sprite_output->sprite_header.mode=21;
      }
    }
  break;
  case 32: {
    sprite_output->sprite_header.mode=0x302D0169;
    }
  break;
  }
/* Note actual position for later usage */
regs.r[0]=0;
regs.r[1]=output_file;
_kernel_swi(OS_Args, &regs, &regs);
sprite_output->header_start=regs.r[2];
/* Write Sprite header */
#ifndef SPRITE_TRACE_MODE
regs.r[0]=2;
regs.r[1]=output_file;
regs.r[2]=(int) &sprite_output->sprite_header;
regs.r[3]=sizeof(struct sprite_header_struct);
_kernel_swi(OS_GBPB, &regs, &regs);
if (sprite_output->bits_per_pixel != 32) {
  /* Pallette data */
  regs.r[0]=2;
  regs.r[1]=output_file;
  regs.r[2]=(int) sprite_output->pallette_information;
  regs.r[3]=(sprite_output->max_pallette_entry + 1)*sizeof(struct sprite_pallette_struct);
  _kernel_swi(OS_GBPB, &regs, &regs);
  }
#endif
return TRUE;
}

int finish_sprite_page(struct sprite_output_struct *sprite_output,
                       int output_file)
{
long actual_file_position;
_kernel_swi_regs regs;

if (sprite_output->error_flag) return FALSE;
/* Correction of data */
sprite_output->sprite_header.offset_to_next_sprite+=sprite_output->sprite_header.width*sprite_output->sprite_header.height*sizeof(unsigned long);
sprite_output->sprite_header.width--;
sprite_output->sprite_header.height--;
sprite_output->sprite_area.total_size+=sprite_output->sprite_header.offset_to_next_sprite;
/* Note actual position */
regs.r[0]=0;
regs.r[1]=output_file;
_kernel_swi(OS_Args, &regs, &regs);
actual_file_position=regs.r[2];
/* Rewrite of Sprite header with correct data */
#ifndef SPRITE_TRACE_MODE
regs.r[0]=1;
regs.r[1]=output_file;
regs.r[2]=(int) &sprite_output->sprite_header;
regs.r[3]=sizeof(struct sprite_header_struct);
regs.r[4]=sprite_output->header_start;
_kernel_swi(OS_GBPB, &regs, &regs);
#endif
/* Rewrite of Sprite area with updated data.
   Note this is needed here because you are not knowing
   whether a page comes still afterwards and RISCOS
   specification does not allow any output at PDumper_Abort */
#ifndef SPRITE_TRACE_MODE
regs.r[0]=1;
regs.r[1]=output_file;
regs.r[2]=(int) &sprite_output->sprite_area;
regs.r[3]=sizeof(struct sprite_area_struct);
regs.r[4]=(int) sprite_output->area_start;
_kernel_swi(OS_GBPB, &regs, &regs);
#endif
/* Restore actual position */
regs.r[0]=1;
regs.r[1]=output_file;
regs.r[2]=actual_file_position;
_kernel_swi(OS_Args, &regs, &regs);
return TRUE;
}

int write_sprite_line(struct sprite_output_struct *sprite_output,
                      int output_file,
                      unsigned long *pixel_data,
                      int line_length,
                      unsigned long paper_colour)
{
unsigned long *max_source_buffer_pos;
unsigned long *source_buffer_pos;
unsigned long *target_buffer_pos;
unsigned long map_result;
unsigned long mask;
int target_sub_pos;
_kernel_swi_regs regs;

if (sprite_output->error_flag) return FALSE;
if (line_length > sprite_output->pixel_width) {
  line_length=sprite_output->pixel_width;
  }
/* Pad with paper colour if required */
while (line_length < sprite_output->pixel_width) {
  pixel_data[line_length]=paper_colour;
  line_length++;
  }
if (sprite_output->bits_per_pixel != 32) {
  mask=(1<<sprite_output->bits_per_pixel)-1;
  max_source_buffer_pos=&pixel_data[sprite_output->pixel_width];
  source_buffer_pos=pixel_data;
  target_buffer_pos=pixel_data;
  target_sub_pos=0;
  map_result=0;
  while (source_buffer_pos < max_source_buffer_pos) {
    map_result=(map_result & (~(mask<<target_sub_pos))) | ((*source_buffer_pos)<<target_sub_pos);
    target_sub_pos+=sprite_output->bits_per_pixel;
    if (target_sub_pos >= 32) {
      *target_buffer_pos=map_result;
      map_result=0;
      target_sub_pos=0;
      target_buffer_pos++;
      }
    source_buffer_pos++;
    }
  *target_buffer_pos=map_result;
  if (target_sub_pos > 0) {
    target_buffer_pos++;
    }
  line_length=target_buffer_pos - pixel_data;
  }
/* Write Sprite line data */
#ifndef SPRITE_TRACE_MODE
regs.r[0]=2;
regs.r[1]=output_file;
regs.r[2]=(int) pixel_data;
regs.r[3]=line_length*sizeof(unsigned long);
_kernel_swi(OS_GBPB, &regs, &regs);
#endif
/* Correction */
sprite_output->sprite_header.height++;
return TRUE;
}

#endif
