示例#1
0
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Syntax: {0} <fsb file> [<output file>]", typeof(Program).Assembly.Location);
                Console.WriteLine("NOTE: If output file is not given, it will overwrite the given FSB file.");
                return;
            }

            string outputFile = args[args.Length == 2 ? 1 : 0];

            var fsb5Header = new FSB5Header();
            var mpegData = new List<byte>();
            byte[] shdrData;
            string name = "";
            using (var br = new BinaryReader(File.OpenRead(args[0])))
            {
                int sign = CheckSignEndian(br);
                if (sign < 0)
                {
                    Console.WriteLine("Invalid file detected.");
                    return;
                }
                if (sign != '5')
                {
                    Console.WriteLine("This tool is designed for FSB5 files only.");
                    return;
                }

                int fhSize = ReadFSB5Header(br, fsb5Header);

                if (fsb5Header.numSamples != 1)
                {
                    Console.WriteLine("ERROR: Currently this tool only supports FSB5 files that contain a single file within them.");
                    return;
                }
                if (fsb5Header.mode != 0x0B)
                {
                    Console.WriteLine("ERROR: This tool is meant to process MP3-based FSB5s only.");
                    return;
                }

                uint nameOffset = (uint)(fhSize + fsb5Header.shdrSize);
                uint fileOffset = (uint)(nameOffset + fsb5Header.nameSize);
                uint baseOffset = fileOffset;

                uint offset = fr32(br);

                uint type = offset & 0x7F;
                shdrData = BitConverter.GetBytes(type);
                shdrData = shdrData.Concat(br.ReadBytes(4)).ToArray();
                offset = GET_FSB5_OFFSET(offset); // This is the offset into the file section

                long currOffset;
                while ((type & 1) == 1)
                {
                    uint t32 = fr32(br);
                    shdrData = shdrData.Concat(BitConverter.GetBytes(t32)).ToArray();
                    type = t32 & 1;
                    int len = (int)((t32 & 0xFFFFFF) >> 1);
                    t32 >>= 24;
                    currOffset = br.BaseStream.Position;
                    shdrData = shdrData.Concat(br.ReadBytes(len)).ToArray();
                    currOffset += len;
                    br.BaseStream.Position = currOffset;
                }

                currOffset = br.BaseStream.Position;
                uint size;
                if (br.BaseStream.Position < nameOffset)
                {
                    size = fr32(br);
                    if (size == 0)
                        size = (uint)br.BaseStream.Length;
                    else
                        size = GET_FSB5_OFFSET(size) + baseOffset;
                }
                else
                    size = (uint)br.BaseStream.Length;
                br.BaseStream.Position = currOffset;
                fileOffset = baseOffset + offset;
                size -= fileOffset;

                if (fsb5Header.nameSize != 0)
                {
                    currOffset = br.BaseStream.Position;
                    br.BaseStream.Position = nameOffset/* + i * 4*/;
                    br.BaseStream.Position = nameOffset + fr32(br);
                    do
                    {
                        byte c = br.ReadByte();
                        if (c == 0)
                            break;
                        name += (char)c;
                    } while (true);
                    br.BaseStream.Position = currOffset;
                }

                br.BaseStream.Position = currOffset = fileOffset;

                // Get MPEG data and remove padding
                long endOffset = fileOffset + size;
                while (currOffset < endOffset)
                {
                    var header = br.ReadBytes(4);

                    var mpegVersion = (mpg123_version)(3 - ((header[1] >> 3) & 0x03));
                    int layer = 4 - ((header[1] >> 1) & 0x03);
                    int bitrateIndex = (header[2] >> 4) & 0x0F;
                    int sampleRateIndex = (header[2] >> 2) & 0x03;
                    int padding = (header[2] >> 1) & 0x01;
                    int bitrate = getMPEGBitrate(mpegVersion, layer, bitrateIndex);
                    int sampleRate = getMPEGSampleRate((int)mpegVersion, sampleRateIndex);
                    int frameLengthInBytes = getMPEGFrameRateInBytes(layer, bitrate, sampleRate, padding);

                    mpegData.AddRange(header);
                    mpegData.AddRange(br.ReadBytes(frameLengthInBytes - 4));

                    // Check if the next 2 bytes would be an MPEG header, if not, skip to the next 4-byte offset as well as skip any other nul bytes we encounter after that
                    if (br.BaseStream.Position < endOffset && br.PeekByte() != 0xFF && (br.PeekByte(1) & 0xF0) != 0xF0)
                    {
                        br.BaseStream.Position += GetNextMultipleOf4(frameLengthInBytes) - frameLengthInBytes;
                        if (br.BaseStream.Position < endOffset && br.PeekByte() == 0)
                        {
                            while (br.BaseStream.Position < endOffset && br.ReadByte() == 0)
                                ;
                            if (br.BaseStream.Position != endOffset)
                                --br.BaseStream.Position;
                        }
                    }
                    currOffset = br.BaseStream.Position;
                }
            }

            using (var bw = new BinaryWriter(File.Create(outputFile)))
            {
                bw.Write(Encoding.ASCII.GetBytes("FSB5"));
                bw.Write(fsb5Header.version);
                bw.Write(1);
                bw.Write(shdrData.Length);
                int fullNameSize = (int)Math.Ceiling((name.Length + 5) / 16.0) * 16;
                bw.Write(fullNameSize);
                bw.Write(mpegData.Count);
                bw.Write(fsb5Header.mode);
                if (fsb5Header.version == 0)
                    bw.Write(fsb5Header.extra);
                bw.Write(fsb5Header.zero);
                bw.Write(fsb5Header.hash);
                bw.Write(fsb5Header.dummy);
                bw.Write(shdrData);
                bw.Write(4);
                bw.Write(Encoding.ASCII.GetBytes(name));
                bw.Write((byte)0);
                for (int j = name.Length + 5; j < fullNameSize; ++j)
                    bw.Write((byte)0);
                bw.Write(mpegData.ToArray());
            }

            if (args.Length == 1)
                Console.WriteLine("Overwrote {0} with un-padded MPEG FSB5.", outputFile);
            else
                Console.WriteLine("Wrote un-padded MPEG FSB5 to {0}.", outputFile);
        }
