<commit_before>// -------------------------------------------------------------------------------------
//         Author: Sourabh S Joshi (cbrghostrider); Copyright - All rights reserved.
//                       For email, run on linux (perl v5.8.5):
//   perl -e 'print pack "H*","736f75726162682e732e6a6f73686940676d61696c2e636f6d0a"'
// -------------------------------------------------------------------------------------
//
// This problem is driving me NUTS!
// I'm trying to solve this in O(n), and currently passing 9/17, the rest are WA.
//
// Here is the main issue with the problem:
//
// Initially the problem seems to be analogous to the merge step of merge sort,
// sadly that is not the case. This is because the case where the two strings have
// equal chars, we need to look ahead (possibly quite a bit) before picking the right one
//
// e.g. (AAABBBBBC) and (AAABBBBBD) in this case, we need to look ahead until we notice a difference (c vs d)
// Unfortunately this makes the algorithm O(n^2)
//
// My Solution in O(n):
// Assume s1p points to current location in str1, and s2p for str2.
//
// This solution exploits what I call *speculative execution in software*.
// e.g. in the above case, we will speculatively pick str1, and go ahead with the merging.
// (we will also note down some bookkeeping information, e.g. the positions where we first started
// our speculation as s1eqint and s2eqind. these are the indices of the first A in both strings)
//
// When we finally reach C vs D, we can check whether our speculation was correct. If so, we keep going
// as if nothing happened. If it was wrong, we can *switch* pointers to make s1p = s1eqind, and s2p = s2eqind + deltaTraveled
// This allows the whole algorithm to now finish in O(n)
//
// Of course, this was easier said than done. In particular, I found the details of the speculation very tricky to implement.
// I have provided comments in the code below, to show the various cases, e.g.
            //      |
            // ->CABCCCC
            // ->CABAAAA
            //   |  *
// Here The starting Cs of both are s1eqind and s2eqind (pointed by ->)
// s2p remained at starting C, while s1p went ahead executing speculatively (denoted by |)
// In the above example, we noticed later on, that our speculation was wrong, so we will
// switch s1p to point to starting C, and s2p to point to the A (marked by *)
// Hope that makes sense.
//
// Unfortunately there is some bug somewhere, which causes 1 test pair from each of the tests from 10 onwards to fail.
// If you can get through the convoluted code below, and spot and fix the bug, PLEASE shoot me an email! :-)
//

#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <algorithm>
#include <assert.h>

using std::string;
using std::vector;

//s1p is the same as s2p
// how does s1p compare against s2future?
void processStateEqual(string& ret, const string& str1, const string& str2, int& s1p, int& s2p, int& s1eqind, int& s2eqind, bool& stateequal) {
    int delta = s1p - s1eqind;
    int s2future = s2p + delta;
    if (s2future < str2.length()) {
        if (str1[s1p] > str2[s2future]) { //made a bad decision earlier
            //    |
            // CABCCCC
            // CABAAAA
            // |
            s2p = s2future;
            s1p = s1eqind;
            stateequal = false;
        } else if (str1[s1p] < str2[s2future]) { //previous call was correct
            //    |
            // CABCCCC
            // CABDDDD
            // |
            stateequal = false;
        } else { //same
            //    |
            // CABCCCC
            // CABCCCC
            // |
            ret.push_back(str1[s1p++]); //choose str1 speculatively
        }
    }  else {
        //    |
        // CABCCCC
        // CAB
        // |
        stateequal = false;
    }
}

// s1p is lesser than s2p
// how does s1p compare to s2future?
void processStateLesser(string& ret, const string& str1, const string& str2, int& s1p, int& s2p, int& s1eqind, int& s2eqind, bool& stateequal) {
    int delta = s1p - s1eqind;
    int s2future = s2p + delta;
    if (s2future < str2.length()) {
        if (str1[s1p] > str2[s2future]) { //made a bad decision earlier
            //    |
            // CABBBBB
            // CABABBB
            // |
            s2p = s2future;
            s1p = s1eqind;
            stateequal = false;
        } else if (str1[s1p] < str2[s2future]) { //previous call was correct
            //    |
            // CABACCC
            // CABDDDD
            // |
            stateequal = false;
        } else { //same
            //    |
            // CABACCC
            // CABADDD
            // |
            ret.push_back(str1[s1p++]);
        }
    } else {
        //    |
        // CABACCC
        // CAB
        // |
        stateequal = false;
    }
}

// s1p is greater than s2p
// how does s2p compare to s2future?
void processStateGreater(string& ret, const string& str1, const string& str2, int& s1p, int& s2p, int& s1eqind, int& s2eqind, bool& stateequal) {
    int delta = s1p - s1eqind;
    int s2future = s2p + delta;
    if (s2future < str2.length()) {
        if (str1[s1p] < str2[s2future]) { //previous call was correct
            //    |
            // CABFCCC
            // CABGDDD
            // |
            stateequal = false;
        } else if (str1[s1p] > str2[s2future]) {
            //    |
            // CABFCCC
            // CABEDDD
            // |
            s2p = s2future;
            s1p = s1eqind;
            stateequal = false;
        } else {
            //    |
            // CABFCCC
            // CABFDDD
            // |
            stateequal = false; //PROGRAMMER BEWARE! Deceptive.
        }
    } else {
        //    |
        // CABFCCC
        // CAB
        // |
        //s2p = s2future;
        //s1p = s1eqind;
        stateequal = false;
    }
}

void processStrings(const string& str1, const string& str2) {
    string ret;
    int s1p=0, s2p=0;
    int s1eqind = -1, s2eqind = -1;

    //stateequal denotes the following invariant
    //  Everything from [s1eqind, s1p] is the same as everything from [s2eqind, s2eqind + (s1eqind - s1p)]
    bool stateequal = false;
    while (s1p < str1.length() && s2p < str2.length()) {
        if (stateequal) {
            int len = s1p - s1eqind;
            if (s1p < str1.length() && (s2p + len) < str2.length()) {
                assert(str1.substr(s1eqind, len) == str2.substr(s2eqind, len));
            }
        }
        if (str1[s1p] < str2[s2p]){
            if (stateequal) {
                processStateLesser(ret, str1, str2, s1p, s2p, s1eqind, s2eqind, stateequal);
            } else {
                ret.push_back(str1[s1p++]);
            }
        }else if (str1[s1p] > str2[s2p]){
            if (stateequal) {
                processStateGreater(ret, str1, str2, s1p, s2p, s1eqind, s2eqind, stateequal);
            } else {
                ret.push_back(str2[s2p++]);
            }
        } else {
            if (stateequal == false) {
                stateequal = true;
                s1eqind = s1p;
                s2eqind = s2p;
                ret.push_back(str1[s1p++]); //choose str1 speculatively
            } else {
                processStateEqual(ret, str1, str2, s1p, s2p, s1eqind, s2eqind, stateequal);
            }
        }
        if (stateequal && s1p == str1.length()) { //and we are in equal state, swap
            int delta = s1p - s1eqind;
            int s2pfuture = s2p + delta;
            s2p = s2pfuture;
            s1p = s1eqind;
            stateequal = false;
        }
    }

    if (s1p < str1.length()) {
        ret += str1.substr(s1p);
    } else if (s2p < str2.length()) {
        ret += str2.substr(s2p);
    }

    std::cout << ret << std::endl;;
}

int main() {
    int tc; std::cin >> tc;
    for (int i=0; i<tc; i++) {
        string str1, str2;
        std::cin >> str1;
        std::cin >> str2;
        processStrings(str1, str2);
    }
}

<commit_msg>Fixed the cryptic bug in this!<commit_after>// -------------------------------------------------------------------------------------
//         Author: Sourabh S Joshi (cbrghostrider); Copyright - All rights reserved.
//                       For email, run on linux (perl v5.8.5):
//   perl -e 'print pack "H*","736f75726162682e732e6a6f73686940676d61696c2e636f6d0a"'
// -------------------------------------------------------------------------------------
//
// This problem is driving me NUTS!
// I'm trying to solve this in O(n), and currently passing 9/17, the rest are WA.
// EDIT: Bug fixed. All tests pass within the 2 sec time limit now.
//
// Here is the main issue with the problem:
//
// Initially the problem seems to be analogous to the merge step of merge sort,
// sadly that is not the case. This is because the case where the two strings have
// equal chars, we need to look ahead (possibly quite a bit) before picking the right one
//
// e.g. (AAABBBBBC) and (AAABBBBBD) in this case, we need to look ahead until we notice a difference (c vs d)
// Unfortunately this makes the algorithm O(n^2)
//
// My Solution in O(n):
// Assume s1p points to current location in str1, and s2p for str2.
//
// This solution exploits what I call *speculative execution in software*.
// e.g. in the above case, we will speculatively pick str1, and go ahead with the merging.
// (we will also note down some bookkeeping information, e.g. the positions where we first started
// our speculation as s1eqint and s2eqind. these are the indices of the first A in both strings)
//
// When we finally reach C vs D, we can check whether our speculation was correct. If so, we keep going
// as if nothing happened. If it was wrong, we can *switch* pointers to make s1p = s1eqind, and s2p = s2eqind + deltaTraveled
// This allows the whole algorithm to now finish in O(n)
//
// Of course, this was easier said than done. In particular, I found the details of the speculation very tricky to implement.
// I have provided comments in the code below, to show the various cases, e.g.
            //      |
            // ->CABCCCC
            // ->CABAAAA
            //   |  *
