|
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 idem 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.
CAPITULO 1
CREANDO
LIBRERIAS
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 LIBRERIAS 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.
UTILIZACION
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 imprecindible 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 LIBRERIA 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 apliació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 ******************************************/ |
|