示例#2
0
        static int ReadFSB5Header(BinaryReader br, FSB5Header fsb5Header)
        {
            long oldPosition = br.BaseStream.Position;

            fsb5Header.id = br.ReadBytes(4);
            fsb5Header.version = (int)fr32(br);
            fsb5Header.numSamples = (int)fr32(br);
            fsb5Header.shdrSize = (int)fr32(br);
            fsb5Header.nameSize = (int)fr32(br);
            fsb5Header.dataSize = (int)fr32(br);
            fsb5Header.mode = fr32(br);
            if (fsb5Header.version == 0)
                fsb5Header.extra = br.ReadBytes(4);
            else
                fsb5Header.extra = null;
            fsb5Header.zero = br.ReadBytes(8);
            fsb5Header.hash = br.ReadBytes(16);
            fsb5Header.dummy = br.ReadBytes(8);

            return (int)(br.BaseStream.Position - oldPosition);
        }
示例#3
0
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Syntax: {0} <fsb file> [<output directory>]", typeof(Program).Assembly.Location);
                Console.WriteLine("NOTE: If the output directory is not given, the new FSB files will be written");
                Console.WriteLine("      to the same directory as the given FSB file.");
                return;
            }

            string outputDirectory;
            if (args.Length == 2)
                outputDirectory = args[1];
            else
            {
                outputDirectory = Path.ChangeExtension(args[0], "");
                outputDirectory = outputDirectory.Remove(outputDirectory.Length - 1);
            }

            if (!Directory.Exists(outputDirectory))
                Directory.CreateDirectory(outputDirectory);

            using (var br = new BinaryReader(File.OpenRead(args[0])))
            {
                int sign = CheckSignEndian(br);
                if (sign < 0)
                {
                    Console.WriteLine("Invalid file detected.");
                    return;
                }
                if (sign != '5')
                {
                    Console.WriteLine("This tool is designed for FSB5 files only.");
                    return;
                }

                var fsb5Header = new FSB5Header();
                int fhSize = ReadFSB5Header(br, fsb5Header);
                uint nameOffset = (uint)(fhSize + fsb5Header.shdrSize);
                uint fileOffset = (uint)(nameOffset + fsb5Header.nameSize);
                uint baseOffset = fileOffset;

                bool pcmEndian = (fsb5Header.zero[4] & 1) == 1;

                for (int i = 0; i < fsb5Header.numSamples; ++i)
                {
                    uint offset = fr32(br);

                    uint type = offset & 0x7F;
                    var shdrData = BitConverter.GetBytes(type);
                    shdrData = shdrData.Concat(br.ReadBytes(4)).ToArray();
                    offset = GET_FSB5_OFFSET(offset); // This is the offset into the file section

                    long currOffset;
                    while ((type & 1) == 1)
                    {
                        uint t32 = fr32(br);
                        shdrData = shdrData.Concat(BitConverter.GetBytes(t32)).ToArray();
                        type = t32 & 1;
                        int len = (int)((t32 & 0xFFFFFF) >> 1);
                        t32 >>= 24;
                        currOffset = br.BaseStream.Position;
                        shdrData = shdrData.Concat(br.ReadBytes(len)).ToArray();
                        currOffset += len;
                        br.BaseStream.Position = currOffset;
                    }

                    currOffset = br.BaseStream.Position;
                    uint size;
                    if (br.BaseStream.Position < nameOffset)
                    {
                        size = fr32(br);
                        if (size == 0)
                            size = (uint)br.BaseStream.Length;
                        else
                            size = GET_FSB5_OFFSET(size) + baseOffset;
                    }
                    else
                        size = (uint)br.BaseStream.Length;
                    br.BaseStream.Position = currOffset;
                    fileOffset = baseOffset + offset;
                    size -= fileOffset;

                    string name = "";
                    if (fsb5Header.nameSize != 0)
                    {
                        currOffset = br.BaseStream.Position;
                        br.BaseStream.Position = nameOffset + i * 4;
                        br.BaseStream.Position = nameOffset + fr32(br);
                        do
                        {
                            byte c = br.ReadByte();
                            if (c == 0)
                                break;
                            name += (char)c;
                        } while (true);
                        br.BaseStream.Position = currOffset;
                    }

                    Console.Write("Processing {0}...", name);

                    string outputFilename = Path.Combine(outputDirectory, name + ".fsb");

                    currOffset = br.BaseStream.Position;
                    br.BaseStream.Position = fileOffset;
                    // Get file
                    using (var bw = new BinaryWriter(File.Create(outputFilename)))
                    {
                        bw.Write(Encoding.ASCII.GetBytes("FSB5"));
                        bw.Write(fsb5Header.version);
                        bw.Write(1);
                        bw.Write(shdrData.Length);
                        int fullNameSize = (int)Math.Ceiling((name.Length + 5) / 16.0) * 16;
                        bw.Write(fullNameSize);
                        bw.Write(size);
                        bw.Write(fsb5Header.mode);
                        if (fsb5Header.version == 0)
                            bw.Write(fsb5Header.extra);
                        bw.Write(fsb5Header.zero);
                        bw.Write(fsb5Header.hash);
                        bw.Write(fsb5Header.dummy);
                        bw.Write(shdrData);
                        bw.Write(4);
                        bw.Write(Encoding.ASCII.GetBytes(name));
                        bw.Write((byte)0);
                        for (int j = name.Length + 5; j < fullNameSize; ++j)
                            bw.Write((byte)0);
                        br.BaseStream.CopyStream(bw.BaseStream, (int)size);
                    }
                    fileOffset += size;
                    br.BaseStream.Position = currOffset;

            #if FMOD
                    // Also use the FMOD API to output to a WAV file
                    FMOD.System system;
                    var result = FMOD.Factory.System_Create(out system);

                    string outputWAV = Path.Combine(outputDirectory, name + ".wav");

                    result = system.setOutput(FMOD.OUTPUTTYPE.WAVWRITER);

                    result = system.init(32, FMOD.INITFLAGS.NORMAL, System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(outputWAV));

                    FMOD.Sound sound;
                    result = system.createSound(args[0], FMOD.MODE._2D | FMOD.MODE.LOOP_OFF | FMOD.MODE.CREATESTREAM, out sound);

                    FMOD.Sound subsound;
                    result = sound.getSubSound(i, out subsound);

                    FMOD.Channel channel;
                    result = system.playSound(subsound, null, false, out channel);

                    do
                    {
                        result = system.update();
                        if (channel != null)
                        {
                            bool playing;
                            result = channel.isPlaying(out playing);
                            if (!playing)
                                break;
                        }
                    } while (true);

                    result = sound.release();
                    result = system.close();
                    result = system.release();
            #endif

                    Console.WriteLine(" DONE!");
                }
            }
        }