// Here The starting Cs of both are s1eqind and s2eqind (pointed by ->)
// s2p remained at starting C, while s1p went ahead executing speculatively (denoted by |)
// In the above example, we noticed later on, that our speculation was wrong, so we will
// switch s1p to point to starting C, and s2p to point to the A (marked by *)
// Hope that makes sense.
//
// Unfortunately there is some bug somewhere, which causes 1 test pair from each of the tests from 10 onwards to fail.
// If you can get through the convoluted code below, and spot and fix the bug, PLEASE shoot me an email! :-)
//
// EDIT: Fixed the bug. It had to do with the case shown below in processStateEqual
//       Basically, I needed an additional rollback point. Compare the corresponding cases
//       in processStateGreater and processStateLesser, to see why.
//
//       I apologize, the code is really convoluted. I would like to come back and clean it up sometime! ;)
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <algorithm>
#include <assert.h>

using std::string;
using std::vector;

//s1p is the same as s2p
// how does s1p compare against s2future?
void processStateEqual(string& ret, const string& str1, const string& str2, int& s1p, int& s2p, int& s1eqind, int& s2eqind, int& rollbackpoint, bool& stateequal) {
    int delta = s1p - s1eqind;
    int s2future = s2p + delta;
    if (s2future < str2.length()) {
        if (str1[s1p] > str2[s2future]) { //made a bad decision earlier
            //    |
            // CABCCCC
            // CABAAAA
            // |
            s2p = rollbackpoint == -1 ? s2future : s2p + (rollbackpoint- s1eqind);
            if (rollbackpoint != -1) { for (int c=0; c<(s1p-rollbackpoint); c++) ret.pop_back(); }
            s1p = s1eqind;
            stateequal = false; rollbackpoint = -1;
        } else if (str1[s1p] < str2[s2future]) { //previous call was correct
            //    |
            // CABCCCC
            // CABDDDD
            // |
            stateequal = false; rollbackpoint = -1;
        } else { //same
            //    |
            // CABCCCC
            // CABCCCC
            // |
	    //PROGRAMMER BEWARE: This is where the nasty bug was found. Basically compare these else cases in the
	    // processStateLesser and processStateGreater functions. This case is on the fence, and we really need
	    // to go futher down the road to figure out how it proceeds. If speculation fails, I need
	    // to rollback to this point
            if (rollbackpoint == -1) {rollbackpoint = s1p;}
            ret.push_back(str1[s1p++]); //choose str1 speculatively
        }
    }  else {
        //    |
        // CABCCCC
        // CAB
        // |
        stateequal = false; rollbackpoint = -1;
    }
}

// s1p is lesser than s2p
// how does s1p compare to s2future?
void processStateLesser(string& ret, const string& str1, const string& str2, int& s1p, int& s2p, int& s1eqind, int& s2eqind, int& rollbackpoint, bool& stateequal) {
    int delta = s1p - s1eqind;
    int s2future = s2p + delta;
    if (s2future < str2.length()) {
        if (str1[s1p] > str2[s2future]) { //made a bad decision earlier
            //    |
            // CABBBBB
            // CABABBB
            // |
            s2p = rollbackpoint == -1 ? s2future : s2p + (rollbackpoint- s1eqind);
            if (rollbackpoint != -1) { for (int c=0; c<(s1p-rollbackpoint); c++) ret.pop_back(); }
            s1p = s1eqind;
            stateequal = false; rollbackpoint = -1;
        } else if (str1[s1p] < str2[s2future]) { //previous call was correct
            //    |
            // CABACCC
            // CABDDDD
            // |
            stateequal = false; rollbackpoint = -1;
        } else { //same
            //    |
            // CABACCC
            // CABADDD
            // |
            ret.push_back(str1[s1p++]);
        }
    } else {
        //    |
        // CABACCC
        // CAB
        // |
        stateequal = false; rollbackpoint = -1;
    }
}

// s1p is greater than s2p
// how does s2p compare to s2future?
void processStateGreater(string& ret, const string& str1, const string& str2, int& s1p, int& s2p, int& s1eqind, int& s2eqind, int& rollbackpoint, bool& stateequal) {
    int delta = s1p - s1eqind;
    int s2future = s2p + delta;
    if (s2future < str2.length()) {
        if (str1[s1p] < str2[s2future]) { //previous call was correct
            //    |
            // CABFCCC
            // CABGDDD
            // |
            stateequal = false; rollbackpoint = -1;
        } else if (str1[s1p] > str2[s2future]) {
            //    |
            // CABFCCC
            // CABEDDD
            // |
            s2p = rollbackpoint == -1 ? s2future : s2p + (rollbackpoint- s1eqind);
            if (rollbackpoint != -1) { for (int c=0; c<(s1p-rollbackpoint); c++) ret.pop_back(); }
            s1p = s1eqind;
            stateequal = false; rollbackpoint = -1;
        } else {
            //    |
            // CABFCCC
            // CABFDDD
            // |
            stateequal = false; rollbackpoint = -1;
        }
    } else {
        //    |
        // CABFCCC
        // CAB
        // |
        //s2p = s2future;
        //s1p = s1eqind;
        stateequal = false; rollbackpoint = -1;
    }
}

void processStrings(const string& str1, const string& str2) {
    string ret;
    int s1p=0, s2p=0;
    int s1eqind = -1, s2eqind = -1;
    int rollbackpoint=-1;

    //stateequal denotes the following invariant
    //  Everything from [s1eqind, s1p] is the same as everything from [s2eqind, s2eqind + (s1eqind - s1p)]
    bool stateequal = false;
    while (s1p < str1.length() && s2p < str2.length()) {
        if (stateequal) {
            int len = s1p - s1eqind;
            if (s1p < str1.length() && (s2p + len) < str2.length()) {
                assert(str1.substr(s1eqind, len) == str2.substr(s2eqind, len));
            }
        }
        if (str1[s1p] < str2[s2p]){
            if (stateequal) {
                processStateLesser(ret, str1, str2, s1p, s2p, s1eqind, s2eqind, rollbackpoint, stateequal);
            } else {
                ret.push_back(str1[s1p++]);
            }
        }else if (str1[s1p] > str2[s2p]){
            if (stateequal) {
                processStateGreater(ret, str1, str2, s1p, s2p, s1eqind, s2eqind, rollbackpoint, stateequal);
            } else {
                ret.push_back(str2[s2p++]);
            }
        } else {
            if (stateequal == false) {
                stateequal = true;
                s1eqind = s1p;
                s2eqind = s2p;
                ret.push_back(str1[s1p++]); //choose str1 speculatively
            } else {
                processStateEqual(ret, str1, str2, s1p, s2p, s1eqind, s2eqind, rollbackpoint, stateequal);
            }
        }
        if (stateequal && s1p == str1.length()) { //and we are in equal state, swap
            int delta = s1p - s1eqind;
            int s2pfuture = s2p + delta;
            s2p = rollbackpoint == -1 ? s2pfuture : s2p + (rollbackpoint- s1eqind);
            if (rollbackpoint != -1) { for (int c=0; c<(s1p-rollbackpoint); c++) ret.pop_back(); }
            s1p = s1eqind;
            stateequal = false; rollbackpoint = -1;
        }
    }

    if (s1p < str1.length()) {
        ret += str1.substr(s1p);
    } else if (s2p < str2.length()) {
        ret += str2.substr(s2p);
    }

    std::cout << ret << std::endl;;
}

int main() {
    int tc; std::cin >> tc;
    for (int i=0; i<tc; i++) {
        string str1, str2;
        std::cin >> str1;
        std::cin >> str2;
        processStrings(str1, str2);
    }
}

