// ==SiteScript==
// @siteName    みなくるビデオ
// @siteUrl     http://video.mina-kuru.jp/
// @author      DarkKnight
// @authorUrl   http://darkknightlabs.com/
// @scriptUrl   http://darkknightlabs.com/site-script/
// @description 
// @date        2008/11/17
// @version     0.1
// ==/SiteScript==


function CravingSiteScript() {
    this._initialize();
}


CravingSiteScript.prototype = {
    _xhr: null,
    
    _initialize: function() {},
    
    _getXmlHttpRequest: function() {
        if ( this._xhr != null ) {
            return this._xhr;
        }
        
        var xhr = null;
        var these = [
              function() { return new XMLHttpRequest(); }
            , function() { return new ActiveXObject( "Msxml2.XMLHTTP" ); }
            , function() { return new ActiveXObject( "Microsoft.XMLHTTP" ); }
            , function() { return new ActiveXObject( "Msxml2.XMLHTTP.4.0" ); }
        ];
        
        for ( var i = 0, length = these.length; i < length; i++ ) {
            var func = these[ i ];
            try {
                xhr = func();
                break;
            }
            catch( e ) {}
        }
        this._xhr = xhr;
        
        return this._xhr;
    },
    
    _load: function( url, data, method ) {
        var req = this._getXmlHttpRequest();
        
        var mtd = ( method == null ) ? "GET" : "POST";
        
        req.open( mtd, url, false );
        
        if ( mtd == "POST" ) {
            req.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
        }
        
        req.send( data );
        
        return req.responseText;
    },
    
    getResponseText: function( url, data, method ) {
        return this._load( url, data, method );
    },
    
    getResponseJSON: function( url, data, method ) {
        var text = this._load( url, data, method );
        
        return eval( "("+text+")" );
    },
    
    /// Math
    random: function( limit ) {
        return Math.floor( Math.random() * limit );
    },
    
    /// String
    decodeHtml: function( str ) {
        return str.replace( /&(quot|#34);/ig,    "\"" )
                  .replace( /&(amp|#38);/ig,     "&"  )
                  .replace( /&(apos|#39);/ig,    "'"  )
                  .replace( /&(lt|#60);/ig,      "<"  )
                  .replace( /&(gt|#62);/ig,      ">"  )
                  .replace( /&(nbsp|#160);/ig,   " "  )
                  .replace( /&(frasl|#8260);/ig, "/"  );
    }
}


function MinaKuru() {
    this._initialize();
}


MinaKuru.prototype = {
    _key: null,
    _Rcon: null,
    _SBox: null,
    _SBoxInverse: null,
    _blockSize: null,
    _keySize: null,
    _NB: null,
    _Nk: null,
    _Nr: null,
    _roundsArray: null,
    _shiftOffsets: null,
    
    _initialize: function() {
        this._key = "key.video.mina-kuru.jp";
        this._Rcon = [ 1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154, 47, 94, 188, 99, 198, 151, 53, 106, 212, 179, 125, 250, 239, 197, 145 ];
        this._SBox = [ 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 ];
        this._SBoxInverse = [ 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125 ];
        this._blockSize = 128;
        this._keySize = 192;
        this._roundsArray = [ 0, 0, 0, 0, [ 0, 0, 0, 0, 10, 0, 12, 0, 14 ], 0, [ 0, 0, 0, 0, 12, 0, 12, 0, 14 ], 0, [ 0, 0, 0, 0, 14, 0, 14, 0, 14 ] ];
        this._shiftOffsets = [ 0, 0, 0, 0, [ 0, 1, 2, 3 ], 0, [ 0, 1, 2, 3 ], 0, [ 0, 1, 3, 4 ] ];
        this._Nb = 4;
        this._Nk = 6;
        this._Nr = this._roundsArray[ this._Nk ][ this._Nb ];
    },
    
    _cyclicShiftLeft: function( src, pos ) {
        var i = src.slice( 0, pos );
        src = src.slice( pos ).concat( i );
        
        return src;
    },
    
    _xtime: function( poly ) {
        poly = poly << 1;
        return poly & 256 ? poly ^ 283 : poly;
    },
    
    _mult_GF256: function( x, y ) {
        var result = 0;
        
        for ( var i = 1; i < 256; i *= 2 ) {
            if ( x & i ) {
                result = result ^ y;
            }
            y = this._xtime( y );
        }
        
        return result;
    },
    
    _byteSub: function( state ) {
        var sbox = this._SBoxInverse;
        
        for ( var i = 0; i < 4; i++ ) {
            for ( var j = 0; j < this._Nb; j++ ) {
                state[ i ][ j ] = sbox[ state[ i ][ j ] ];
            }
        }
    },
    
    _shiftRow: function( state ) {
        for ( var i = 1; i < 4; i++ ) {
            state[ i ] = this._cyclicShiftLeft( state[ i ], this._Nb - this._shiftOffsets[ this._Nb ][ i ] );
        }
    },
    
    _mixColumn: function( state ) {
        var arr = new Array();
        
        for ( var i = 0; i < this._Nb; i++ ) {
            for ( var j = 0; j < 4; j++ ) {
                arr[ j ] = this._mult_GF256( state[   j           ][ i ], 14 )
                         ^ this._mult_GF256( state[ ( j + 1 ) % 4 ][ i ], 11 )
                         ^ this._mult_GF256( state[ ( j + 2 ) % 4 ][ i ], 13 )
                         ^ this._mult_GF256( state[ ( j + 3 ) % 4 ][ i ],  9 );
            }
            
            for ( var j = 0;j < 4; j++ ) {
                state[ j ][ i ] = arr[ j ];
            }
        }
    },
    
    _addRoundKey: function( state, roundKey ) {
        for ( var i = 0; i < this._Nb; i++ ) {
            state[ 0 ][ i ] = state[ 0 ][ i ] ^ ( roundKey[ i ]       & 255 );
            state[ 1 ][ i ] = state[ 1 ][ i ] ^ ( roundKey[ i ] >>  8 & 255 );
            state[ 2 ][ i ] = state[ 2 ][ i ] ^ ( roundKey[ i ] >> 16 & 255 );
            state[ 3 ][ i ] = state[ 3 ][ i ] ^ ( roundKey[ i ] >> 24 & 255 );
        }
    },
    
    _keyExpansion: function( key ) {
        this._Nk = this._keySize   / 32;
        this._Nb = this._blockSize / 32;
        this._Nr = this._roundsArray[ this._Nk ][ this._Nb ];
        var keys = new Array();
        
        for ( var i = 0; i < this._Nk; i++ ) {
            keys[ i ] = key[ 4 * i     ]
                      | key[ 4 * i + 1 ] <<  8
                      | key[ 4 * i + 2 ] << 16
                      | key[ 4 * i + 3 ] << 24;
        }
        
        for ( var i = this._Nk; i < this._Nb * ( this._Nr + 1 ); i++ ) {
            var j = keys[ i - 1 ];
            
            if ( i % this._Nk == 0 ) {
                j = ( this._SBox[ j >>  8 & 255 ]
                    | this._SBox[ j >> 16 & 255 ] <<  8
                    | this._SBox[ j >> 24 & 255 ] << 16
                    | this._SBox[ j       & 255 ] << 24 ) ^ this._Rcon[ Math.floor( i / this._Nk ) - 1 ];
            }
            else {
                if ( this._Nk > 6 && i % this._Nk == 4 ) {
                    j = this._SBox[ j >> 24 & 255 ] << 24
                      | this._SBox[ j >> 16 & 255 ] << 16
                      | this._SBox[ j >>  8 & 255 ] <<  8
                      | this._SBox[ j       & 255 ];
                }
            }
            
            keys[ i ] = keys[ i - this._Nk ] ^ j;
        }
        
        return keys;
    },
    
    _InverseRound: function (state, roundKey) {
        this._addRoundKey( state, roundKey );
        this._mixColumn( state );
        this._shiftRow( state );
        this._byteSub( state );
    },
    
    _InverseFinalRound: function( state, roundKey ) {
        this._addRoundKey( state, roundKey );
        this._shiftRow( state );
        this._byteSub( state );
    },
    
    _decryption: function( block, expandedKey ) {
        block = this._packBytes( block );
        this._InverseFinalRound( block, expandedKey.slice( this._Nb * this._Nr ) );
        
        var i = this._Nr - 1;
        for (; i > 0; i-- ) {
            this._InverseRound( block, expandedKey.slice( this._Nb * i, this._Nb * ( i + 1 ) ) );
        }
        this._addRoundKey( block, expandedKey );
        
        return this._unpackBytes( block );
    },
    
    _packBytes: function( octets ) {
        var bytes  = new Array();
        bytes[ 0 ] = new Array();
        bytes[ 1 ] = new Array();
        bytes[ 2 ] = new Array();
        bytes[ 3 ] = new Array();
        
        for ( var i = 0; i < octets.length; i += 4 ) {
            bytes[ 0 ][ i / 4 ] = octets[ i     ];
            bytes[ 1 ][ i / 4 ] = octets[ i + 1 ];
            bytes[ 2 ][ i / 4 ] = octets[ i + 2 ];
            bytes[ 3 ][ i / 4 ] = octets[ i + 3 ];
        }
        
        return bytes;
    },
    
    _unpackBytes: function( packed ) {
        var bytes = new Array();
        
        for ( var i = 0; i < packed[ 0 ].length; i++ ) {
            bytes[ bytes.length ] = packed[ 0 ][ i ];
            bytes[ bytes.length ] = packed[ 1 ][ i ];
            bytes[ bytes.length ] = packed[ 2 ][ i ];
            bytes[ bytes.length ] = packed[ 3 ][ i ];
        }
        
        return bytes;
    },
    
    _hexToChars: function( hex ) {
        var chars = new Array();
        var char = hex.substr( 0, 2 ) == "0x" ? 2 : 0;
        
        while ( char < hex.length ) {
            chars.push( parseInt( hex.substr( char, 2 ), 16 ) );
            char += 2;
        }
        return chars;
    },
    
    _strToChars: function( str ) {
        var chars = new Array();
        
        for ( var i = 0; i < str.length; i++ ) {
            chars.push( str.charCodeAt( i ) );
        }
        
        return chars;
    },
    
    _charsToStr: function( chars ) {
        var str = new String( "" );
        
        for ( var i = 0; i < chars.length; i++ ) {
            str = str + String.fromCharCode( chars[ i ] );
        }
        
        return str;
    },
    
    decrypt: function( src ) {
        var result = new Array();
        var bytes  = new Array();
        var chars = this._hexToChars( src );
        var blockSize = this._blockSize / 8;
        var key = this._keyExpansion( this._strToChars( this._key ) );
        var i = chars.length / blockSize - 1;
        
        for ( ; i > 0; i-- ) {
            bytes = this._decryption( chars.slice( i * blockSize, ( i + 1 ) * blockSize ), key );
            result = bytes.concat( result );
        }
        
        result = this._decryption( chars.slice( 0, blockSize ), key ).concat( result );
        
        return this._charsToStr( result );
    }
}


function isSiteUrl( url ) {
    if ( url.match( /http:\/\/video\.mina-kuru\.jp\/soft_detail\.html\?softsq=\d+/ ) ) {
        return true;
    }
}


function getVideoDetail( url ) {
    var craving = new CravingSiteScript();
    var text = craving.getResponseText( url );
    
    if ( text == null ) {
        return null;
    }
    
    text.match( /<h4 class="conTitle">\r\n\s+(.*?)\s+<\/h4>/ );
    var title = RegExp.$1;
    
    text.match( /httpObj\.open\("GET","(.*?)",false\)/ );
    var xmlUrl = RegExp.$1;
    
    text = craving.getResponseText( xmlUrl );
    
    if ( text == null ) {
        return null;
    }
    
    var realUrl = "http://flv1.mina-kuru.jp/media1";
    var mina = new MinaKuru();
    realUrl += mina.decrypt( text );
    
    return { videoTitle0: title, videoUrl0: realUrl };
}
