public static Grf grf_callback_open(string fname, string mode, GrfOpenCallback callback) { byte[] buf = new byte[GRF_HEADER_FULL_LEN]; uint i;//, zero_fcount = GrfSupport.ToLittleEndian32(7), create_ver = GrfSupport.ToLittleEndian32(0x0200); Grf grf; if (fname == null || mode == null) { throw new Exception("GE_BADARGS"); } /* Allocate memory for grf */ grf = new Grf(); /* Copy the filename */ grf.filename = fname; /* Open the file */ var fStream = FileManager.Load(grf.filename) as Stream; if (fStream == null) { throw new Exception("GE_ERRNO"); } using (var br = new System.IO.BinaryReader(fStream)) { grf.allowWrite = !mode.Contains("+") && mode.Contains("w"); //***skipped write***/ /* Read the header */ buf = br.ReadBytes(GRF_HEADER_FULL_LEN); /* Check the header */ string strA = Encoding.ASCII.GetString(buf); int v = string.Compare(strA, 0, GRF_HEADER, 0, GRF_HEADER_LEN); if (v != 0) { throw new Exception("GE_INVALID"); } /* Continued header check... * * GRF files that allow encryption of the files inside the archive * have a hex header following "Master of Magic" (not including * the nul-terminator) that looks like: * 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E * * GRF files that do not allow it have a hex header that looks like: * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * * GRF files that do not allow it are generally found after a * "Ragnarok.exe /repak" command has been issued */ if (buf[GRF_HEADER_LEN + 1] == 1) { grf.allowCrypt = 1; /* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E */ for (i = 0; i < 0xF; i++) { if (buf[GRF_HEADER_LEN + i] != (int)i) { throw new Exception("GE_CORRUPTED"); } } } else if (buf[GRF_HEADER_LEN] == 0) { grf.allowCrypt = 0; /* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 */ for (i = 0; i < 0xF; i++) { if (buf[GRF_HEADER_LEN + i] != 0) { new Exception("GE_CORRUPTED"); } } } else { throw new Exception("GE_CORRUPTED"); } /* Okay, so we finally are sure that its a valid GRF/GPF file. * now its time to read info from it */ /* Set the type of archive this is */ grf.type = GRF_TYPE_GRF; /* Read the version */ grf.version = GrfSupport.LittleEndian32(buf, GRF_HEADER_MID_LEN + 0xC); /* Read the number of files */ grf.nfiles = GrfSupport.LittleEndian32(buf, GRF_HEADER_MID_LEN + 8); grf.nfiles -= GrfSupport.LittleEndian32(buf, GRF_HEADER_MID_LEN + 4) + 7; /* Create the array of files */ grf.files = new Hashtable(StringComparer.OrdinalIgnoreCase); /* Grab the filesize */ grf.len = (uint)fStream.Length; /* Seek to the offset of the file tables */ br.BaseStream.Seek(GrfSupport.LittleEndian32(buf, GRF_HEADER_MID_LEN) + GRF_HEADER_FULL_LEN, SeekOrigin.Begin); /* Run a different function to read the file information based on * the major version number */ switch (grf.version & 0xFF00) { case 0x0200: i = GRF_readVer2_info(br, grf, callback); break; default: throw new Exception("UNSUP_GRF_VERSION"); } if (i > 0) { return(null); } } return(grf); }
/*! \brief Private function to read GRF0x2xx headers * * Reads the information about files within the archive... * for archive versions 0x02xx * * \todo Find GRF versions other than just 0x200 (do any exist?) * * \param grf Pointer to the Grf struct to read to * \param error Pointer to a GrfErrorType struct/enum for error reporting * \param callback Function to call for each read file. It should return 0 if * everything is fine, 1 if everything is fine (but further * reading should stop), or -1 if there has been an error * \return 0 if everything went fine, 1 if something went wrong */ static uint GRF_readVer2_info(System.IO.BinaryReader br, Grf grf, GrfOpenCallback callback) { uint i, offset, len, len2; byte[] buf, zbuf; /* Check grf */ if (grf.version != 0x200) { throw new Exception("GE_NSUP"); } /* Read the original and compressed sizes */ buf = br.ReadBytes(8); /* Allocate memory and read the compressed file table */ len = GrfSupport.LittleEndian32(buf, 0); zbuf = br.ReadBytes((int)len); if (0 == (len2 = GrfSupport.LittleEndian32(buf, 4))) { return(0); } /* Allocate memory and uncompress the compressed file table */ Array.Resize(ref buf, (int)len2); var stream = new InflaterInputStream(new MemoryStream(zbuf)); stream.Read(buf, 0, buf.Length); stream.Close(); /* Read information about each file */ for (i = offset = 0; i < grf.nfiles; i++) { /* Grab the filename length */ len = GrfSupport.getCStringLength(buf, offset) + 1; /* Make sure its not too large */ if (len >= GrfTypes.GRF_NAMELEN) { throw new Exception("GE_CORRUPTED"); } /* Grab filename */ GrfFile file = new GrfFile(); file.name = GrfSupport.getCString(buf, offset); offset += len; /* Grab the rest of the information */ file.compressed_len = GrfSupport.LittleEndian32(buf, (int)offset); file.compressed_len_aligned = GrfSupport.LittleEndian32(buf, (int)offset + 4); file.real_len = GrfSupport.LittleEndian32(buf, (int)offset + 8); file.flags = buf[offset + 0xC]; file.pos = GrfSupport.LittleEndian32(buf, (int)(offset + 0xD)) + (uint)GRF_HEADER_FULL_LEN; file.hash = GrfSupport.GRF_NameHash(file.name); file.name = NormalizePath(file.name); grf.files.Add(file.name, file); /* Advance to the next file */ offset += 0x11; /* Run the callback, if we have one */ if (callback != null) { callback.doCallback(file); if (!callback.hasReturned()) { throw new Exception("NO_RETURN"); } if (callback.Response < 0) { /* Callback function had an error, so we * have an error */ return(1); } else if (callback.Response > 0) { /* Callback function found the file it needed, * just exit now */ return(0); } } } /* Calling functions will set success... * GRF_SETERR(error,GE_SUCCESS,GRF_readVer2_info); */ return(0); }