<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2013, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * Redistributions in binary form must reproduce the above
//        copyright notice, this list of conditions and the following
//        disclaimer in the documentation and/or other materials provided with
//        the distribution.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "boost/algorithm/string/predicate.hpp"

#include "Gaffer/Context.h"
#include "Gaffer/StringPlug.h"

#include "GafferScene/Isolate.h"
#include "GafferScene/PathMatcherData.h"

using namespace std;
using namespace IECore;
using namespace Gaffer;
using namespace GafferScene;

IE_CORE_DEFINERUNTIMETYPED( Isolate );

size_t Isolate::g_firstPlugIndex = 0;

Isolate::Isolate( const std::string &name )
	:	FilteredSceneProcessor( name, Filter::EveryMatch )
{
	storeIndexOfNextChild( g_firstPlugIndex );
	addChild( new StringPlug( "from", Plug::In, "/" ) );
	addChild( new BoolPlug( "adjustBounds", Plug::In, false ) );

	// Direct pass-throughs
	outPlug()->transformPlug()->setInput( inPlug()->transformPlug() );
	outPlug()->attributesPlug()->setInput( inPlug()->attributesPlug() );
	outPlug()->objectPlug()->setInput( inPlug()->objectPlug() );
	outPlug()->globalsPlug()->setInput( inPlug()->globalsPlug() );
	outPlug()->setNamesPlug()->setInput( inPlug()->setNamesPlug() );
}

Isolate::~Isolate()
{
}

Gaffer::StringPlug *Isolate::fromPlug()
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

const Gaffer::StringPlug *Isolate::fromPlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

Gaffer::BoolPlug *Isolate::adjustBoundsPlug()
{
	return getChild<BoolPlug>( g_firstPlugIndex + 1 );
}

const Gaffer::BoolPlug *Isolate::adjustBoundsPlug() const
{
	return getChild<BoolPlug>( g_firstPlugIndex + 1 );
}

void Isolate::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outputs ) const
{
	FilteredSceneProcessor::affects( input, outputs );

	const ScenePlug *in = inPlug();
	if( input->parent<ScenePlug>() == in )
	{
		outputs.push_back( outPlug()->getChild<ValuePlug>( input->getName() ) );
	}
	else if( input == filterPlug() || input == fromPlug() )
	{
		outputs.push_back( outPlug()->childNamesPlug() );
		outputs.push_back( outPlug()->setPlug() );
	}
	else if( input == adjustBoundsPlug() )
	{
		outputs.push_back( outPlug()->boundPlug() );
	}
}

bool Isolate::acceptsInput( const Gaffer::Plug *plug, const Gaffer::Plug *inputPlug ) const
{
	if( !FilteredSceneProcessor::acceptsInput( plug, inputPlug ) )
	{
		return false;
	}

	if( plug == filterPlug() )
	{
		if( const Filter *filter = runTimeCast<const Filter>( inputPlug->source<Plug>()->node() ) )
		{
			if(
				filter->sceneAffectsMatch( inPlug(), inPlug()->boundPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->transformPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->attributesPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->objectPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->childNamesPlug() )
			)
			{
				// We make a single call to filterHash() in hashSet(), to account for
				// the fact that the filter is used in remapping sets. This wouldn't
				// work for filter types which actually vary based on data within the
				// scene hierarchy, because then multiple calls would be necessary.
				// We could make more calls here, but that would be expensive.
				/// \todo In an ideal world we'd be able to compute a hash for the
				/// filter across a whole hierarchy.
				return false;
			}
		}
	}

	return true;
}

void Isolate::hashBound( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	if( adjustBoundsPlug()->getValue() && mayPruneChildren( path, filterValue( context ) ) )
	{
		h = hashOfTransformedChildBounds( path, outPlug() );
		return;
	}

	// pass through
	h = inPlug()->boundPlug()->hash();
}

Imath::Box3f Isolate::computeBound( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	if( adjustBoundsPlug()->getValue() && mayPruneChildren( path, filterValue( context ) ) )
	{
		return unionOfTransformedChildBounds( path, outPlug() );
	}

	return inPlug()->boundPlug()->getValue();
}

void Isolate::hashChildNames( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	ContextPtr tmpContext = filterContext( context );
	Context::Scope scopedContext( tmpContext.get() );

	if( mayPruneChildren( path, filterPlug()->getValue() ) )
	{
		// we might be computing new childnames for this level.
		FilteredSceneProcessor::hashChildNames( path, context, parent, h );
		inPlug()->childNamesPlug()->hash( h );
		filterPlug()->hash( h );
	}
	else
	{
		// pass through
		h = inPlug()->childNamesPlug()->hash();
	}
}

IECore::ConstInternedStringVectorDataPtr Isolate::computeChildNames( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	ContextPtr tmpContext = filterContext( context );
	Context::Scope scopedContext( tmpContext.get() );

	if( mayPruneChildren( path, filterPlug()->getValue() ) )
	{
		// we may need to delete one or more of our children
		ConstInternedStringVectorDataPtr inputChildNamesData = inPlug()->childNamesPlug()->getValue();
		const vector<InternedString> &inputChildNames = inputChildNamesData->readable();

		InternedStringVectorDataPtr outputChildNamesData = new InternedStringVectorData;
		vector<InternedString> &outputChildNames = outputChildNamesData->writable();

		ScenePath childPath = path;
		childPath.push_back( InternedString() ); // for the child name
		for( vector<InternedString>::const_iterator it = inputChildNames.begin(), eIt = inputChildNames.end(); it != eIt; it++ )
		{
			childPath[path.size()] = *it;
			tmpContext->set( ScenePlug::scenePathContextName, childPath );
			if( filterPlug()->getValue() != Filter::NoMatch )
			{
				outputChildNames.push_back( *it );
			}
		}

		return outputChildNamesData;
	}
	else
	{
		// pass through
		return inPlug()->childNamesPlug()->getValue();
	}
}

void Isolate::hashSet( const IECore::InternedString &setName, const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	FilteredSceneProcessor::hashSet( setName, context, parent, h );
	inPlug()->setPlug()->hash( h );
	fromPlug()->hash( h );

	// The sets themselves do not depend on the "scene:path"
	// context entry - the whole point is that they're global.
	// However, the PathFilter is dependent on scene:path, so
	// we must remove the path before hashing in the filter in
	// case we're computed from multiple contexts with different
	// paths (from a SetFilter for instance). If we didn't do this,
	// our different hashes would lead to huge numbers of redundant
	// calls to computeSet() and a huge overhead in recomputing
	// the same sets repeatedly.
	//
	// See further comments in FilteredSceneProcessor::affects().
	ContextPtr c = filterContext( context );
	c->remove( ScenePlug::scenePathContextName );
	Context::Scope s( c.get() );
	filterPlug()->hash( h );
}

GafferScene::ConstPathMatcherDataPtr Isolate::computeSet( const IECore::InternedString &setName, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	ConstPathMatcherDataPtr inputSetData = inPlug()->setPlug()->getValue();
	const PathMatcher &inputSet = inputSetData->readable();
	if( inputSet.isEmpty() )
	{
		return inputSetData;
	}

	PathMatcherDataPtr outputSetData = new PathMatcherData;
	PathMatcher &outputSet = outputSetData->writable();

	ContextPtr tmpContext = filterContext( context );
	Context::Scope scopedContext( tmpContext.get() );

	const std::string fromString = fromPlug()->getValue();
	ScenePlug::ScenePath fromPath; ScenePlug::stringToPath( fromString, fromPath );

	for( PathMatcher::RawIterator pIt = inputSet.begin(), peIt = inputSet.end(); pIt != peIt; )
	{
		tmpContext->set( ScenePlug::scenePathContextName, *pIt );
		const int m = filterPlug()->getValue();
		if( m & ( Filter::ExactMatch | Filter::AncestorMatch ) )
		{
			// We want to keep everything below this point, and
			// we can speed things up by not checking the filter
			// for our descendants.
			PathMatcher::RawIterator next = pIt; next.prune(); ++next;
			while( pIt != next )
			{
				if( pIt.exactMatch() )
				{
					outputSet.addPath( *pIt );
				}
				++pIt;
			}
		}
		else if( m & Filter::DescendantMatch )
		{
			// We might be removing things below here,
			// so just continue our iteration normally
			// so we can find out.
			if( pIt.exactMatch() )
			{
				outputSet.addPath( *pIt );
			}
			++pIt;
		}
		else
		{
			assert( m == Filter::NoMatch );
			if( boost::starts_with( *pIt, fromPath ) )
			{
				// Not going to keep anything below
				// here, so we can prune traversal
				// entirely.
				pIt.prune();
			}
			else if( pIt.exactMatch() )
			{
				outputSet.addPath( *pIt );
			}
			++pIt;
		}
	}

	return outputSetData;
}

