private void set(GapBlockType type) { if (_current == null || _current.Type != type) { _blocks.Add(_current = new GapBlock() { Type = type, Count = 1 }); } else { _current.Count++; } this.Length += BlockSize; }
private long writeGap(ConvertFile file, ref long nullsPos, ref long srcPos, long dstPos, NStream inStream, Stream target, bool firstOrLastFile) { if (file.GapLength == 0) { if (file.FstFile.Length == 0) { nullsPos = dstPos + 0x1c; } return(0); } MemorySection ms = MemorySection.Read(inStream, 4); srcPos += 4; long size = ms.ReadUInt32B(0); GapType gt = (GapType)(size & 0b11); size &= 0xFFFFFFFC; if (size == 0xFFFFFFFC) //for wii only. not a thing for GC { srcPos += 4; inStream.Read(ms.Data, 0, 4); size = 0xFFFFFFFCL + (long)ms.ReadUInt32B(0); //cater for files > 0xFFFFFFFF } long nulls; long junkFileLen = 0; //set nullsPos value if zerobyte file without junk if (gt == GapType.JunkFile) { nullsPos = Math.Min(nullsPos - dstPos, 0); nulls = (size & 0xFC) >> 2; inStream.Read(ms.Data, 0, 4); srcPos += 4; junkFileLen = ms.ReadUInt32B(0); file.FstFile.Length = junkFileLen; junkFileLen += junkFileLen % 4 == 0 ? 0 : 4 - (junkFileLen % 4); ByteStream.Zeros.Copy(target, nulls); inStream.JunkStream.Position = dstPos + nulls; inStream.JunkStream.Copy(target, junkFileLen - nulls); dstPos += junkFileLen; if (file.GapLength <= 8) { return(junkFileLen); } else { //read gap inStream.Read(ms.Data, 0, 4); srcPos += 4; size = ms.ReadUInt32B(0); gt = (GapType)(size & 0b11); size &= 0xFFFFFFFC; } } else if (file.FstFile.Length == 0) //last zero byte file was legit { nullsPos = dstPos + 0x1c; } long maxNulls = Math.Max(0, nullsPos - dstPos); //0x1cL if (size < maxNulls) //need to test this commented if { nulls = size; } else { nulls = size >= 0x40000 && !firstOrLastFile ? 0 : maxNulls; } if (gt == GapType.AllJunk) { ByteStream.Zeros.Copy(target, nulls); inStream.JunkStream.Position = dstPos + nulls; inStream.JunkStream.Copy(target, size - nulls); dstPos += size; } else if (gt == GapType.AllScrubbed) { ByteStream.Zeros.Copy(target, size); dstPos += size; } else { long prg = size; byte btByte = 0x00; GapBlockType bt = GapBlockType.Junk; //should never be used while (prg > 0) { inStream.Read(ms.Data, 0, 4); srcPos += 4; long bytes; long blk = ms.ReadUInt32B(0); GapBlockType btType = (GapBlockType)(blk >> 30); bool btRepeat = btType == GapBlockType.Repeat; if (!btRepeat) { bt = btType; } long cnt = 0x3FFFFFFF & blk; if (bt == GapBlockType.NonJunk) { bytes = Math.Min(cnt * Gap.BlockSize, prg); inStream.Copy(target, bytes); srcPos += bytes; } else if (bt == GapBlockType.ByteFill) { if (!btRepeat) { btByte = (byte)(0xFF & cnt); //last 8 bits when not repeating are the byte cnt >>= 8; } bytes = Math.Min(cnt * Gap.BlockSize, prg); Stream bs; switch (btByte) { case 0x00: bs = ByteStream.Zeros; break; case 0x55: bs = ByteStream.Fives; break; case 0xff: bs = ByteStream.FFs; break; default: bs = new ByteStream(btByte); break; } bs.Copy(target, bytes); } else //if (bt == GapBlockType.Junk) { bytes = Math.Min(cnt * Gap.BlockSize, prg); maxNulls = Math.Max(0, nullsPos - dstPos); //0x1cL if (prg < maxNulls) { nulls = bytes; } else { nulls = bytes >= 0x40000 && !firstOrLastFile ? 0 : maxNulls; } ByteStream.Zeros.Copy(target, nulls); inStream.JunkStream.Position = dstPos + nulls; inStream.JunkStream.Copy(target, bytes - nulls); } prg -= bytes; dstPos += bytes; } } return(size + junkFileLen); }
private long writeGap(ref long fileLength, LongRef gapLength, ref long nullsPos, ref long srcPos, long dstPos, Stream inStream, Stream target, JunkStream junk, bool firstOrLastFile, ScrubManager scrub) { if (gapLength.Value == 0) { if (fileLength == 0) { nullsPos = dstPos + 0x1c; } return(0); } long srcLen = gapLength.Value; //fix added for (padding between junk files) - Zumba Fitness (Europe) (En,Fr,De,Es,It) MemorySection ms = MemorySection.Read(inStream, 4); srcPos += 4; long size = ms.ReadUInt32B(0); GapType gt = (GapType)(size & 0b11); size &= 0xFFFFFFFC; if (size == 0xFFFFFFFC) //for wii only. not a thing for GC { srcPos += 4; inStream.Read(ms.Data, 0, 4); size = 0xFFFFFFFCL + (long)ms.ReadUInt32B(0); //cater for files > 0xFFFFFFFF } gapLength.Value = size; scrub.AddGap(fileLength, dstPos, size); //keep track of trailing nulls when restoring scrubbed images long nulls; long junkFileLen = 0; //set nullsPos value if zerobyte file without junk if (gt == GapType.JunkFile) { nullsPos = Math.Min(nullsPos - dstPos, 0); nulls = (size & 0xFC) >> 2; inStream.Read(ms.Data, 0, 4); srcPos += 4; junkFileLen = ms.ReadUInt32B(0); fileLength = junkFileLen; junkFileLen += junkFileLen % 4 == 0 ? 0 : 4 - (junkFileLen % 4); ByteStream.Zeros.Copy(target, nulls); junk.Position = dstPos + nulls; junk.Copy(target, junkFileLen - nulls); dstPos += junkFileLen; if (srcLen <= 8) { return(junkFileLen); } else { //read gap inStream.Read(ms.Data, 0, 4); srcPos += 4; size = ms.ReadUInt32B(0); gt = (GapType)(size & 0b11); size &= 0xFFFFFFFC; gapLength.Value = size; } } else if (fileLength == 0) //last zero byte file was legit { nullsPos = dstPos + 0x1c; } long maxNulls = Math.Max(0, nullsPos - dstPos); //0x1cL if (size < maxNulls) //need to test this commented if { nulls = size; } else { nulls = size >= 0x40000 && !firstOrLastFile ? 0 : maxNulls; } nullsPos = dstPos + nulls; //belt and braces if (gt == GapType.AllJunk) { ByteStream.Zeros.Copy(target, nulls); junk.Position = dstPos + nulls; junk.Copy(target, size - nulls); dstPos += size; } else if (gt == GapType.AllScrubbed) { scrub.Scrub(target, dstPos, size, 0); dstPos += size; } else { long prg = size; byte btByte = 0x00; GapBlockType bt = GapBlockType.Junk; //should never be used while (prg > 0) { inStream.Read(ms.Data, 0, 4); srcPos += 4; long bytes; long blk = ms.ReadUInt32B(0); GapBlockType btType = (GapBlockType)(blk >> 30); bool btRepeat = btType == GapBlockType.Repeat; if (!btRepeat) { bt = btType; } long cnt = 0x3FFFFFFF & blk; if (bt == GapBlockType.NonJunk) { bytes = Math.Min(cnt * Gap.BlockSize, prg); inStream.Copy(target, bytes); srcPos += bytes; } else if (bt == GapBlockType.ByteFill) { if (!btRepeat) { btByte = (byte)(0xFF & cnt); //last 8 bits when not repeating are the byte cnt >>= 8; } bytes = Math.Min(cnt * Gap.BlockSize, prg); scrub.Scrub(target, dstPos, bytes, btByte); } else //if (bt == GapBlockType.Junk) { bytes = Math.Min(cnt * Gap.BlockSize, prg); maxNulls = Math.Max(0, nullsPos - dstPos); //0x1cL if (prg < maxNulls) { nulls = bytes; } else { nulls = bytes >= 0x40000 && !firstOrLastFile ? 0 : maxNulls; } ByteStream.Zeros.Copy(target, nulls); junk.Position = dstPos + nulls; junk.Copy(target, bytes - nulls); } prg -= bytes; dstPos += bytes; } } return(gapLength.Value + junkFileLen); }
private long write(Stream s, bool writeHeader) { if (_gapLength % 4 != 0) //right most 2 bits will be 00 { throw new Exception("GapLength should be on a 4 byte boundary"); } long written = 0; MemorySection m = new MemorySection(new byte[0x10]); //for junk files % 4 (0 to 3) - load the 4 bytes, in a real iso the last byte would always be null (if not a legit junk file) //if a removed juk file the last byte will end with 2 bits (GapType.JunkFile) the rest of the 4 bytes is the file size / 4 //multiply value by 4 and add the fst file size. if (writeHeader) { if (JunkFile != 0) { m.WriteUInt32B(0, (uint)(JunkFileNulls << 2) | (uint)GapType.JunkFile); //junk file nulls (first 3 bytes are unused) m.WriteUInt32B(4, JunkFile); //length of file s.Write(m.Data, 0, 8); written += 8; } } else if (_blocks.Count == 0) { return(0); } if (_blocks.Count == 0) //non if padding for 32k alignment, or junk file with no padding { Set(); //add a junk item, length will be 0 } GapType gt; if (_blocks.Count == 1 && _blocks[0].Type == GapBlockType.Junk) { gt = GapType.AllJunk; } else if (_blocks.Count == 1 && _blocks[0].Type == GapBlockType.ByteFill && _blocks[0].Byte == 0) //00 scrubbed { gt = GapType.AllScrubbed; } else { gt = GapType.Mixed; } if (writeHeader) { long hdr = _gapLength >= 0xFFFFFFFCL ? 0xFFFFFFFCL : (uint)_gapLength; //always use 4 bytes if the gap is <= 0xFFFFFFFF - it should never be larger than that anyway. if so then use another 4 bytes hdr |= (uint)gt; m.WriteUInt32B(0, (uint)hdr); s.Write(m.Data, 0, 4); written += 4; if (hdr >= 0xFFFFFFFCL && _gapLength >= 0xFFFFFFFC) { m.WriteUInt32B(0, (uint)(_gapLength - 0xFFFFFFFCL)); //will cater for dual layer where most or all of it is empty s.Write(m.Data, 0, 4); written += 4; } } if (gt == GapType.Mixed || !writeHeader) //mixed { foreach (GapBlock b in _blocks) { if (b.NonJunk != null) { b.NonJunk.Position = 0; } uint cnt = (uint)b.Count; //total amount of blocks uint max; uint v = 0; uint w = 0; GapBlockType t = b.Type; while (cnt != 0) { if (t == GapBlockType.Junk || t == GapBlockType.NonJunk || t == GapBlockType.Repeat) { max = 0x3FFFFFFF; if (cnt > max) { v = max; } else { v = cnt; } cnt -= v; w = (uint)t << 30 | v; //type | count of 256 byte blocks } else if (t == GapBlockType.ByteFill) { max = 0x3FFFFF; if (cnt > max) { v = max; } else { v = cnt; } cnt -= v; w = (uint)t << 30 | v << 8 | b.Byte; //type | count of 256 byte blocks | fill byte } m.WriteUInt32B(0, w); s.Write(m.Data, 0, 4); //write encoded bytes written += 4; if (b.Type == GapBlockType.NonJunk) { written += b.NonJunk.Copy(s, Math.Min(b.NonJunk.Length - b.NonJunk.Position, v * Gap.BlockSize)); b.NonJunk.Close(); b.NonJunk = null; //free it } t = GapBlockType.Repeat; } } } _blocks.Clear(); _current = null; return(written); }