|
Programación de librerías
ILBM
Por Vicente Santos
NOTA: Las fotos con recuadro en azul pueden ser ampliadas para ver detalles. Situando el ratón encima de la foto durante unos instantes, podrás ver una pequeña descripción y lo que ocupa la ampliación.
Las librerías son muy importantes en la correcta programación de sistemas complejos. En el Amiga, por supuesto, el dominio de su programación pasa por la correcta utilización de las librerías propias o del sistema. |
Mi nombre es Vicente y soy usuario de ordenadores Amiga desde hace tres años largos. Una de mis debilidades a la hora de trabajar con esta máquina es la programación y este es el motivo por el que ahora les estoy presentando este trabajo. Hace algunos meses descubrí en las páginas de Amiga World su solicitud de colaboraciones y esto, unido a la debilidad citada, me animó a crear alguna aplicación que pudiese ser útil a los lectores de la revista (y entretenida para mi al realizarla). mientras la estaba escribiendo se me ocurrió que podría ser interesante, al menos para algunos, seguir paso a paso el desarrollo de la misma... y aquí tienen el resultado: una serie de cuatro capítulos en la que se muestra la creación de una librería "run-time" (es decir, como las que vienen con el sistema operativo) que además, una vez generada, pueden utilizarla en sus programas.
Con cada capítulo se dispone del código fuente en C y Ensamblador de los módulos que forman la librería, instrucciones de compilación y ensamblaje, ficheros de enlace y cuanta información se necesita para la obtención de la misma, así como los listados de un par de programas demostrativos de su uso.
En el directorio ":Libs/" está el ejecutable de la librería, listo para copiarlo en el disco de trabajo.
En otro directorio, ":Ejecutables/", los ídem de los programas de demostración y una librería de compilador para enlazar los suyos (si en algún momento decide escribir alguno que utilice la run-time).
En fin, para no cansar más, decir solamente que espero que la serie y la librería resulten útiles y sobre todo sirvan para que se animen a crear aplicaciones para nuestras máquinas (¡y que podamos compartirlas!).
Se me olvidaba. Tanto la librería como los porgramas y ficheros que la acompañan SON DE DOMINIO PUBLICO.
CAPÍTULO 1
CREANDO LIBRERÍAS
Todos los aficionados a la programación nos hemos encontrado con ellas. Son parte esencial de nuestro ordenador, diseñadas para permitirnos el acceso a cualquiera de los elementos que forman su estupendo -y complejo- sistema operativo a fin de que podamos aprovechar al máximo sus muchas prestaciones. Pero, como siempre, la carencia epidémica de información técnica en castellano nos tiene condenados, las más de las veces, a conocer solamente un aspecto: su uso. Pues bien, con la serie que iniciamos vamos a tratar de poner una cataplasma a esta epidemia explicando en lo posible, qué son, como están formadas y -lo mejor- como crear librerías del sistema.
LAS LIBRERÍAS DEL AMIGA
Una librería -en otros sistemas se llaman "bibliotecas", pues esa es la traducción correcta de la palabra "library"- no es más que una colección de rutinas agrupadas bajo un denominador común, normalmente el tipo de aplicación para cuyo tratamiento hayan sido diseñadas. En el Amiga las hay de dos tipos:
* Librerías explorables ("scaned libraries"), llamadas así por la forma en que el enlazador busca en ellas las rutinas solicitadas por nuestros programas para, una vez encontradas, "inyectar" literalmente su código en ellos. Forman parte de compiladores y ensambladores.
* Librerías del sistema, oficialmente llamadas "run-time", o sea, a "tiempo de ejecución". Se denominan así porque sus rutinas no forman parte de los programas, sino que se encuentran en cualquier zona de la memoria del ordenador y permanecen en ella mientras se requiera su uso. Son las que vienen "de fábrica" con el sistema operativo, unas en la ROM y otras -las de uso menos frecuente- en disco.
De ambos sólo nos interesa el segundo, que es el objetivo de la serie, pero ya veremos hacia el final de esta la relación que existe entre ambos, especialmente cuando se desarrollan librerías run-time de usuario.
LAS RUN-TIME POR DENTRO
Una librería run-time se compone de los elementos siguientes:
* Una colección de funciones accesibles por todas las aplicaciones del usuario y el sistema operativo.
* Una tabla de "vectores de salto" (_LVO - Library Vector Offset) a las funciones.
* Y una estructura llamada "Base" formada por:- otra estructura elemental del sistema llamada "Nodo" que contiene información sobre otros nodos, el tipo de nodo, la prioridad de carga y el nombre de nodo.
- Variables relativas al estado interno de la librería y
- variables y estructuras específicas de cada una.
Esta "Base" con todos sus componentes más la tabla de vectores forma el "área de datos" de la librería y es semiindependiente de las funciones. Todos estos elementos son cargados en memoria, inicializados y mantenidos en ella por Exec, pero para ello es necesaria la presencia de otro más: una estructura "romtag" -algo así como "señal de rom"- cuya misión es permitir que Exec mantenga la librería "residente" en la memoria. Para que Exec pueda efectuar el proceso descrito necesita conocer dónde se encuentra físicamente la librería. Ya hemos visto que puede estar en dos sitios: la ROM o un disco. Si está en ROM no hay problema, pero si reside en disco es necesario que se encuentre en un directorio previamente asignado al dispositivo lógico "LIBS:". Este directorio es, por defecto, el ":Libs/" del Workbench, aunque podemos cambiarlo a otro cualquiera utilizando el comando ASSIGN del AmigaDOS, por ejemplo: ASSIGN LIBS: midisco:mislibs. Pero ¡cuidado!, este cambio de asignación siempre debe hacerse ANTES de utilizar la librería si no queremos que el sistema se "cuelgue" (un buen lugar para realizarlo de forma automática es el fichero startup-sequence).
En caso de que la run-time no se encuentre en el directorio asignado a "LIBS:" será necesario cargarla e inicializarla "a mano", desarrollando un programa al efecto -método que por su laboriosidad no utilizaremos aquí-.
Una vez en memoria el aspecto de la librería es el que podéis ver en la figura 1. Naturalmente para que Exec ejecute todo este proceso habrá que decírselo antes, lo que nos lleva directamente al uso.
UTILIZACIÓN
Quien ya lo sepa tiene permiso de la autoridad competente para saltarse este párrafo -aunque si lo hace seguro que se pierde alguna cosa curiosa-.
Librería Run-Time en memoria |
Lo primero es indicarle a Exec que queremos usar una run-time. Esto se conoce como "apertura" de la librería y para ello se llama a la función de Exec "OpenLibrary()" dándole dos argumentos: el nombre y la versión de la que deseamos -o, en su defecto, el valor comodín "OL"-. OpenLibrary() hace que Exec busque en su lista de librerías del sistema la que queremos y si la encuentra "y la propia librería lo permite" -ya veremos cómo y porqué- podremos acceder a sus funciones. En caso de que no aparezca en la lista. Exec busca en el directorio asignado a "LIBS:". Si está ahí la cargará, inicializará y añadirá a su lista, de nuevo si la librería lo permite. En ambos casos OpenLibrary() nos devolverá la dirección de memoria a partir de la cual Exec ha colocado la Base -más concretamente el "Nodo"-, dirección que será necesario guardar. Si la librería no aparece o Exec no obtiene su permiso para cargarla OpenLibrary() devolverá NULL (0).
Tras una apertura exitosa tendremos vía libre para usar las funciones que deseemos, aunque la forma de hacerlo variará según el lenguaje que estemos empleando ya que puede ser necesario el uso de ficheros especiales -caso del AmigaBASIC o del C con ficheros "proto"- o el enlazado con rutinas de llamada en librerías explorables -como en C sin "protos" o, en algunos casos, Ensamblador-.
Una vez finalizado el trabajo es imprescindible comunicárselo a Exec. Esta operación es el "cierre" de la librería y si ejecuta llamando a la función -adivinen de quién- "CloseLibrary()", pasándole como argumento la dirección de la Base previamente obtenida.
Como veis todo muy fácil, así que vamos a peternos de lleno en el tema central de la serie: la creación de nuestra propia run-time.
UNA LIBRERÍA DE USUARIO
Quienes sean afortunados poseedores de los manuales oficiales del Amiga y además entiendan el inglés posiblemente habrán visto que en el volumen "Includes & Autodocs" aparece un ejemplo de librería run-time. El problema es que no se explica con detalle como implementarla. Por otro lado en otras publicaciones -siempre en inglés- sí han aparecido ejemplos similares bien documentados sobre la implementación, pero en ambos casos las librerías generadas son solo eso: ejemplos, carentes de utilidad práctica en programas reales. Bueno, pues aquí no solo vamos a ver la creación de una sino que, además, podremos emplearla en nuestros futuros programas.
Para ello lo primero que debemos considerar es el "tema", o sea el tipo de aplicación para el que estará orientada (ahora viene la sorpresa): una de las tareas a las que más dedicación prestamos los usuarios de Amiga es la creación y tratamiento de gráficos y, más concretamente, el de gráficos en formato IFF-ILBM, así que la librería servirá para el manejo de estos.
Pantalla explicativa, incluida en los programas |
Vamos a ver ahora las herramientas. Una característica de las run-time es que sus funciones pueden escribirse en casi cualquier lenguaje -chicos del BASIC abstenerse-. Esto facilita mucho su desarrollo ya que permite probarlas antes de incluirlas definitivamente en la librería. Como uno de los lenguajes más utilizados es el C -de hecho se considera "oficiosamente" el lenguaje nativo del Amiga-, vamos a emplearlo para escribir las nuestras. ¿Y el área de datos?...pues en Ensamblador, como manda nuestra santa madre Commodore.
Tal vez alguno se extrañe de esta rara mezcla, pero es bastante habitual -una buen parte de las librerías oficiales se han construido así-. Por si os sirve de orientación yo he utilizado Lattice C 4.0 -algo antiguo pero útil- y HiSoft Devpac 2. Por supuesto quien lo desee puede emplear el MacroAssembler que va incluido con el compilador o cualquier otro de su gusto (mi elección del Devpac es, simplemente, porque acabo de comprarlo y quería probarlo a fondo...).
En cuanto a requerimientos hardware cualquier máquina con 512 Kb de RAM es más que suficiente aunque mejor si tiene más, dos disqueteras o disco duro.
Y ahora que ya tenemos tema y herramientas vamos a empezar (¡¡al fin!!) a escribir nuestra propia run-time
ILBM.LIBRARY
Ya lo sé, hay otras librerías dedicadas a los IFF, incluso en la versión 2.0 del Workbench, pero si dan un vistazo a la figura 2 verán por qué la nuestra puede ser incluso más útil que alguna de ellas.
Antes de comenzar con el trabajo serio, una cosilla a tener en cuenta: a causa de lo forzosamente resumida que tiene que ser esta serie aquellos que no tengáis una idea clara de qué son los IFF en general y el subtipo ILBM en particular es aconsejable que se informen sobre ellos. Aquí va algo de bibliografía para consultar:
* para los que entiendan inglés:
Rom Kernel Reference Manual: Includes & Autodocs, apéndice I. Contiene la documentación oficial de Electronic Arts y Commodore. Incluye el código fuente de las funciones originales de EA y programas demostrativos, todo ello en C.
* para los que no (vaya, casi todos nosotros):
Revista Commodore World número 50, suplemento Amiga World. Artículo -muy bueno- sobre los IFF-ILBM. Incluye programas demostrativos en AmigaBASIC.
Y ahora sí, empezamos.
Lo primero que vamos a escribir es un fichero de cabecera en el que definiremos modelos de estructuras y constantes para facilitar el desarrollo posterior de las funciones. Estas estructuras son en su mayoría las originales de Electornic Arts así que para no liar las cosas con posibles meteduras de pata en la traducción de sus nombres y los de sus variables he preferido mantener los ingleses. El fichero, llamado"ilbmdefs.h", lo tienen acompañando a este capítulo.
Una vez creado -guardenlo en lugar seguro- vamos con las funciones. Estas pueden encontrarla en dos módulos separados: "ilbmread.c" contiene las de lectura, descompresión y visualización de gráficos: "ilbmwrite.c" las de escritura con o sin compresión.
Cada función va precedida de un comentario explicativo de el trabajo que realiza así que no es necesario analizarlas aquí una por una. En ambos módulos hay dos tipos de funciones: las propias de la librería y las que sólo servirán de "apoyo" para estas. Para distinguir unas de otras he añadido al nombre de las primeras el sufijo "_c". Puede que esto os suene raro a causa de lo feos que quedan pero la verdad es que no van a ser los definitivos (en realidad solo son simples idetificadores del código de cada una para uso del programa principal, que veremos en el siguiente capítulo).
Una vez escrito el código fuente es necesario compilarlo. Para ello, con SAS/Lattice C utilizaremos el siguiente mandato:
lc -v ilbmread ilbmwrite.
La opción "-v" es necesaria para evitar que el compilador genere código de comprobación de la pila (stack checking) al principio de cada función.
Los usuarios de Aztec C consulten la documentación para compilar, si es posible, con opciones similares. Además, al utilizar programación modular, deben evitar la inclusión de "public .begin" en todos los módulos ya que el código de inicio lo desarrollaremos nosotros mismos.
Y con esto finalizamos por ahora. En el próximo capítulo terminaremos de crear la librería y veremos como isntalarla en nuestro sistema para que Exec la reconozca como a cualquiera de las oficiales de Amiga.
A continuación, los listados de los programas. El último de este capítulo se
publicará en otro número, por falta de espacio físico para más listados.
/*****************************************************************
FICHERO .H: ilbmdefs.h
DESCRIPCION: Definicion de estructuras para compilacion de
funciones.
DESARROLLO : 15-06-92 - Creacion (V. Santos)
*****************************************************************/
/* MACRO PARA IDENTIFICADORES DE TIPO, SUBTIPO Y CHUNKS ========*/
#define MakeID(a,b,c,d) ((LONG)(a)<<24L|(LONG)(b)<<16L|(c)<<8|(d))
/* IDENTIFICADORES DE TIPO Y SUBTIPO ===========================*/
#define FORM MakeID('F','O','R','M')
#define ILBM MakeID('I','L','B','M')
/* IDENTIFICADORES DE CHUNK ====================================*/
#define BMHD MakeID('B','M','H','D')
#define CAMG MakeID('C','A','M','G')
#define CMAP MakeID('C','M','A','P')
#define BODY MakeID('B','O','D','Y')
/* MODELO DE ESTRUCTURA DE CABECERA DE FICHERO =================*/
struct IFFHead {
LONG typeID;
LONG size;
LONG subtypeID;
};
/* MODELO DE ESTRUCTURA DE CABECERA DE CHUNK ===================*/
struct ChunkHead {
LONG ckID;
LONG ckSize;
};
/* MODELO DE ESTRUCTURA DE CHUNK BMHD ==========================*/
struct BitMapHeader {
UWORD w,h;
WORD x,y;
UBYTE nPlanes,masking,compression,pad1;
BYTE xAspect,yAspect;
WORD pageWidth,pageHeight;
};
/* Relacion de aspecto horizontal/vertical segun especificaciones
de Electronic Arts. Las siguientes definiciones han sido
extraidas de la documentacion publicada en el "RKM:Includes &
Autodocs" y son validad para graficos NTSC.
Para graficos PAL no hay documentacion oficial (que yo sepa) y
los programas que trabajan en este modo no siguen un mismo
criterio.
Ej: Deluxe Paint III utiliza una relacion de aspecto 1:1 para
graficos en 320x256. Deluxe Photolab, también de Electronic
Arts, calcula su propia relacion (163:154 para 320x256).
Como DPaintIII es el mas popular, aqui he decidido seguir sus
normas utilizando la relacion 1:1 para graficos PAL. */
/* NTSC */
#define X_ASP_320x200 10
#define X_ASP_320x400 20
#define X_ASP_640x200 5
#define X_ASP_640x400 10
#define Y_ASP_TODOS 11
/* PAL */
#define XY_ASP_PAL 1
/* MODELO DE ESTRUCTURA DE REGISTRO DE COLOR DE CHUNK CMAP ====*/
struct RegColor {
UBYTE r,g,b;
};
/* tamano de registro de color segun especificaciones de
Electronic Arts (utilizar esta definicion en lugar de
"sizeof(stuct RegColor)" */
#define SIZEREGCOLOR 3
/* mascara para escritura de datos */
#define COLORMASK 0xF0
/* FIN DE ilbmdefs.h ********************************************
//***************************************************************
MODULO : ilbmread.c
DESCRIPCION : funciones para lectura de ficheros IFF-ILBM.
DESARROLLO : 15-06-92 - Creacion (V. Santos)
****************************************************************/
/* FICHEROS DE CABECERA =======================================*/
#include <exec/types.h>
#include <exec/memory.h>
#incluse <graphics/gfx.h>
#include <graphics/view.h>
#include <libraries/dosextens.h>
#include "ilbmdefs.h"
/* DEFINICIONES================================================*/
/* macros para descompresion */
#define GetByte() (*fuente++)
#define PutByte(dato) (*destino++=(dato))
/* GLOBALES====================================================*/
/* prototipos */
LONG checkilbm_c(struct FileHandle *);
struct BitMapHeader *getbmheader_c(struct FileHandle *,
struct BitMapHeader*);
LONG getgfxmode_c(struct FileHandle *);
LONG getcolmap_c(struct FileHandle *, struct BitMap *,
UBYTE,UBYTE);
BOOL findchunk(struct FileHandle *,struct ChunkeHead *);
BOOL unpackbr1(BYTE **,BYTE **,LONG);
/* FUNCIONES DE LIBRERIA =====================================*/
/* checkilbm_c()------------------------------------------------
Descripcion: comprueba si el fichero es IFF del subtipo ILBM
Argumentos : puntero a fichero.
Retorno : tamano total si es IFF-ILBM, NULL si no lo es o
error de lectura
--------------------------------------------------------------*/
LONG checkilbm_c(f)
struct FileHandle *f;
{
struct IFFHead iffh; /* cabecera de fichero */
LONG n; /* comprobacion de lectura */
if((n = Read((BPTR)f,(BYTE *)&iffh;, 12))!= 12)
return(NULL);
if(iffh.typeIDE == FORM)
{
if(iffh.subtypeID == ILBM)
return(iffh.size);
else
return(NULL);
}
else
return(NULL);
}
/* getbmheader_c() --------------------------------------------
Descripcion : lee datos de BMHD.
Argumentos : puntero a fichero.
direccion de escritura de modelo BitMapHeader
Retorno : puntero a estructura BitMapHeader ya inicializada
o NULL si error de lectura o chunk no encontrado.
-------------------------------------------------------------*/
struct BitMapHeader *getbmheader_c(f,bmhd)
struct FileHandle *f;
struct BitMapHeader *bmhd;
{
struct ChunkHead ckh; /* cabecera de chunk */
LONG n; /* comprobacion de lectura */
BOOL encontrado = TRUE; /* indicador */
/* buscar chunk */
ckh.ckID = BMHD;
if((encontrado = findchunk(f,&ckh;)) == FALSE)
return(NULL);
/* leer datos */
if((n = Read((BPTR)f, (BYTE *)nmhd,ckh.ckSize)) != 20)
return(NULL);
return(bmhd);
}
/* getgfxmode_c()--------------------------------------------
Descripcion : lee modo grafico en CAMG.
Argumentos : puntero a fichero.
Retorno : modo grafico o NULL si chunk no encontrado o
error de lectura.
-----------------------------------------------------------*/
LONG getgfxmode_c(f)
struct FileHandle *f;
{
struct ChunkHead ckh; /* cabecera de chunk */
ULONG gmodo; /* modo grafico */
LONG n; /* comprobacion de lectura */
BOOL encontrado = TRUE; /* indicador */
ckh.ckID = CAMG;
if((encontrado = findchunk(f,&ckh;)) == FALSE)
return(NULL);
if((n = Read((BPTR)f,(BYTE *)&gmodo;,ckh.ckSize))!=4)
return(NULL);
return(gmodo);
}
/* getcolmap_c()---------------------------------------------
Descripcion : lee paleta de colores convirtiendo de formato
ILBM-CMAP (0xR0, 0xG0, 0xB0) a Amiga-CMap
(0x0RGB).
Argumentos : puntero a fichero,
direccion de array para contener colores,
numero de planos de bits.
Retorno : numero de colores o 0 si chunk no encontrado o
error de lectura
-----------------------------------------------------------*/
LONG getcolmap_c(f, colores, nplanos)
struct FileHandle *f;
UWORD *colores;
UBYTE nplanos;
{
struct ChunkHead ckh; /* cabecera de chunk */
struct RegColor rc; /* registros en formato ILBM-CMAP */
LONG numrc; /* numero de registros de color */
LONG n; /* comprobacion de lectura */
WORD i; /* control de bucle de lectura */
BOOL encontrado = TRUE; /* indicador */
ckh.ckID = CMAP;
if((encontrado = findchunk(f,&ckh;)) == FALSE)
return(NULL);
/* averiguar numero de registros de color */
numrc = (((ckh.ckSize >> nplanos) & 1)<< nplanos);
/* leer datos en formato ILBM... */
for(i = 0;i < numrc;i++)
{
if((n = Read((BPTR)f,(BYTE *)&rc;, SIZEREGCOLOR)) !=
SIZEREGCOLOR)
return(NULL);
/* ...convirtiendo a formato Amiga */
*colores++ = ((rc.r << 4) + (rc.g) + (rc.b >> 4));
}
return(numrc);
}
/* displaydata_c()--------------------------------------------
Descripcion : lee y visualiza datos graficos de BODY. SI estos
estan comprimidos llama a su funcion auxiliar
"unpackbr1()".
Argumentos : puntero a fichero,
direccion del BitMap,
tipo de mascara (para esta version siempre 0),
tipo de compresion (no comp = 0, ByteRun1 = 1).
Retorno : bytes leidos si no hay errores, NULL si chunk no
encontrado,
memoria insuficiente, error en reserva, error en
descompresion o error en lectura.
-----------------------------------------------------------*/
LONG displaydata_c(f, bmap, mask, comp)
struct FileHnadle *f;
struct BitMap *bmap;
UBYTE mask, comp;
{
struct ChunkHead ckh; /* cabecera de chunk */
ULONG memlibre; /* comprobacion de memoria */
BYTE *bufmem, *memdirec; /* memoria para datos */
BYTE *planos[6]; /* copia de planos de bits */
LONG bytesfila; /* num de bytes por fila */
LONG n; /* comprobacion de lectura */
WORD i,j; /* control de bucles */
BOOL encontrado = TRUE; /* indicador */
BOOL nodesc = FALSE; /* error de descompresion */
ckh.ckID = BODY;
iff((encontrado = findchunk(f, &ckh;)) == FALSE)
return(NULL);
/* comprobar si hay memoira suficiente y
si la hay reservar */
if((memlibre = AvailMem(MEMF_PUBLIC)) < ckh,ckSize)
return(NULL);
if((bufmem = (BYTE *)AllocMem(ckh.ckSize, MEMF_PUBLIC))
== NULL)
return(NULL);
memdirec = bufmen;
/* pasar datos a memoria */
if((n = Read((BPTR)f, bufmem, ckh.ckSize)) !=ckh.ckSize)
{
FreeMem(memdirec,ckh.ckSize);
return(NULL);
}
/* sacar copias de planos de bits */
for(j = 0;j < bmap->Depth; j++)
planos[j] = (BYTE *)bmap->Planes[j];
/* visualizar */
bytesfila = bmap->BytesPerRow;
for(i = bmap->Rows; i > 0;i--)
{
for(j = 0;j < bmap->Depth; j++)
{
if(comp == 0)
{
CopyMem((APTR)bufmem,(APTR)planos[j], bytesfila):
bufmem += bytesfila;
planos[j] += bytesfila;
}
else
{
if((nodesc = unpackbr1(&bufmem;,&planos;[j], bytesfila))
== TRUE)
{
FreeMem(memdirec, ckh,ckSize);
return(NULL);
}
}
}
}
/* liberar memoria */
FreeMem(memdirec, ckh.ckSize);
return(ckh.ckSize);
}
/* FUNCIONES AUXILIARES (USO INTERNO) =======================*/
/* findchunk()-------------------------------------------------
Descripcion : funcion auxiliar para todas las de lectura. Busca
chunks en el fichero sin importa el orden en que
se encuentren.
Argumentos : puntero a fichero,
direccion de estructura de modelo ChunkHead.
Retorno : TRUE si chunk encontrado, FALSE si no o error en
lectura.
------------------------------------------------------------*/
BOOL findchunk(f,ckh)
struct FileHandle *f;
struct ChunkHead *ckh;
{
struct ChunkHead chunk; /* para lectura de cabeceras */
LONG n; /* comprobacion de lectura */
/* buscar chunks desde inicio de fichero (saltando FORM) */
Seek((BPTR)f, 12, OFFSET_BEGINNING);
while(! existe)
{
if((n = Read((BPTR)f, (BYTE *)&chunk;,8))!=8)
break;
if(chunk.ckID == ckh->ckID)
{
ckh->ckSize = chunk.ckSize;
existe = TRUE;
}
else
{
/* saltar chunks no buscados o desconocidos
(si tamaño impar saltar 1 byte mas)*/
if((chunk.ckSize) & 1)
chunk.ckSize += 1;
Seek((BPTR)f,chunk.ckSize, OFFSET_CURRENT);
continue;
}
}
return(existe);
}
/* unpackbr1()----------------------------------------------
Descricion : Funcion auxiliar de "displaydata_c", descompri-
me datos en formato ByteRun1.
Argumentos : puntero a puntero copias de planos de bites,
numero de bytes por fila en cada plano.
Retorno : FALSE si no hay fallos, TRUE si numero de bytes
resultantes de descompresion mayor que
numero de bytes por fila.
Nota : Adaptacion de la funcion "UnPackRow()" de
Electronic Arts, original de Jerry Morrison y
Steve Shaw y puesta en el Dominio Publico
---------------------------------------------------------*/
BOOL unpackbr1(buf, planos, nbytes)
BYTE **buf, **planos;
LONG nbytes;
{
register BYTE *fuente = *buf; /* datos en nuffer */
register BYTE *destino = *planos; /* copias de planos de bits */
register WORD control; /* byte de control */
register BYTE dato; /* byte de dato grafico */
LONG numb = 0; /* control de bytes por fila */
WORD nohacernada = -128; /* esta claro, no? */
BOOL error = FALSE; /* asumir no errores */
/* iniciar descompresion */
do
{
if((control = GetByte()) > 0) /* control entre 0 y 128 */
{
control + = 1;
if((numb += control) > nbytes) /* comprobar bytes por fila */
error = TRUE;
else
{
do
{
PutByte(GetByte()); /* pasar bytes no empaquetados */
} while(--control > 0);
}
}
else
{
if(control != nohacernada) /* o sea, entre -1 y -127 */
{
control = -control + 1;
if((numb += control) > nbytes)
error = TRUE;
else
{
dato = GetByte();
do
{
PutByte(dato): /* pasar byte empaquetado */
} while(--control > 0);
}
}
} while(numb < nbytes);
/* actualizar punteros */
*buf = fuente;
*planos = destino;
return(error);
}
/* FIN DE ilbmread.c ******************************************/ |
|