bool Isolate::mayPruneChildren( const ScenePath &path, unsigned filterValue ) const
{
	const std::string fromString = fromPlug()->getValue();
	ScenePlug::ScenePath fromPath; ScenePlug::stringToPath( fromString, fromPath );
	if( !boost::starts_with( path, fromPath ) )
	{
		return false;
	}

	return filterValue == Filter::DescendantMatch || filterValue == Filter::NoMatch;
}
<commit_msg>Isolate : Take advantage of PathMatcher node sharing.<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2013, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//      * Redistributions of source code must retain the above
//        copyright notice, this list of conditions and the following
//        disclaimer.
//
//      * Redistributions in binary form must reproduce the above
//        copyright notice, this list of conditions and the following
//        disclaimer in the documentation and/or other materials provided with
//        the distribution.
//
//      * Neither the name of John Haddon nor the names of
//        any other contributors to this software may be used to endorse or
//        promote products derived from this software without specific prior
//        written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "boost/algorithm/string/predicate.hpp"

#include "Gaffer/Context.h"
#include "Gaffer/StringPlug.h"

#include "GafferScene/Isolate.h"
#include "GafferScene/PathMatcherData.h"

using namespace std;
using namespace IECore;
using namespace Gaffer;
using namespace GafferScene;

IE_CORE_DEFINERUNTIMETYPED( Isolate );

size_t Isolate::g_firstPlugIndex = 0;

Isolate::Isolate( const std::string &name )
	:	FilteredSceneProcessor( name, Filter::EveryMatch )
{
	storeIndexOfNextChild( g_firstPlugIndex );
	addChild( new StringPlug( "from", Plug::In, "/" ) );
	addChild( new BoolPlug( "adjustBounds", Plug::In, false ) );

	// Direct pass-throughs
	outPlug()->transformPlug()->setInput( inPlug()->transformPlug() );
	outPlug()->attributesPlug()->setInput( inPlug()->attributesPlug() );
	outPlug()->objectPlug()->setInput( inPlug()->objectPlug() );
	outPlug()->globalsPlug()->setInput( inPlug()->globalsPlug() );
	outPlug()->setNamesPlug()->setInput( inPlug()->setNamesPlug() );
}

Isolate::~Isolate()
{
}

Gaffer::StringPlug *Isolate::fromPlug()
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

const Gaffer::StringPlug *Isolate::fromPlug() const
{
	return getChild<StringPlug>( g_firstPlugIndex );
}

Gaffer::BoolPlug *Isolate::adjustBoundsPlug()
{
	return getChild<BoolPlug>( g_firstPlugIndex + 1 );
}

const Gaffer::BoolPlug *Isolate::adjustBoundsPlug() const
{
	return getChild<BoolPlug>( g_firstPlugIndex + 1 );
}

void Isolate::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outputs ) const
{
	FilteredSceneProcessor::affects( input, outputs );

	const ScenePlug *in = inPlug();
	if( input->parent<ScenePlug>() == in )
	{
		outputs.push_back( outPlug()->getChild<ValuePlug>( input->getName() ) );
	}
	else if( input == filterPlug() || input == fromPlug() )
	{
		outputs.push_back( outPlug()->childNamesPlug() );
		outputs.push_back( outPlug()->setPlug() );
	}
	else if( input == adjustBoundsPlug() )
	{
		outputs.push_back( outPlug()->boundPlug() );
	}
}

bool Isolate::acceptsInput( const Gaffer::Plug *plug, const Gaffer::Plug *inputPlug ) const
{
	if( !FilteredSceneProcessor::acceptsInput( plug, inputPlug ) )
	{
		return false;
	}

	if( plug == filterPlug() )
	{
		if( const Filter *filter = runTimeCast<const Filter>( inputPlug->source<Plug>()->node() ) )
		{
			if(
				filter->sceneAffectsMatch( inPlug(), inPlug()->boundPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->transformPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->attributesPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->objectPlug() ) ||
				filter->sceneAffectsMatch( inPlug(), inPlug()->childNamesPlug() )
			)
			{
				// We make a single call to filterHash() in hashSet(), to account for
				// the fact that the filter is used in remapping sets. This wouldn't
				// work for filter types which actually vary based on data within the
				// scene hierarchy, because then multiple calls would be necessary.
				// We could make more calls here, but that would be expensive.
				/// \todo In an ideal world we'd be able to compute a hash for the
				/// filter across a whole hierarchy.
				return false;
			}
		}
	}

	return true;
}

void Isolate::hashBound( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	if( adjustBoundsPlug()->getValue() && mayPruneChildren( path, filterValue( context ) ) )
	{
		h = hashOfTransformedChildBounds( path, outPlug() );
		return;
	}

	// pass through
	h = inPlug()->boundPlug()->hash();
}

Imath::Box3f Isolate::computeBound( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	if( adjustBoundsPlug()->getValue() && mayPruneChildren( path, filterValue( context ) ) )
	{
		return unionOfTransformedChildBounds( path, outPlug() );
	}

	return inPlug()->boundPlug()->getValue();
}

void Isolate::hashChildNames( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	ContextPtr tmpContext = filterContext( context );
	Context::Scope scopedContext( tmpContext.get() );

	if( mayPruneChildren( path, filterPlug()->getValue() ) )
	{
		// we might be computing new childnames for this level.
		FilteredSceneProcessor::hashChildNames( path, context, parent, h );
		inPlug()->childNamesPlug()->hash( h );
		filterPlug()->hash( h );
	}
	else
	{
		// pass through
		h = inPlug()->childNamesPlug()->hash();
	}
}

IECore::ConstInternedStringVectorDataPtr Isolate::computeChildNames( const ScenePath &path, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	ContextPtr tmpContext = filterContext( context );
	Context::Scope scopedContext( tmpContext.get() );

	if( mayPruneChildren( path, filterPlug()->getValue() ) )
	{
		// we may need to delete one or more of our children
		ConstInternedStringVectorDataPtr inputChildNamesData = inPlug()->childNamesPlug()->getValue();
		const vector<InternedString> &inputChildNames = inputChildNamesData->readable();

		InternedStringVectorDataPtr outputChildNamesData = new InternedStringVectorData;
		vector<InternedString> &outputChildNames = outputChildNamesData->writable();

		ScenePath childPath = path;
		childPath.push_back( InternedString() ); // for the child name
		for( vector<InternedString>::const_iterator it = inputChildNames.begin(), eIt = inputChildNames.end(); it != eIt; it++ )
		{
			childPath[path.size()] = *it;
			tmpContext->set( ScenePlug::scenePathContextName, childPath );
			if( filterPlug()->getValue() != Filter::NoMatch )
			{
				outputChildNames.push_back( *it );
			}
		}

		return outputChildNamesData;
	}
	else
	{
		// pass through
		return inPlug()->childNamesPlug()->getValue();
	}
}

void Isolate::hashSet( const IECore::InternedString &setName, const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	FilteredSceneProcessor::hashSet( setName, context, parent, h );
	inPlug()->setPlug()->hash( h );
	fromPlug()->hash( h );

	// The sets themselves do not depend on the "scene:path"
	// context entry - the whole point is that they're global.
	// However, the PathFilter is dependent on scene:path, so
	// we must remove the path before hashing in the filter in
	// case we're computed from multiple contexts with different
	// paths (from a SetFilter for instance). If we didn't do this,
	// our different hashes would lead to huge numbers of redundant
	// calls to computeSet() and a huge overhead in recomputing
	// the same sets repeatedly.
	//
	// See further comments in FilteredSceneProcessor::affects().
	ContextPtr c = filterContext( context );
	c->remove( ScenePlug::scenePathContextName );
	Context::Scope s( c.get() );
	filterPlug()->hash( h );
}

