/* ** XModuleHook.c ** ** Copyright (C) 1994,95,96,97 Bernardo Innocenti ** ** Internal loader/saver hook for the IFF XMOD module format */ #include #include #include #include #include #include #include #include "XModulePriv.h" #include "Gui.h" /* Local functions prototypes */ static HOOKCALL struct XMHook *IdentifyXModule ( REG(d0, BPTR fh), REG(a0, struct XMHook *loader), REG(a1, ULONG *tags)); static HOOKCALL LONG LoadXModule ( REG(d0, BPTR fh), REG(a0, struct SongInfo *si), REG(a1, struct XMHook *loader), REG(a2, ULONG *tags)); static HOOKCALL LONG SaveXModule ( REG(d0, BPTR fh), REG(a0, struct SongInfo *si), REG(a1, struct XMHook *saver), REG(a2, ULONG *tags)); GLOBALCALL void AddXModuleHooks (void) /* Adds XModule loader and saver */ { xmAddHook ( XMHOOK_Type, NT_XMLOADER, XMHOOK_Name, (LONG)"XModule", XMHOOK_Priority, 50, XMHOOK_Descr, (LONG)"XModule internal format", XMHOOK_Author, (LONG)"Bernardo Innocenti", XMHOOK_Flags, XMHF_INTERNAL, XMHOOK_LoadModFunc, LoadXModule, XMHOOK_IdentifyModFunc, IdentifyXModule, TAG_DONE); xmAddHook ( XMHOOK_Type, NT_XMSAVER, XMHOOK_Name, (LONG)"XModule", XMHOOK_Priority, 50, XMHOOK_Descr, (LONG)"XModule Internal format", XMHOOK_Author, (LONG)"Bernardo Innocenti", XMHOOK_Flags, XMHF_INTERNAL | XMHF_EXCLUDE_INSTRUMENTS | XMHF_EXCLUDE_NAMES | XMHF_EXCLUDE_SEQUENCE, XMHOOK_SaveModFunc, SaveXModule, TAG_DONE); } static HOOKCALL struct XMHook *IdentifyXModule ( REG(d0, BPTR fh), REG(a0, struct XMHook *loader), REG(a1, ULONG *tags)) /* Determine if the given file is an XModule module. * Note: the file position will be changed on exit. */ { ULONG id[3]; Seek (fh, 0, OFFSET_BEGINNING); if (FRead (fh, &id, 12, 1) != 1) return NULL; if ((id[0] == ID_FORM) && (id[2] == ID_XMOD)) return loader; return NULL; } static HOOKCALL LONG LoadXModule ( REG(d0, BPTR fh), REG(a0, struct SongInfo *si), REG(a1, struct XMHook *loader), REG(a2, ULONG *tags)) { struct IFFHandle *iff; struct ContextNode *cn; LONG err; if (iff = AllocIFF()) { iff->iff_Stream = (ULONG) fh; InitIFFasDOS (iff); if (!(err = OpenIFF (iff, IFFF_READ))) { struct ModuleHeader mhdr; static LONG stopchunks[] = { ID_XMOD, ID_NAME, ID_XMOD, ID_MHDR, ID_SONG, ID_FORM }; if (err = StopChunks (iff, stopchunks, 3)) goto error; if (err = StopOnExit (iff, ID_XMOD, ID_FORM)) goto error; /* Scan module */ while (1) { if (err = ParseIFF (iff, IFFPARSE_SCAN)) { if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK; break; /* Free resources & exit */ } if (cn = CurrentChunk (iff)) { switch (cn->cn_ID) { case ID_NAME: { UBYTE name[128]; ReadChunkBytes (iff, name, min(cn->cn_Size, 127)); name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */ SetAttrs (si, SNGA_Title, name, TAG_DONE); break; } case ID_MHDR: if ((err = ReadChunkBytes (iff, &mhdr, sizeof (mhdr))) != sizeof(mhdr)) goto error; break; case ID_FORM: if (cn->cn_Type == ID_SONG) if (err = LoadSong (iff, si)) goto error; break; default: break; } } } error: CloseIFF (iff); } FreeIFF (iff); } else err = ERROR_NO_FREE_STORE; return err; } GLOBALCALL LONG LoadSong (struct IFFHandle *iff, struct SongInfo *si) { LONG err, len; struct ContextNode *cn; struct SongHeader shdr; static LONG stopchunks[] = { ID_SONG, ID_NAME, ID_SONG, ID_AUTH, ID_SONG, ID_ANNO, ID_SONG, ID_SHDR, ID_SONG, ID_SEQN, ID_PATT, ID_FORM, ID_8SVX, ID_FORM }; memset (&shdr, 0, sizeof (shdr)); if (err = StopChunks (iff, stopchunks, 7)) return err; if (err = StopOnExit (iff, ID_SONG, ID_FORM)) return err; /* Scan song */ while (1) { if (err = ParseIFF (iff, IFFPARSE_SCAN)) { if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK; break; /* Free resources & exit */ } if (cn = CurrentChunk (iff)) { switch (cn->cn_ID) { case ID_NAME: { UBYTE name[128]; ReadChunkBytes (iff, name, min(cn->cn_Size, 127)); name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */ SetAttrs (si, SNGA_Title, name, TAG_DONE); break; } case ID_AUTH: { UBYTE name[128]; ReadChunkBytes (iff, name, min(cn->cn_Size, 127)); name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */ SetAttrs (si, SNGA_Author, name, TAG_DONE); break; } case ID_ANNO: { UBYTE name[128]; ReadChunkBytes (iff, name, min(cn->cn_Size, 127)); name[min(cn->cn_Size, 127)] = '\0'; /* Ensure string termination */ SetAttrs (si, SNGA_Description, name, TAG_DONE); break; } case ID_SHDR: if ((err = ReadChunkBytes (iff, &shdr, sizeof (shdr))) == 0) return err; SetAttrs (si, SNGA_GlobalSpeed, shdr.GlobalSpeed, SNGA_GlobalTempo, shdr.GlobalTempo, SNGA_RestartPos, shdr.RestartPos, SNGA_CurrentPatt, shdr.CurrentPatt, SNGA_CurrentPos, shdr.CurrentPos, SNGA_CurrentInst, shdr.CurrentInst, SNGA_DefaultTracks, shdr.DefNumTracks ? shdr.DefNumTracks : shdr.MaxTracks, SNGA_DefaultPattLen, shdr.DefPattLen ? shdr.DefPattLen : DEF_PATTLEN, SNGA_CreationDate, shdr.CreationDate, SNGA_LastChanged, shdr.LastChanged, SNGA_TotalChanges, shdr.TotalChanges, TAG_DONE); break; case ID_SEQN: len = min (cn->cn_Size / 2, MAXPOSITIONS); if (xmSetSongLen (si, len)) { if ((err = ReadChunkBytes (iff, si->Sequence, si->Length * 2)) != si->Length * 2) return err; } else return ERROR_NO_FREE_STORE; break; case ID_FORM: if (cn->cn_Type == ID_PATT) { if (!si->NumPatterns) xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION, (APTR)MSG_READING_PATTS, NULL); if (xmDisplayProgress (si->NumPatterns, shdr.NumPatterns)) return ERROR_BREAK; if (!LoadPattern (si, si->NumPatterns, iff)) return IoErr(); } else if (cn->cn_Type == ID_8SVX) { if (!si->LastInstrument) xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION, (APTR)MSG_READING_INSTS, NULL); if (xmDisplayProgress (si->LastInstrument, shdr.LastInstrument)) return ERROR_BREAK; if (err = Load8SVXInstrument (si, 0, iff, NULL)) return err; } break; default: break; } } } return err; } GLOBALCALL struct Pattern *LoadPattern (struct SongInfo *si, ULONG num, struct IFFHandle *iff) { LONG err, tracksize; struct Pattern *patt = NULL; struct ContextNode *cn; struct PatternHeader phdr; UWORD i; BYTE name[64]; static LONG stopchunks[] = { ID_PATT, ID_NAME, ID_PATT, ID_PHDR, ID_PATT, ID_BODY }; name[0] = '\0'; if (err = StopChunks (iff, stopchunks, 3)) { SetIoErr (err); return NULL; } if (err = StopOnExit (iff, ID_PATT, ID_FORM)) { SetIoErr (err); return NULL; } /* Scan Pattern */ while (1) { if (err = ParseIFF (iff, IFFPARSE_SCAN)) { if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK; break; /* Free resources & exit */ } if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_PATT)) { switch (cn->cn_ID) { case ID_NAME: { ReadChunkBytes (iff, name, min(cn->cn_Size, 63)); name[min(cn->cn_Size, 63)] = '\0'; /* Ensure string termination */ break; } case ID_PHDR: if ((err = ReadChunkBytes (iff, &phdr, sizeof (phdr))) != sizeof(phdr)) { SetIoErr (err); return NULL; } if (!(patt = xmAddPattern (si, PATTA_Lines, phdr.Lines, PATTA_Tracks, phdr.Tracks, PATTA_Num, num, TAG_DONE))) { SetIoErr (ERROR_NO_FREE_STORE); return NULL; } tracksize = phdr.Lines * sizeof (struct Note); break; case ID_BODY: { if (!patt) { SetIoErr (IFFERR_SYNTAX); return NULL; } for (i = 0; i < patt->Tracks; i++) { if ((err = ReadChunkBytes (iff, patt->Notes[i], tracksize)) != tracksize) { SetIoErr (err); return NULL; } } break; } default: break; } } } if (name[0]) xmSetPattern (si, si->NumPatterns - 1, PATTA_Name, name, TAG_DONE); if (!err && !patt) err = ERROR_OBJECT_NOT_FOUND; if (err) SetIoErr (err); return patt; } static HOOKCALL LONG SaveXModule ( REG(d0, BPTR fh), REG(a0, struct SongInfo *si), REG(a1, struct XMHook *saver), REG(a2, ULONG *tags)) { struct IFFHandle *iff; LONG err; if (iff = AllocIFF()) { iff->iff_Stream = (ULONG) fh; InitIFFasDOS (iff); if (!(err = OpenIFF (iff, IFFF_WRITE))) { /* Write XMOD */ if (err = PushChunk (iff, ID_XMOD, ID_FORM, IFFSIZE_UNKNOWN)) goto error; /* Write module NAME */ if (err = WriteStringChunk (iff, FilePart (si->Path), ID_NAME)) goto error; /* Write module ANNO */ if (err = WriteStringChunk (iff, VERS, ID_ANNO)) goto error; /* Write Module Header (MHDR) */ { struct ModuleHeader mhdr; mhdr.XModuleVersion = VERSION; mhdr.XModuleRevision = REVISION; mhdr.NumSongs = 1; mhdr.ActiveSong = 1; mhdr.MasterVolume = 0xFFFF; mhdr.MixingRate = 44100; if (err = PushChunk (iff, ID_XMOD, ID_MHDR, sizeof (mhdr))) goto error; if ((err = WriteChunkBytes (iff, &mhdr, sizeof (mhdr))) != sizeof(mhdr)) goto error; if (err = PopChunk (iff)) goto error; /* Pop MHDR */ } if (err = SaveSong (iff, si)) goto error; err = PopChunk (iff); /* Pop FORM XMOD */ error: CloseIFF (iff); } else err = IFFERR_NOTIFF; FreeIFF (iff); } else err = ERROR_NO_FREE_STORE; return (UWORD) err; } GLOBALCALL LONG SaveSong (struct IFFHandle *iff, struct SongInfo *si) { LONG err; if (err = PushChunk (iff, ID_SONG, ID_FORM, IFFSIZE_UNKNOWN)) return err; /* Write Song Name */ WriteStringChunk (iff, si->Title, ID_NAME); /* Write Author Name */ WriteStringChunk (iff, si->Author, ID_AUTH); /* Write Annotations */ WriteStringChunk (iff, si->Description, ID_ANNO); /* Write Song Header (SHDR) */ { struct SongHeader shdr; if (err = PushChunk (iff, ID_SONG, ID_SHDR, IFFSIZE_UNKNOWN)) return err; /* Set last changed date */ { ULONG dummy; CurrentTime (&si->LastChanged, &dummy); } shdr.Length = si->Length; shdr.MaxTracks = si->MaxTracks; shdr.NumPatterns = si->NumPatterns; shdr.LastInstrument = si->LastInstrument; shdr.GlobalSpeed = si->GlobalSpeed; shdr.GlobalTempo = si->GlobalTempo; shdr.RestartPos = si->RestartPos; shdr.CurrentPatt = si->CurrentPatt; shdr.CurrentLine = si->CurrentLine; shdr.CurrentTrack = si->CurrentTrack; shdr.CurrentPos = si->CurrentPos; shdr.CurrentInst = si->CurrentInst; shdr.DefNumTracks = si->DefNumTracks; shdr.DefPattLen = si->DefPattLen; shdr.TotalChanges = si->TotalChanges + si->Changes; shdr.CreationDate = si->CreationDate; shdr.LastChanged = si->LastChanged; if ((err = WriteChunkBytes (iff, &shdr, sizeof (shdr))) != sizeof (shdr)) return err; if (err = PopChunk (iff)) /* Pop SHDR */ return err; } if (err = SaveSequence (iff, si)) return err; if (err = SavePatterns (iff, si)) return err; if (err = SaveInstruments (iff, si)) return err; err = PopChunk (iff); /* Pop FORM SONG */ return err; } GLOBALCALL LONG SaveSequence (struct IFFHandle *iff, struct SongInfo *si) { LONG err; if (err = PushChunk (iff, 0, ID_SEQN, si->Length * 2)) return err; if ((err = WriteChunkBytes (iff, si->Sequence, si->Length * 2)) != si->Length * 2) return err; err = PopChunk (iff); return err; } GLOBALCALL LONG SavePatterns (struct IFFHandle *iff, struct SongInfo *si) { LONG err; ULONG i; xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION, (APTR)MSG_WRITING_PATTS, NULL); for (i = 0; i < si->NumPatterns; i++) { if (xmDisplayProgress (i, si->NumPatterns)) return ERROR_BREAK; if (si->Patt[i]) if (err = SavePattern (iff, si->Patt[i])) return err; } return RETURN_OK; } GLOBALCALL LONG SaveInstruments (struct IFFHandle *iff, struct SongInfo *si) { LONG err; ULONG i; xmDisplayMessageA (XMDMF_USECATALOG | XMDMF_ACTION, (APTR)MSG_WRITING_INSTS, NULL); for (i = 1; i <= si->LastInstrument; i++) { if (!(si->Instr[i])) continue; if (xmDisplayProgress (i - 1, si->LastInstrument)) return ERROR_BREAK; if (err = Save8SVXInstrument (si->Instr[i], i, iff)) return err; } return RETURN_OK; } GLOBALCALL LONG SavePattern (struct IFFHandle *iff, struct Pattern *patt) { LONG err; if (err = PushChunk (iff, ID_PATT, ID_FORM, IFFSIZE_UNKNOWN)) return err; /* Write pattern NAME */ if (err = WriteStringChunk (iff, patt->Name, ID_NAME)) return err; /* Write pattern header (PHDR) */ { struct PatternHeader phdr; phdr.Lines = patt->Lines; phdr.Tracks = patt->Tracks; if (err = PushChunk (iff, 0, ID_PHDR, sizeof (struct PatternHeader))) return err; if ((err = WriteChunkBytes (iff, &phdr, sizeof (phdr))) != sizeof (phdr)) return err; if (err = PopChunk (iff)) /* PHDR */ return err; } /* Write pattern BODY */ { ULONG tracksize = sizeof (struct Note) * patt->Lines; ULONG i; if (err = PushChunk (iff, 0, ID_BODY, tracksize * patt->Tracks)) return err; for (i = 0; i < patt->Tracks; i++) { if ((err = WriteChunkBytes (iff, patt->Notes[i], tracksize)) != tracksize) return err; } if (err = PopChunk (iff)) /* BODY */ return err; } err = PopChunk (iff); /* PATT */ return err; } GLOBALCALL LONG WriteStringChunk (struct IFFHandle *iff, CONST_STRPTR str, ULONG id) { LONG err; ULONG len; if (!str || !str[0] || !SaveSwitches.SaveNames) return RETURN_OK; if (err = PushChunk (iff, 0, id, len = strlen (str))) return err; if ((err = WriteChunkBytes (iff, str, len)) != len) return err; err = PopChunk (iff); return err; }