GafferScene::ConstPathMatcherDataPtr Isolate::computeSet( const IECore::InternedString &setName, const Gaffer::Context *context, const ScenePlug *parent ) const
{
	ConstPathMatcherDataPtr inputSetData = inPlug()->setPlug()->getValue();
	const PathMatcher &inputSet = inputSetData->readable();
	if( inputSet.isEmpty() )
	{
		return inputSetData;
	}

	PathMatcherDataPtr outputSetData = inputSetData->copy();
	PathMatcher &outputSet = outputSetData->writable();

	ContextPtr tmpContext = filterContext( context );
	Context::Scope scopedContext( tmpContext.get() );

	const std::string fromString = fromPlug()->getValue();
	ScenePlug::ScenePath fromPath; ScenePlug::stringToPath( fromString, fromPath );

	for( PathMatcher::RawIterator pIt = inputSet.begin(), peIt = inputSet.end(); pIt != peIt; )
	{
		tmpContext->set( ScenePlug::scenePathContextName, *pIt );
		const int m = filterPlug()->getValue();
		if( m & ( Filter::ExactMatch | Filter::AncestorMatch ) )
		{
			// We want to keep everything below this point, so
			// can just prune our iteration.
			pIt.prune();
			++pIt;
		}
		else if( m & Filter::DescendantMatch )
		{
			// We might be removing things below here,
			// so just continue our iteration normally
			// so we can find out.
			++pIt;
		}
		else
		{
			assert( m == Filter::NoMatch );
			if( boost::starts_with( *pIt, fromPath ) )
			{
				// Not going to keep anything below
				// here, so we can prune traversal
				// entirely.
				outputSet.prune( *pIt );
				pIt.prune();
			}
			++pIt;
		}
	}

	return outputSetData;
}

bool Isolate::mayPruneChildren( const ScenePath &path, unsigned filterValue ) const
{
	const std::string fromString = fromPlug()->getValue();
	ScenePlug::ScenePath fromPath; ScenePlug::stringToPath( fromString, fromPath );
	if( !boost::starts_with( path, fromPath ) )
	{
		return false;
	}

	return filterValue == Filter::DescendantMatch || filterValue == Filter::NoMatch;
}
<|endoftext|>


<commit_before>#include <SDL2/SDL.h>

#include "PrjHndl.h"
#include "TxtRead.h"
#include "Resource.h"

#include "compression/KidDec.h"
#include "compression/ReadPlain.h"
#include "FW_KENSC/comper.h"
#include "FW_KENSC/enigma.h"
#include "FW_KENSC/kosinski.h"
#include "FW_KENSC/nemesis.h"
#include "FW_KENSC/saxman.h"

const char* const FILE_MAP_DEFAULT = "MapDefault.bin";

Resource::Resource(void)
{
	strcpy(this->name, "");
	this->offset = 0;
	this->length = 0;
	this->compression = comprType::INVALID;
	this->kosinski_module_size = 0x1000;
}

void Resource::Save(const char* const filename, const char* const dstfilename)
{
	CompressFile(filename, dstfilename);
	remove(filename);
}

long Resource::DecompressToFile(const char* const dstfile)
{
	int decompressed_length;
	switch (this->compression)
	{
		case comprType::NONE:
			decompressed_length = ReadPlain(this->name, dstfile, this->offset, this->length);
			break;
		case comprType::ENIGMA:
			decompressed_length = enigma::decode(this->name, dstfile, this->offset, false);
			break;
		case comprType::KOSINSKI:
			decompressed_length = kosinski::decode(this->name, dstfile, this->offset, false, 16u);
			break;
		case comprType::MODULED_KOSINSKI:
			decompressed_length = kosinski::decode(this->name, dstfile, this->offset, true, 16u);
			break;
		case comprType::NEMESIS:
			decompressed_length = nemesis::decode(this->name, dstfile, this->offset, 0);
			break;
		case comprType::KID_CHAMELEON:
			decompressed_length = KidDec(this->name, dstfile, this->offset);
			break;
		case comprType::COMPER:
			decompressed_length = comper::decode(this->name, dstfile, this->offset);
			break;
		case comprType::SAXMAN:
			decompressed_length = saxman::decode(this->name, dstfile, this->offset, 0);
			break;
	}

	return decompressed_length;
}

void Resource::CompressFile(const char* const srcfile, const char* const dstfile)
{
	switch (this->compression)
	{
		case comprType::NONE:
			remove(dstfile);
			rename(srcfile, dstfile);
			break;
		case comprType::ENIGMA:
			enigma::encode(srcfile, dstfile, false);
			break;
		case comprType::KOSINSKI:
			kosinski::encode(srcfile, dstfile, false, this->kosinski_module_size, 16u);
			break;
		case comprType::MODULED_KOSINSKI:
			kosinski::encode(srcfile, dstfile, true, this->kosinski_module_size, 16u);
			break;
		case comprType::NEMESIS:
			nemesis::encode(srcfile, dstfile);
			break;
		case comprType::COMPER:
			comper::encode(srcfile, dstfile);
			break;
		case comprType::SAXMAN:
			saxman::encode(srcfile, dstfile, false);
			break;
	}
}

ResourceArt::ResourceArt(void)
{
	this->tileAmount = 0;
}

void ResourceArt::Load(const char* const filename)
{
	if (this->compression == comprType::INVALID)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Invalid art compression format. Should be one of the following:\n\n'None'\n'Enigma'\n'Kosinski'\n'Moduled Kosinski'\n'Nemesis'\n'Kid Chameleon'\n'Comper'\n'Saxman'", NULL);
		exit(1);
	}

	int decompressed_length = DecompressToFile(filename);

	if (decompressed_length < 0)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Could not decompress art file. Are you sure the compression is correct?", NULL);
		exit(1);
	}
	this->tileAmount = decompressed_length/0x20;
}

ResourceMap::ResourceMap(void)
{
	this->xSize = 0;
	this->ySize = 0;
	strcpy(this->saveName, "");
}

void ResourceMap::Load(const char* const filename)
{
	if (this->compression == comprType::INVALID || this->compression == comprType::KID_CHAMELEON)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Invalid map compression format. Should be one of the following:\n\n'None'\n'Enigma'\n'Kosinski'\n'Moduled Kosinski'\n'Nemesis'\n'Comper'\n'Saxman'", NULL);
		exit(1);
	}

	int decompressed_length = DecompressToFile(filename);

	if (decompressed_length < 0) {
		//file could not be decompressed or found
		decompressed_length = 2*this->xSize*this->ySize;
		if (!CheckCreateBlankFile(this->name, filename, this->offset, decompressed_length))
		{
			//file is existant but could not be decompressed
			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Could not decompress map file. Are you sure the compression is correct?", NULL);
			exit(1);
		}
		else
		{
			//file non-existant, blank template created
			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Information", "No map file found, created blank template.", NULL);
		}
	}

	if (decompressed_length < 2*this->xSize*this->ySize)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning", "Specified size exceeds map size.\nField has been trimmed vertically.", NULL);
		this->ySize = (decompressed_length/this->xSize) / 2;
		if (this->ySize == 0)
			exit(1);
	}

	if (strlen(this->saveName) == 0)
	{
		if (this->offset == 0)
		{
			strcpy(this->saveName, this->name); //overwrite existing map
		}
		else
		{
			const char* const part_message = "This tool cannot overwrite a ROM. Plane map will be saved to ";
			char* whole_message = new char[strlen(part_message)+strlen(FILE_MAP_DEFAULT)+1];
			sprintf(whole_message, "%s%s", part_message, FILE_MAP_DEFAULT);
			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Information", whole_message, NULL);
			delete[] whole_message;
			strcpy(this->saveName, FILE_MAP_DEFAULT); //write to default file
		}
	}
}

ResourcePal::ResourcePal(void)
{
	// For backwards compatibility, palette is assumed to be uncompressed by default
	this->compression = comprType::NONE;
}

void ResourcePal::Load(const char* const filename)
{
	if (this->compression == comprType::INVALID)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Invalid palette compression format. Should be one of the following:\n\n'None'\n'Enigma'\n'Kosinski'\n'Moduled Kosinski'\n'Nemesis'\n'Kid Chameleon'\n'Comper'\n'Saxman'", NULL);
		exit(1);
	}

	int decompressed_length = DecompressToFile(filename);

	if (decompressed_length < 0)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Could not decompress palette file. Are you sure the compression is correct?", NULL);
		exit(1);
	}
}
<commit_msg>Changed how saveName is deemed unset<commit_after>#include <SDL2/SDL.h>

#include "PrjHndl.h"
#include "TxtRead.h"
#include "Resource.h"

#include "compression/KidDec.h"
#include "compression/ReadPlain.h"
#include "FW_KENSC/comper.h"
#include "FW_KENSC/enigma.h"
#include "FW_KENSC/kosinski.h"
#include "FW_KENSC/nemesis.h"
#include "FW_KENSC/saxman.h"

const char* const FILE_MAP_DEFAULT = "MapDefault.bin";

Resource::Resource(void)
{
	strcpy(this->name, "");
	this->offset = 0;
	this->length = 0;
	this->compression = comprType::INVALID;
	this->kosinski_module_size = 0x1000;
}

void Resource::Save(const char* const filename, const char* const dstfilename)
{
	CompressFile(filename, dstfilename);
	remove(filename);
}

long Resource::DecompressToFile(const char* const dstfile)
{
	int decompressed_length;
	switch (this->compression)
	{
		case comprType::NONE:
			decompressed_length = ReadPlain(this->name, dstfile, this->offset, this->length);
			break;
		case comprType::ENIGMA:
			decompressed_length = enigma::decode(this->name, dstfile, this->offset, false);
			break;
		case comprType::KOSINSKI:
			decompressed_length = kosinski::decode(this->name, dstfile, this->offset, false, 16u);
			break;
		case comprType::MODULED_KOSINSKI:
			decompressed_length = kosinski::decode(this->name, dstfile, this->offset, true, 16u);
			break;
		case comprType::NEMESIS:
			decompressed_length = nemesis::decode(this->name, dstfile, this->offset, 0);
			break;
		case comprType::KID_CHAMELEON:
			decompressed_length = KidDec(this->name, dstfile, this->offset);
			break;
		case comprType::COMPER:
			decompressed_length = comper::decode(this->name, dstfile, this->offset);
			break;
		case comprType::SAXMAN:
			decompressed_length = saxman::decode(this->name, dstfile, this->offset, 0);
			break;
	}

	return decompressed_length;
}

void Resource::CompressFile(const char* const srcfile, const char* const dstfile)
{
	switch (this->compression)
	{
		case comprType::NONE:
			remove(dstfile);
			rename(srcfile, dstfile);
			break;
		case comprType::ENIGMA:
			enigma::encode(srcfile, dstfile, false);
			break;
		case comprType::KOSINSKI:
			kosinski::encode(srcfile, dstfile, false, this->kosinski_module_size, 16u);
			break;
		case comprType::MODULED_KOSINSKI:
			kosinski::encode(srcfile, dstfile, true, this->kosinski_module_size, 16u);
			break;
		case comprType::NEMESIS:
			nemesis::encode(srcfile, dstfile);
			break;
		case comprType::COMPER:
			comper::encode(srcfile, dstfile);
			break;
		case comprType::SAXMAN:
			saxman::encode(srcfile, dstfile, false);
			break;
	}
}

ResourceArt::ResourceArt(void)
{
	this->tileAmount = 0;
}

void ResourceArt::Load(const char* const filename)
{
	if (this->compression == comprType::INVALID)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Invalid art compression format. Should be one of the following:\n\n'None'\n'Enigma'\n'Kosinski'\n'Moduled Kosinski'\n'Nemesis'\n'Kid Chameleon'\n'Comper'\n'Saxman'", NULL);
		exit(1);
	}

	int decompressed_length = DecompressToFile(filename);

	if (decompressed_length < 0)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Could not decompress art file. Are you sure the compression is correct?", NULL);
		exit(1);
	}
	this->tileAmount = decompressed_length/0x20;
}

ResourceMap::ResourceMap(void)
{
	this->xSize = 0;
	this->ySize = 0;
	strcpy(this->saveName, "");
}

void ResourceMap::Load(const char* const filename)
{
	if (this->compression == comprType::INVALID || this->compression == comprType::KID_CHAMELEON)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Invalid map compression format. Should be one of the following:\n\n'None'\n'Enigma'\n'Kosinski'\n'Moduled Kosinski'\n'Nemesis'\n'Comper'\n'Saxman'", NULL);
		exit(1);
	}

	int decompressed_length = DecompressToFile(filename);

	if (decompressed_length < 0) {
		//file could not be decompressed or found
		decompressed_length = 2*this->xSize*this->ySize;
		if (!CheckCreateBlankFile(this->name, filename, this->offset, decompressed_length))
		{
			//file is existant but could not be decompressed
			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Could not decompress map file. Are you sure the compression is correct?", NULL);
			exit(1);
		}
		else
		{
			//file non-existant, blank template created
			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Information", "No map file found, created blank template.", NULL);
		}
	}

	if (decompressed_length < 2*this->xSize*this->ySize)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning", "Specified size exceeds map size.\nField has been trimmed vertically.", NULL);
		this->ySize = (decompressed_length/this->xSize) / 2;
		if (this->ySize == 0)
			exit(1);
	}

	if (strcmp(this->saveName, "") == 0)
	{
		if (this->offset == 0)
		{
			strcpy(this->saveName, this->name); //overwrite existing map
		}
		else
		{
			const char* const part_message = "This tool cannot overwrite a ROM. Plane map will be saved to ";
			char* whole_message = new char[strlen(part_message)+strlen(FILE_MAP_DEFAULT)+1];
			sprintf(whole_message, "%s%s", part_message, FILE_MAP_DEFAULT);
			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Information", whole_message, NULL);
			delete[] whole_message;
			strcpy(this->saveName, FILE_MAP_DEFAULT); //write to default file
		}
	}
}

ResourcePal::ResourcePal(void)
{
	// For backwards compatibility, palette is assumed to be uncompressed by default
	this->compression = comprType::NONE;
}

void ResourcePal::Load(const char* const filename)
{
	if (this->compression == comprType::INVALID)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Invalid palette compression format. Should be one of the following:\n\n'None'\n'Enigma'\n'Kosinski'\n'Moduled Kosinski'\n'Nemesis'\n'Kid Chameleon'\n'Comper'\n'Saxman'", NULL);
		exit(1);
	}

	int decompressed_length = DecompressToFile(filename);

	if (decompressed_length < 0)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Could not decompress palette file. Are you sure the compression is correct?", NULL);
		exit(1);
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Navigates the browser to server and client redirect pages and makes sure
// that the correct redirects are reflected in the history database. Errors
// here might indicate that WebKit changed the calls our glue layer gets in
// the case of redirects. It may also mean problems with the history system.

#include "base/file_util.h"
#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/view_ids.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
#include "views/event.h"

namespace {

class RedirectTest : public UITest {
 public:
  RedirectTest()
      : test_server_(net::TestServer::TYPE_HTTP,
                     FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
  }

 protected:
  net::TestServer test_server_;
};

// Tests a single server redirect
TEST_F(RedirectTest, Server) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec());

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests a single client redirect.
TEST_F(RedirectTest, Client) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + final_url.spec());

  // The client redirect appears as two page visits in the browser.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());

  // The address bar should display the final URL.
  GURL tab_url;
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);

  // Navigate one more time.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  // The address bar should still display the final URL.
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);
}

TEST_F(RedirectTest, ClientEmptyReferer) {
  ASSERT_TRUE(test_server_.Start());

  // Create the file contents, which will do a redirect to the
  // test server.
  GURL final_url = test_server_.GetURL(std::string());
  ASSERT_TRUE(final_url.is_valid());
  std::string file_redirect_contents = StringPrintf(
      ""
      ""
      ""
      "",
      final_url.spec().c_str());

  // Write the contents to a temporary file.
  ScopedTempDir temp_directory;
  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
  FilePath temp_file;
  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(),
                                                  &temp_file));
  ASSERT_EQ(static_cast<int>(file_redirect_contents.size()),
            file_util::WriteFile(temp_file,
                                 file_redirect_contents.data(),
                                 file_redirect_contents.size()));

  // Navigate to the file through the browser. The client redirect will appear
  // as two page visits in the browser.
  GURL first_url = net::FilePathToFileURL(temp_file);
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  std::vector<GURL> redirects;
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests to make sure a location change when a pending redirect exists isn't
// flagged as a redirect.
#if defined(OS_MACOSX)
// SimulateOSClick is broken on the Mac: http://crbug.com/45162
#define MAYBE_ClientCancelled DISABLED_ClientCancelled
#elif defined(OS_WIN)
// http://crbug.com/53091
#define MAYBE_ClientCancelled FAILS_ClientCancelled
#else
#define MAYBE_ClientCancelled ClientCancelled
#endif

TEST_F(RedirectTest, MAYBE_ClientCancelled) {
  FilePath first_path(test_data_directory_);
  first_path = first_path.AppendASCII("cancelled_redirect_test.html");
  ASSERT_TRUE(file_util::AbsolutePath(&first_path));
  GURL first_url = net::FilePathToFileURL(first_path);

  NavigateToURLBlockUntilNavigationsComplete(first_url, 1);

  scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
  ASSERT_TRUE(browser.get());
  scoped_refptr<WindowProxy> window = browser->GetWindow();
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  int64 last_nav_time = 0;
  EXPECT_TRUE(tab_proxy->GetLastNavigationTime(&last_nav_time));
  // Simulate a click to force to make a user-initiated location change;
  // otherwise, a non user-initiated in-page location change will be treated
  // as client redirect and the redirect will be recoreded, which can cause
  // this test failed.
  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(browser->BringToFront());
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER, &tab_view_bounds,
                                    true));
  ASSERT_TRUE(
      window->SimulateOSClick(tab_view_bounds.CenterPoint(),
                              views::Event::EF_LEFT_BUTTON_DOWN));
  EXPECT_TRUE(tab_proxy->WaitForNavigation(last_nav_time));

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  // There should be no redirects from first_url, because the anchor location
  // change that occurs should not be flagged as a redirect and the meta-refresh
  // won't have fired yet.
  ASSERT_EQ(0U, redirects.size());
  GURL current_url;
  ASSERT_TRUE(tab_proxy->GetCurrentURL(&current_url));

  // Need to test final path and ref separately since constructing a file url
  // containing an anchor using FilePathToFileURL will escape the anchor as
  // %23, but in current_url the anchor will be '#'.
  std::string final_ref = "myanchor";
  FilePath current_path;
  ASSERT_TRUE(net::FileURLToFilePath(current_url, &current_path));
  ASSERT_TRUE(file_util::AbsolutePath(&current_path));
  // Path should remain unchanged.
  EXPECT_EQ(StringToLowerASCII(first_path.value()),
            StringToLowerASCII(current_path.value()));
  EXPECT_EQ(final_ref, current_url.ref());
}

// Tests a client->server->server redirect
TEST_F(RedirectTest, ClientServerServer) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL next_to_last = test_server_.GetURL(
      "server-redirect?" + final_url.spec());
  GURL second_url = test_server_.GetURL(
      "server-redirect?" + next_to_last.spec());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + second_url.spec());
  std::vector<GURL> redirects;

  // We need the sleep for the client redirects, because it appears as two
  // page visits in the browser.
  NavigateToURL(first_url);

  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
    if (!redirects.empty())
      break;
  }

  ASSERT_EQ(3U, redirects.size());
  EXPECT_EQ(second_url.spec(), redirects[0].spec());
  EXPECT_EQ(next_to_last.spec(), redirects[1].spec());
  EXPECT_EQ(final_url.spec(), redirects[2].spec());
}

// Tests that the "#reference" gets preserved across server redirects.
TEST_F(RedirectTest, ServerReference) {
  ASSERT_TRUE(test_server_.Start());

  const std::string ref("reference");

  GURL final_url = test_server_.GetURL(std::string());
  GURL initial_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec() + "#" + ref);

  NavigateToURL(initial_url);

  GURL url = GetActiveTabURL();
  EXPECT_EQ(ref, url.ref());
}

// Test that redirect from http:// to file:// :
// A) does not crash the browser or confuse the redirect chain, see bug 1080873
// B) does not take place.
TEST_F(RedirectTest, NoHttpToFile) {
  ASSERT_TRUE(test_server_.Start());
  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("http_to_file.html");
  GURL file_url = net::FilePathToFileURL(test_file);

  GURL initial_url = test_server_.GetURL(
      "client-redirect?" + file_url.spec());

  NavigateToURL(initial_url);
  // UITest will check for crashes. We make sure the title doesn't match the
  // title from the file, because the nav should not have taken place.
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  std::wstring actual_title;
  ASSERT_TRUE(tab_proxy->GetTabTitle(&actual_title));
  EXPECT_NE("File!", WideToUTF8(actual_title));
}

// Ensures that non-user initiated location changes (within page) are
// flagged as client redirects. See bug 1139823.
TEST_F(RedirectTest, ClientFragments) {
  ASSERT_TRUE(test_server_.Start());

  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("ref_redirect.html");
  GURL first_url = net::FilePathToFileURL(test_file);
  std::vector<GURL> redirects;

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  EXPECT_EQ(1U, redirects.size());
  EXPECT_EQ(first_url.spec() + "#myanchor", redirects[0].spec());
}

// TODO(timsteele): This is disabled because our current testserver can't
// handle multiple requests in parallel, making it hang on the first request
// to /slow?60. It's unable to serve our second request for files/title2.html
// until /slow? completes, which doesn't give the desired behavior. We could
// alternatively load the second page from disk, but we would need to start
// the browser for this testcase with --process-per-tab, and I don't think
// we can do this at test-case-level granularity at the moment.
// http://crbug.com/45056
TEST_F(RedirectTest,
       DISABLED_ClientCancelledByNewNavigationAfterProvisionalLoad) {
  // We want to initiate a second navigation after the provisional load for
  // the client redirect destination has started, but before this load is
  // committed. To achieve this, we tell the browser to load a slow page,
  // which causes it to start a provisional load, and while it is waiting
  // for the response (which means it hasn't committed the load for the client
  // redirect destination page yet), we issue a new navigation request.
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL("files/title2.html");
  GURL slow = test_server_.GetURL("slow?60");
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + slow.spec());
  std::vector<GURL> redirects;

  NavigateToURL(first_url);
  // We don't sleep here - the first navigation won't have been committed yet
  // because we told the server to wait a minute. This means the browser has
  // started it's provisional load for the client redirect destination page but
  // hasn't completed. Our time is now!
  NavigateToURL(final_url);

  std::wstring tab_title;
  std::wstring final_url_title = UTF8ToWide("Title Of Awesomeness");
  // Wait till the final page has been loaded.
  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetTabTitle(&tab_title));
    if (tab_title == final_url_title) {
      ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
      break;
    }
  }

  // Check to make sure the navigation did in fact take place and we are
  // at the expected page.
  EXPECT_EQ(final_url_title, tab_title);

  bool final_navigation_not_redirect = true;
  // Check to make sure our request for files/title2.html doesn't get flagged
  // as a client redirect from the first (/client-redirect?) page.
  for (std::vector<GURL>::iterator it = redirects.begin();
       it != redirects.end(); ++it) {
    if (final_url.spec() == it->spec()) {
      final_navigation_not_redirect = false;
      break;
    }
  }
  EXPECT_TRUE(final_navigation_not_redirect);
}

}  // namespace
<commit_msg>Marking the RedirectTest.ClientEmptyReferer as flaky.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Navigates the browser to server and client redirect pages and makes sure
// that the correct redirects are reflected in the history database. Errors
// here might indicate that WebKit changed the calls our glue layer gets in
// the case of redirects. It may also mean problems with the history system.

#include "base/file_util.h"
#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/view_ids.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
#include "views/event.h"

namespace {

class RedirectTest : public UITest {
 public:
  RedirectTest()
      : test_server_(net::TestServer::TYPE_HTTP,
                     FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
  }

 protected:
  net::TestServer test_server_;
};

// Tests a single server redirect
TEST_F(RedirectTest, Server) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec());

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests a single client redirect.
TEST_F(RedirectTest, Client) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + final_url.spec());

  // The client redirect appears as two page visits in the browser.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());

  // The address bar should display the final URL.
  GURL tab_url;
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);

  // Navigate one more time.
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  // The address bar should still display the final URL.
  EXPECT_TRUE(tab_proxy->GetCurrentURL(&tab_url));
  EXPECT_TRUE(final_url == tab_url);
}

// http://code.google.com/p/chromium/issues/detail?id=62772
TEST_F(RedirectTest, FLAKY_ClientEmptyReferer) {
  ASSERT_TRUE(test_server_.Start());

  // Create the file contents, which will do a redirect to the
  // test server.
  GURL final_url = test_server_.GetURL(std::string());
  ASSERT_TRUE(final_url.is_valid());
  std::string file_redirect_contents = StringPrintf(
      ""
      ""
      ""
      "",
      final_url.spec().c_str());

  // Write the contents to a temporary file.
  ScopedTempDir temp_directory;
  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
  FilePath temp_file;
  ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(),
                                                  &temp_file));
  ASSERT_EQ(static_cast<int>(file_redirect_contents.size()),
            file_util::WriteFile(temp_file,
                                 file_redirect_contents.data(),
                                 file_redirect_contents.size()));

  // Navigate to the file through the browser. The client redirect will appear
  // as two page visits in the browser.
  GURL first_url = net::FilePathToFileURL(temp_file);
  NavigateToURLBlockUntilNavigationsComplete(first_url, 2);

  std::vector<GURL> redirects;
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  ASSERT_EQ(1U, redirects.size());
  EXPECT_EQ(final_url.spec(), redirects[0].spec());
}

// Tests to make sure a location change when a pending redirect exists isn't
// flagged as a redirect.
#if defined(OS_MACOSX)
// SimulateOSClick is broken on the Mac: http://crbug.com/45162
#define MAYBE_ClientCancelled DISABLED_ClientCancelled
#elif defined(OS_WIN)
// http://crbug.com/53091
#define MAYBE_ClientCancelled FAILS_ClientCancelled
#else
#define MAYBE_ClientCancelled ClientCancelled
#endif

TEST_F(RedirectTest, MAYBE_ClientCancelled) {
  FilePath first_path(test_data_directory_);
  first_path = first_path.AppendASCII("cancelled_redirect_test.html");
  ASSERT_TRUE(file_util::AbsolutePath(&first_path));
  GURL first_url = net::FilePathToFileURL(first_path);

  NavigateToURLBlockUntilNavigationsComplete(first_url, 1);

  scoped_refptr<BrowserProxy> browser = automation()->GetBrowserWindow(0);
  ASSERT_TRUE(browser.get());
  scoped_refptr<WindowProxy> window = browser->GetWindow();
  ASSERT_TRUE(window.get());
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  int64 last_nav_time = 0;
  EXPECT_TRUE(tab_proxy->GetLastNavigationTime(&last_nav_time));
  // Simulate a click to force to make a user-initiated location change;
  // otherwise, a non user-initiated in-page location change will be treated
  // as client redirect and the redirect will be recoreded, which can cause
  // this test failed.
  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(browser->BringToFront());
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER, &tab_view_bounds,
                                    true));
  ASSERT_TRUE(
      window->SimulateOSClick(tab_view_bounds.CenterPoint(),
                              views::Event::EF_LEFT_BUTTON_DOWN));
  EXPECT_TRUE(tab_proxy->WaitForNavigation(last_nav_time));

  std::vector<GURL> redirects;
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));

  // There should be no redirects from first_url, because the anchor location
  // change that occurs should not be flagged as a redirect and the meta-refresh
  // won't have fired yet.
  ASSERT_EQ(0U, redirects.size());
  GURL current_url;
  ASSERT_TRUE(tab_proxy->GetCurrentURL(&current_url));

  // Need to test final path and ref separately since constructing a file url
  // containing an anchor using FilePathToFileURL will escape the anchor as
  // %23, but in current_url the anchor will be '#'.
  std::string final_ref = "myanchor";
  FilePath current_path;
  ASSERT_TRUE(net::FileURLToFilePath(current_url, &current_path));
  ASSERT_TRUE(file_util::AbsolutePath(&current_path));
  // Path should remain unchanged.
  EXPECT_EQ(StringToLowerASCII(first_path.value()),
            StringToLowerASCII(current_path.value()));
  EXPECT_EQ(final_ref, current_url.ref());
}

// Tests a client->server->server redirect
TEST_F(RedirectTest, ClientServerServer) {
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL(std::string());
  GURL next_to_last = test_server_.GetURL(
      "server-redirect?" + final_url.spec());
  GURL second_url = test_server_.GetURL(
      "server-redirect?" + next_to_last.spec());
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + second_url.spec());
  std::vector<GURL> redirects;

  // We need the sleep for the client redirects, because it appears as two
  // page visits in the browser.
  NavigateToURL(first_url);

  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
    if (!redirects.empty())
      break;
  }

  ASSERT_EQ(3U, redirects.size());
  EXPECT_EQ(second_url.spec(), redirects[0].spec());
  EXPECT_EQ(next_to_last.spec(), redirects[1].spec());
  EXPECT_EQ(final_url.spec(), redirects[2].spec());
}

// Tests that the "#reference" gets preserved across server redirects.
TEST_F(RedirectTest, ServerReference) {
  ASSERT_TRUE(test_server_.Start());

  const std::string ref("reference");

  GURL final_url = test_server_.GetURL(std::string());
  GURL initial_url = test_server_.GetURL(
      "server-redirect?" + final_url.spec() + "#" + ref);

  NavigateToURL(initial_url);

  GURL url = GetActiveTabURL();
  EXPECT_EQ(ref, url.ref());
}

// Test that redirect from http:// to file:// :
// A) does not crash the browser or confuse the redirect chain, see bug 1080873
// B) does not take place.
TEST_F(RedirectTest, NoHttpToFile) {
  ASSERT_TRUE(test_server_.Start());
  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("http_to_file.html");
  GURL file_url = net::FilePathToFileURL(test_file);

  GURL initial_url = test_server_.GetURL(
      "client-redirect?" + file_url.spec());

  NavigateToURL(initial_url);
  // UITest will check for crashes. We make sure the title doesn't match the
  // title from the file, because the nav should not have taken place.
  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  std::wstring actual_title;
  ASSERT_TRUE(tab_proxy->GetTabTitle(&actual_title));
  EXPECT_NE("File!", WideToUTF8(actual_title));
}

// Ensures that non-user initiated location changes (within page) are
// flagged as client redirects. See bug 1139823.
TEST_F(RedirectTest, ClientFragments) {
  ASSERT_TRUE(test_server_.Start());

  FilePath test_file(test_data_directory_);
  test_file = test_file.AppendASCII("ref_redirect.html");
  GURL first_url = net::FilePathToFileURL(test_file);
  std::vector<GURL> redirects;

  NavigateToURL(first_url);

  scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
  ASSERT_TRUE(tab_proxy.get());
  ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
  EXPECT_EQ(1U, redirects.size());
  EXPECT_EQ(first_url.spec() + "#myanchor", redirects[0].spec());
}

// TODO(timsteele): This is disabled because our current testserver can't
// handle multiple requests in parallel, making it hang on the first request
// to /slow?60. It's unable to serve our second request for files/title2.html
// until /slow? completes, which doesn't give the desired behavior. We could
// alternatively load the second page from disk, but we would need to start
// the browser for this testcase with --process-per-tab, and I don't think
// we can do this at test-case-level granularity at the moment.
// http://crbug.com/45056
TEST_F(RedirectTest,
       DISABLED_ClientCancelledByNewNavigationAfterProvisionalLoad) {
  // We want to initiate a second navigation after the provisional load for
  // the client redirect destination has started, but before this load is
  // committed. To achieve this, we tell the browser to load a slow page,
  // which causes it to start a provisional load, and while it is waiting
  // for the response (which means it hasn't committed the load for the client
  // redirect destination page yet), we issue a new navigation request.
  ASSERT_TRUE(test_server_.Start());

  GURL final_url = test_server_.GetURL("files/title2.html");
  GURL slow = test_server_.GetURL("slow?60");
  GURL first_url = test_server_.GetURL(
      "client-redirect?" + slow.spec());
  std::vector<GURL> redirects;

  NavigateToURL(first_url);
  // We don't sleep here - the first navigation won't have been committed yet
  // because we told the server to wait a minute. This means the browser has
  // started it's provisional load for the client redirect destination page but
  // hasn't completed. Our time is now!
  NavigateToURL(final_url);

  std::wstring tab_title;
  std::wstring final_url_title = UTF8ToWide("Title Of Awesomeness");
  // Wait till the final page has been loaded.
  for (int i = 0; i < 10; ++i) {
    PlatformThread::Sleep(sleep_timeout_ms());
    scoped_refptr<TabProxy> tab_proxy(GetActiveTab());
    ASSERT_TRUE(tab_proxy.get());
    ASSERT_TRUE(tab_proxy->GetTabTitle(&tab_title));
    if (tab_title == final_url_title) {
      ASSERT_TRUE(tab_proxy->GetRedirectsFrom(first_url, &redirects));
      break;
    }
  }

  // Check to make sure the navigation did in fact take place and we are
  // at the expected page.
  EXPECT_EQ(final_url_title, tab_title);

  bool final_navigation_not_redirect = true;
  // Check to make sure our request for files/title2.html doesn't get flagged
  // as a client redirect from the first (/client-redirect?) page.
  for (std::vector<GURL>::iterator it = redirects.begin();
       it != redirects.end(); ++it) {
    if (final_url.spec() == it->spec()) {
      final_navigation_not_redirect = false;
      break;
    }
  }
  EXPECT_TRUE(final_navigation_not_redirect);
}

}  // namespace
<|endoftext|>