<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: multisigdemo.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: vg $ $Date: 2006-04-07 11:58:21 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <stdio.h>
#include "util.hxx"

#include <rtl/ustring.hxx>
#include <cppuhelper/servicefactory.hxx>

#include <xmlsecurity/biginteger.hxx>
#include <xmlsecurity/xmlsignaturehelper.hxx>
#include "xmlsecurity/baseencoding.hxx"
#include <tools/date.hxx>
#include <tools/time.hxx>

using namespace ::com::sun::star;

long denyVerifyHandler( void *, void * )
{
    return  0;
}

long startVerifyHandler( void *, void * )
{
    return QueryVerifySignature();
}

int SAL_CALL main( int argc, char **argv )
{
    if( argc < 5 )
    {
        fprintf( stderr, "Usage: %s <signature file 1> <signature file 2> <xml stream file> <binary stream file> [<cryptoken>]\n" , argv[0] ) ;
        return -1 ;
    }

    uno::Reference< lang::XMultiServiceFactory > xMSF = CreateDemoServiceFactory();

    rtl::OUString aSIGFileName = rtl::OUString::createFromAscii(argv[1]);
    rtl::OUString aSIGFileName2 = rtl::OUString::createFromAscii(argv[2]);
    rtl::OUString aXMLFileName = rtl::OUString::createFromAscii(argv[3]);
    rtl::OUString aBINFileName = rtl::OUString::createFromAscii(argv[4]);
    rtl::OUString aCryptoToken;
    if ( argc >= 7 )
        aCryptoToken = rtl::OUString::createFromAscii(argv[6]);

    sal_Int32 nSecurityId;
    uno::Reference< io::XOutputStream > xOutputStream;
    uno::Reference< io::XInputStream > xInputStream;
    bool bDone;
    SignatureInformations signatureInformations;
    uno::Reference< ::com::sun::star::xml::sax::XDocumentHandler> xDocumentHandler;

    // -------- START -------

    XMLSignatureHelper aSignatureHelper( xMSF );

    bool bInit = aSignatureHelper.Init( aCryptoToken );
    if ( !bInit )
    {
        fprintf( stderr, "Error initializing security context!\n" );
        return -1;
    }

    fprintf( stdout, "\n\nTEST MISSION 1: Create the first signature file\n");

    aSignatureHelper.StartMission();

    /*
     * select a private key certificate
     */
    uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnvironment = aSignatureHelper.GetSecurityEnvironment();
    uno::Sequence< uno::Reference< ::com::sun::star::security::XCertificate > > xPersonalCerts = xSecurityEnvironment->getPersonalCertificates() ;

    fprintf( stdout, "\nPlease select two certificates:\n" );

    for ( int nSig = 0; nSig < 2; nSig++ )
    {
        // New security ID for signature...
        nSecurityId = aSignatureHelper.GetNewSecurityId();

        // Select certificate...
        uno::Reference< ::com::sun::star::security::XCertificate > xPersonalCert = getCertificateFromEnvironment( xSecurityEnvironment, true );
        aSignatureHelper.SetX509Certificate(
            nSecurityId, xPersonalCert->getIssuerName(),
            bigIntegerToNumericString( xPersonalCert->getSerialNumber()),
            baseEncode(xPersonalCert->getEncoded(), BASE64));
        aSignatureHelper.AddForSigning( nSecurityId, aXMLFileName, aXMLFileName, sal_False );
        aSignatureHelper.AddForSigning( nSecurityId, aBINFileName, aBINFileName, sal_True );
        aSignatureHelper.SetDateTime( nSecurityId, Date(), Time() );
    }
    /*
     * creates signature
     */
    xOutputStream = OpenOutputStream( aSIGFileName );
    bDone = aSignatureHelper.CreateAndWriteSignature( xOutputStream );
    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 1: Error creating Signature!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 1: Signature successfully created!\n" );

    aSignatureHelper.EndMission();

    fprintf( stdout, "\n\nTEST MISSION 2: Transfer the second signature to a new signature file\n");

    /*
     * You can use an uninitialized SignatureHelper to perform this mission.
     */

    /*
     * configures the start-verify handler. Don't need to verify for transfering...
     */
    aSignatureHelper.SetStartVerifySignatureHdl( Link( NULL, denyVerifyHandler ) );
    aSignatureHelper.StartMission();

    xInputStream = OpenInputStream( aSIGFileName );
    bDone = aSignatureHelper.ReadAndVerifySignature( xInputStream );
    xInputStream->closeInput();

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 2: Error in reading Signature!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 2: Signature successfully transfered!\n" );

    /*
     * get all signature information
     */
    signatureInformations = aSignatureHelper.GetSignatureInformations();

    /*
     * write the first signature into the second signature file.
     */

    xOutputStream = OpenOutputStream( aSIGFileName2 );
    xDocumentHandler = aSignatureHelper.CreateDocumentHandlerWithHeader( xOutputStream);
    aSignatureHelper.ExportSignature( xDocumentHandler, signatureInformations[1]);
    aSignatureHelper.CloseDocumentHandler( xDocumentHandler);
    aSignatureHelper.EndMission();

    fprintf( stdout, "\n\nTEST MISSION 3: Insert a new signature to the first signature file\n");

    aSignatureHelper.StartMission();

    nSecurityId = aSignatureHelper.GetNewSecurityId();

    // Select certificate...
    uno::Reference< ::com::sun::star::security::XCertificate > xPersonalCert = getCertificateFromEnvironment( xSecurityEnvironment, true );
    aSignatureHelper.SetX509Certificate(
        nSecurityId, xPersonalCert->getIssuerName(),
        bigIntegerToNumericString( xPersonalCert->getSerialNumber()),
        baseEncode(xPersonalCert->getEncoded(), BASE64));
    aSignatureHelper.AddForSigning( nSecurityId, aXMLFileName, aXMLFileName, sal_False );
    aSignatureHelper.AddForSigning( nSecurityId, aBINFileName, aBINFileName, sal_True );
    aSignatureHelper.SetDateTime( nSecurityId, Date(), Time() );

    xOutputStream = OpenOutputStream( aSIGFileName );
    xDocumentHandler = aSignatureHelper.CreateDocumentHandlerWithHeader( xOutputStream);

    aSignatureHelper.ExportSignature( xDocumentHandler, signatureInformations[0]);
    bDone = aSignatureHelper.CreateAndWriteSignature( xDocumentHandler );
    aSignatureHelper.ExportSignature( xDocumentHandler, signatureInformations[1]);
    aSignatureHelper.CloseDocumentHandler( xDocumentHandler);

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 3: Error creating Signature!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 3: Signature successfully created!\n" );

    aSignatureHelper.EndMission();

    fprintf( stdout, "\n\nTEST MISSION 4 : Verify the first signature file\n");

    aSignatureHelper.SetStartVerifySignatureHdl( Link( NULL, startVerifyHandler ) );

    aSignatureHelper.StartMission();

    xInputStream = OpenInputStream( aSIGFileName );
    bDone = aSignatureHelper.ReadAndVerifySignature( xInputStream );
    xInputStream->closeInput();

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 4: Error verifying Signatures!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 4: All choosen Signatures veryfied successfully!\n" );

    aSignatureHelper.EndMission();

    QueryPrintSignatureDetails( aSignatureHelper.GetSignatureInformations(), aSignatureHelper.GetSecurityEnvironment() );

    fprintf( stdout, "\n\nTEST MISSION 5: Verify the second signature file\n");

    aSignatureHelper.StartMission();

    xInputStream = OpenInputStream( aSIGFileName2 );
    bDone = aSignatureHelper.ReadAndVerifySignature( xInputStream );
    xInputStream->closeInput();

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 5: Error verifying Signatures!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 5: All choosen Signatures veryfied successfully!\n" );

    aSignatureHelper.EndMission();

    QueryPrintSignatureDetails( aSignatureHelper.GetSignatureInformations(), aSignatureHelper.GetSecurityEnvironment() );

    return 0;
}
<commit_msg>INTEGRATION: CWS pchfix02 (1.7.38); FILE MERGED 2006/09/01 18:00:57 kaib 1.7.38.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: multisigdemo.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-16 14:47:33 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmlsecurity.hxx"

#include <stdio.h>
#include "util.hxx"

#include <rtl/ustring.hxx>
#include <cppuhelper/servicefactory.hxx>

#include <xmlsecurity/biginteger.hxx>
#include <xmlsecurity/xmlsignaturehelper.hxx>
#include "xmlsecurity/baseencoding.hxx"
#include <tools/date.hxx>
#include <tools/time.hxx>

using namespace ::com::sun::star;

long denyVerifyHandler( void *, void * )
{
    return  0;
}

long startVerifyHandler( void *, void * )
{
    return QueryVerifySignature();
}

int SAL_CALL main( int argc, char **argv )
{
    if( argc < 5 )
    {
        fprintf( stderr, "Usage: %s <signature file 1> <signature file 2> <xml stream file> <binary stream file> [<cryptoken>]\n" , argv[0] ) ;
        return -1 ;
    }

    uno::Reference< lang::XMultiServiceFactory > xMSF = CreateDemoServiceFactory();

    rtl::OUString aSIGFileName = rtl::OUString::createFromAscii(argv[1]);
    rtl::OUString aSIGFileName2 = rtl::OUString::createFromAscii(argv[2]);
    rtl::OUString aXMLFileName = rtl::OUString::createFromAscii(argv[3]);
    rtl::OUString aBINFileName = rtl::OUString::createFromAscii(argv[4]);
    rtl::OUString aCryptoToken;
    if ( argc >= 7 )
        aCryptoToken = rtl::OUString::createFromAscii(argv[6]);

    sal_Int32 nSecurityId;
    uno::Reference< io::XOutputStream > xOutputStream;
    uno::Reference< io::XInputStream > xInputStream;
    bool bDone;
    SignatureInformations signatureInformations;
    uno::Reference< ::com::sun::star::xml::sax::XDocumentHandler> xDocumentHandler;

    // -------- START -------

    XMLSignatureHelper aSignatureHelper( xMSF );

    bool bInit = aSignatureHelper.Init( aCryptoToken );
    if ( !bInit )
    {
        fprintf( stderr, "Error initializing security context!\n" );
        return -1;
    }

    fprintf( stdout, "\n\nTEST MISSION 1: Create the first signature file\n");

    aSignatureHelper.StartMission();

    /*
     * select a private key certificate
     */
    uno::Reference< xml::crypto::XSecurityEnvironment > xSecurityEnvironment = aSignatureHelper.GetSecurityEnvironment();
    uno::Sequence< uno::Reference< ::com::sun::star::security::XCertificate > > xPersonalCerts = xSecurityEnvironment->getPersonalCertificates() ;

    fprintf( stdout, "\nPlease select two certificates:\n" );

    for ( int nSig = 0; nSig < 2; nSig++ )
    {
        // New security ID for signature...
        nSecurityId = aSignatureHelper.GetNewSecurityId();

        // Select certificate...
        uno::Reference< ::com::sun::star::security::XCertificate > xPersonalCert = getCertificateFromEnvironment( xSecurityEnvironment, true );
        aSignatureHelper.SetX509Certificate(
            nSecurityId, xPersonalCert->getIssuerName(),
            bigIntegerToNumericString( xPersonalCert->getSerialNumber()),
            baseEncode(xPersonalCert->getEncoded(), BASE64));
        aSignatureHelper.AddForSigning( nSecurityId, aXMLFileName, aXMLFileName, sal_False );
        aSignatureHelper.AddForSigning( nSecurityId, aBINFileName, aBINFileName, sal_True );
        aSignatureHelper.SetDateTime( nSecurityId, Date(), Time() );
    }
    /*
     * creates signature
     */
    xOutputStream = OpenOutputStream( aSIGFileName );
    bDone = aSignatureHelper.CreateAndWriteSignature( xOutputStream );
    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 1: Error creating Signature!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 1: Signature successfully created!\n" );

    aSignatureHelper.EndMission();

    fprintf( stdout, "\n\nTEST MISSION 2: Transfer the second signature to a new signature file\n");

    /*
     * You can use an uninitialized SignatureHelper to perform this mission.
     */

    /*
     * configures the start-verify handler. Don't need to verify for transfering...
     */
    aSignatureHelper.SetStartVerifySignatureHdl( Link( NULL, denyVerifyHandler ) );
    aSignatureHelper.StartMission();

    xInputStream = OpenInputStream( aSIGFileName );
    bDone = aSignatureHelper.ReadAndVerifySignature( xInputStream );
    xInputStream->closeInput();

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 2: Error in reading Signature!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 2: Signature successfully transfered!\n" );

    /*
     * get all signature information
     */
    signatureInformations = aSignatureHelper.GetSignatureInformations();

    /*
     * write the first signature into the second signature file.
     */

    xOutputStream = OpenOutputStream( aSIGFileName2 );
    xDocumentHandler = aSignatureHelper.CreateDocumentHandlerWithHeader( xOutputStream);
    aSignatureHelper.ExportSignature( xDocumentHandler, signatureInformations[1]);
    aSignatureHelper.CloseDocumentHandler( xDocumentHandler);
    aSignatureHelper.EndMission();

    fprintf( stdout, "\n\nTEST MISSION 3: Insert a new signature to the first signature file\n");

    aSignatureHelper.StartMission();

    nSecurityId = aSignatureHelper.GetNewSecurityId();

    // Select certificate...
    uno::Reference< ::com::sun::star::security::XCertificate > xPersonalCert = getCertificateFromEnvironment( xSecurityEnvironment, true );
    aSignatureHelper.SetX509Certificate(
        nSecurityId, xPersonalCert->getIssuerName(),
        bigIntegerToNumericString( xPersonalCert->getSerialNumber()),
        baseEncode(xPersonalCert->getEncoded(), BASE64));
    aSignatureHelper.AddForSigning( nSecurityId, aXMLFileName, aXMLFileName, sal_False );
    aSignatureHelper.AddForSigning( nSecurityId, aBINFileName, aBINFileName, sal_True );
    aSignatureHelper.SetDateTime( nSecurityId, Date(), Time() );

    xOutputStream = OpenOutputStream( aSIGFileName );
    xDocumentHandler = aSignatureHelper.CreateDocumentHandlerWithHeader( xOutputStream);

    aSignatureHelper.ExportSignature( xDocumentHandler, signatureInformations[0]);
    bDone = aSignatureHelper.CreateAndWriteSignature( xDocumentHandler );
    aSignatureHelper.ExportSignature( xDocumentHandler, signatureInformations[1]);
    aSignatureHelper.CloseDocumentHandler( xDocumentHandler);

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 3: Error creating Signature!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 3: Signature successfully created!\n" );

    aSignatureHelper.EndMission();

    fprintf( stdout, "\n\nTEST MISSION 4 : Verify the first signature file\n");

    aSignatureHelper.SetStartVerifySignatureHdl( Link( NULL, startVerifyHandler ) );

    aSignatureHelper.StartMission();

    xInputStream = OpenInputStream( aSIGFileName );
    bDone = aSignatureHelper.ReadAndVerifySignature( xInputStream );
    xInputStream->closeInput();

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 4: Error verifying Signatures!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 4: All choosen Signatures veryfied successfully!\n" );

    aSignatureHelper.EndMission();

    QueryPrintSignatureDetails( aSignatureHelper.GetSignatureInformations(), aSignatureHelper.GetSecurityEnvironment() );

    fprintf( stdout, "\n\nTEST MISSION 5: Verify the second signature file\n");

    aSignatureHelper.StartMission();

    xInputStream = OpenInputStream( aSIGFileName2 );
    bDone = aSignatureHelper.ReadAndVerifySignature( xInputStream );
    xInputStream->closeInput();

    if ( !bDone )
        fprintf( stderr, "\nSTATUS MISSION 5: Error verifying Signatures!\n" );
    else
        fprintf( stdout, "\nSTATUS MISSION 5: All choosen Signatures veryfied successfully!\n" );

    aSignatureHelper.EndMission();

    QueryPrintSignatureDetails( aSignatureHelper.GetSignatureInformations(), aSignatureHelper.GetSecurityEnvironment() );

    return 0;
}
<|endoftext|>


<commit_before>//
//  CRTC6845.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 31/07/2017.
//  Copyright © 2017 Thomas Harte. All rights reserved.
//

#ifndef CRTC6845_hpp
#define CRTC6845_hpp

#include "../../ClockReceiver/ClockReceiver.hpp"

#include <cstdint>
#include <cstdio>

namespace Motorola {
namespace CRTC {

struct BusState {
	bool display_enable;
	bool hsync;
	bool vsync;
	bool cursor;
	uint16_t refresh_address;
	uint16_t row_address;
};

class BusHandler {
	public:
		/*!
			Performs the first phase of a 6845 bus cycle; this is the phase in which it is intended that
			systems using the 6845 respect the bus state and produce pixels, sync or whatever they require.
		*/
		void perform_bus_cycle_phase1(const BusState &) {}

		/*!
			Performs the second phase of a 6845 bus cycle. Some bus state — including sync — is updated
			directly after phase 1 and hence is visible to an observer during phase 2. Handlers may therefore
			implement @c perform_bus_cycle_phase2 to be notified of the availability of that state without
			having to wait until the next cycle has begun.
		*/
		void perform_bus_cycle_phase2(const BusState &) {}
};

enum Personality {
	HD6845S,	//
	UM6845R,	//
	MC6845,		//
	AMS40226	//
};

template <class T> class CRTC6845 {
	public:

		CRTC6845(Personality p, T &bus_handler) noexcept :
			personality_(p), bus_handler_(bus_handler) {}

		void select_register(uint8_t r) {
			selected_register_ = r;
		}

		uint8_t get_status() const {
			return 0xff;
		}

		uint8_t get_register() const {
			if(selected_register_ < 12 || selected_register_ > 17) return 0xff;
			return registers_[selected_register_];
		}

		void set_register(uint8_t value) {
			static uint8_t masks[] = {
				0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f,
				0xff, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff
			};

			if(selected_register_ < 16) {
				registers_[selected_register_] = value & masks[selected_register_];
			}
		}

		void trigger_light_pen() {
			registers_[17] = bus_state_.refresh_address & 0xff;
			registers_[16] = bus_state_.refresh_address >> 8;
		}

		void run_for(Cycles cycles) {
			int cyles_remaining = cycles.as_int();
			while(cyles_remaining--) {
				// check for end of visible characters
				if(character_counter_ == registers_[1]) {
					// TODO: consider skew in character_is_visible_. Or maybe defer until perform_bus_cycle?
					character_is_visible_ = false;
					end_of_line_address_ = bus_state_.refresh_address;
				}

				perform_bus_cycle_phase1();
				bus_state_.refresh_address = (bus_state_.refresh_address + 1) & 0x3fff;

				// check for end-of-line
				if(character_counter_ == registers_[0]) {
					character_counter_ = 0;
					do_end_of_line();
					character_is_visible_ = true;
				} else {
					// increment counter
					character_counter_++;
				}

				// check for start of horizontal sync
				if(character_counter_ == registers_[2]) {
					hsync_counter_ = 0;
					bus_state_.hsync = true;
				}

				// check for end of horizontal sync; note that a sync time of zero will result in an immediate
				// cancellation of the plan to perform sync
				if(bus_state_.hsync) {
					bus_state_.hsync = hsync_counter_ != (registers_[3] & 15);
					hsync_counter_ = (hsync_counter_ + 1) & 15;
				}

				perform_bus_cycle_phase2();
			}
		}

		const BusState &get_bus_state() const {
			return bus_state_;
		}

	private:
		inline void perform_bus_cycle_phase1() {
			bus_state_.display_enable = character_is_visible_ && line_is_visible_;
			bus_handler_.perform_bus_cycle_phase1(bus_state_);
		}

		inline void perform_bus_cycle_phase2() {
			bus_state_.display_enable = character_is_visible_ && line_is_visible_;
			bus_handler_.perform_bus_cycle_phase2(bus_state_);
		}

		inline void do_end_of_line() {
			// check for end of vertical sync
			if(bus_state_.vsync) {
				vsync_counter_ = (vsync_counter_ + 1) & 15;
				if(vsync_counter_ == (registers_[3] >> 4)) {
					bus_state_.vsync = false;
				}
			}

			if(is_in_adjustment_period_) {
				line_counter_++;
				if(line_counter_ == registers_[5]) {
					is_in_adjustment_period_ = false;
					do_end_of_frame();
				}
			} else {
				// advance vertical counter
				if(bus_state_.row_address == registers_[9]) {
					bus_state_.row_address = 0;
					line_address_ = end_of_line_address_;

					// check for entry into the overflow area
					if(line_counter_ == registers_[4]) {
						if(registers_[5]) {
							line_counter_ = 0;
							is_in_adjustment_period_ = true;
						} else {
							do_end_of_frame();
						}
					} else {
						line_counter_ = (line_counter_ + 1) & 0x7f;

						// check for start of vertical sync
						if(line_counter_ == registers_[7]) {
							bus_state_.vsync = true;
							vsync_counter_ = 0;
						}

						// check for end of visible lines
						if(line_counter_ == registers_[6]) {
							line_is_visible_ = false;
						}
					}
				} else {
					bus_state_.row_address = (bus_state_.row_address + 1) & 0x1f;
				}
			}

			bus_state_.refresh_address = line_address_;
			character_counter_ = 0;
			character_is_visible_ = (registers_[1] != 0);
		}

		inline void do_end_of_frame() {
			line_counter_ = 0;
			line_is_visible_ = true;
			line_address_ = (uint16_t)((registers_[12] << 8) | registers_[13]);
			bus_state_.refresh_address = line_address_;
		}

		Personality personality_;
		T &bus_handler_;
		BusState bus_state_;

		uint8_t registers_[18];
		int selected_register_;

		uint8_t character_counter_;
		uint8_t line_counter_;

		bool character_is_visible_, line_is_visible_;

		int hsync_counter_;
		int vsync_counter_;
		bool is_in_adjustment_period_;

		uint16_t line_address_;
		uint16_t end_of_line_address_;
};

}
}

#endif /* CRTC6845_hpp */
<commit_msg>Started making some formal admissions that different CRTC models exist. Plenty yet to do.<commit_after>//
//  CRTC6845.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 31/07/2017.
//  Copyright © 2017 Thomas Harte. All rights reserved.
//

#ifndef CRTC6845_hpp
#define CRTC6845_hpp

#include "../../ClockReceiver/ClockReceiver.hpp"

#include <cstdint>
#include <cstdio>

namespace Motorola {
namespace CRTC {

struct BusState {
	bool display_enable;
	bool hsync;
	bool vsync;
	bool cursor;
	uint16_t refresh_address;
	uint16_t row_address;
};

class BusHandler {
	public:
		/*!
			Performs the first phase of a 6845 bus cycle; this is the phase in which it is intended that
			systems using the 6845 respect the bus state and produce pixels, sync or whatever they require.
		*/
		void perform_bus_cycle_phase1(const BusState &) {}

		/*!
			Performs the second phase of a 6845 bus cycle. Some bus state — including sync — is updated
			directly after phase 1 and hence is visible to an observer during phase 2. Handlers may therefore
			implement @c perform_bus_cycle_phase2 to be notified of the availability of that state without
			having to wait until the next cycle has begun.
		*/
		void perform_bus_cycle_phase2(const BusState &) {}
};

enum Personality {
	HD6845S,	// Type 0 in CPC parlance. Zero-width HSYNC available, no status, programmable VSYNC length.
	UM6845R,	// Type 1 in CPC parlance. Status register, fixed-length VSYNC.
	MC6845,		// Type 2. No status register, fixed-length VSYNC, no zero-length HSYNC.
	AMS40226	// Type 3. Status is get register, fixed-length VSYNC, no zero-length HSYNC.
};

// TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences

template <class T> class CRTC6845 {
	public:

		CRTC6845(Personality p, T &bus_handler) noexcept :
			personality_(p), bus_handler_(bus_handler), status_(0) {}

		void select_register(uint8_t r) {
			selected_register_ = r;
		}

		uint8_t get_status() const {
			switch(personality_) {
				case UM6845R:	return status_ | (bus_state_.vsync ? 0x20 : 0x00);
				case AMS40226:	return get_register();
				default:		return 0xff;
			}
			return 0xff;
		}

		uint8_t get_register() const {
			if(selected_register_ == 31) status_ &= ~0x80;
			if(selected_register_ == 16 || selected_register_ == 17) status_ &= ~0x40;

			if(personality_ == UM6845R && selected_register_ == 31) return dummy_register_;
			if(selected_register_ < 12 || selected_register_ > 17) return 0xff;
			return registers_[selected_register_];
		}

		void set_register(uint8_t value) {
			static uint8_t masks[] = {
				0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f,
				0xff, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff
			};

			if(selected_register_ < 16) {
				registers_[selected_register_] = value & masks[selected_register_];
			}
			if(selected_register_ == 31 && personality_ == UM6845R) {
				dummy_register_ = value;
			}
		}

		void trigger_light_pen() {
			registers_[17] = bus_state_.refresh_address & 0xff;
			registers_[16] = bus_state_.refresh_address >> 8;
			status_ |= 0x40;
		}

		void run_for(Cycles cycles) {
			int cyles_remaining = cycles.as_int();
			while(cyles_remaining--) {
				// check for end of visible characters
				if(character_counter_ == registers_[1]) {
					// TODO: consider skew in character_is_visible_. Or maybe defer until perform_bus_cycle?
					character_is_visible_ = false;
					end_of_line_address_ = bus_state_.refresh_address;
				}

				perform_bus_cycle_phase1();
				bus_state_.refresh_address = (bus_state_.refresh_address + 1) & 0x3fff;

				// check for end-of-line
				if(character_counter_ == registers_[0]) {
					character_counter_ = 0;
					do_end_of_line();
					character_is_visible_ = true;
				} else {
					// increment counter
					character_counter_++;
				}

				// check for start of horizontal sync
				if(character_counter_ == registers_[2]) {
					hsync_counter_ = 0;
					bus_state_.hsync = true;
				}

				// check for end of horizontal sync; note that a sync time of zero will result in an immediate
				// cancellation of the plan to perform sync
				if(bus_state_.hsync) {
					switch(personality_) {
						case HD6845S:
						case UM6845R:
							bus_state_.hsync = hsync_counter_ != (registers_[3] & 15);
							hsync_counter_ = (hsync_counter_ + 1) & 15;
						break;
						default:
							hsync_counter_ = (hsync_counter_ + 1) & 15;
							bus_state_.hsync = hsync_counter_ != (registers_[3] & 15);
						break;
					}
				}

				perform_bus_cycle_phase2();
			}
		}

		const BusState &get_bus_state() const {
			return bus_state_;
		}

	private:
		inline void perform_bus_cycle_phase1() {
			bus_state_.display_enable = character_is_visible_ && line_is_visible_;
			bus_handler_.perform_bus_cycle_phase1(bus_state_);
		}

		inline void perform_bus_cycle_phase2() {
			bus_state_.display_enable = character_is_visible_ && line_is_visible_;
			bus_handler_.perform_bus_cycle_phase2(bus_state_);
		}

		inline void do_end_of_line() {
			// check for end of vertical sync
			if(bus_state_.vsync) {
				vsync_counter_ = (vsync_counter_ + 1) & 15;
				switch(personality_) {
					case HD6845S:
					case AMS40226:
						bus_state_.vsync = vsync_counter_ != (registers_[3] >> 4);
					break;
					default:
						bus_state_.vsync = vsync_counter_ != 0;
					break;
				}
			}

			if(is_in_adjustment_period_) {
				line_counter_++;
				if(line_counter_ == registers_[5]) {
					is_in_adjustment_period_ = false;
					do_end_of_frame();
				}
			} else {
				// advance vertical counter
				if(bus_state_.row_address == registers_[9]) {
					bus_state_.row_address = 0;
					line_address_ = end_of_line_address_;

					// check for entry into the overflow area
					if(line_counter_ == registers_[4]) {
						if(registers_[5]) {
							line_counter_ = 0;
							is_in_adjustment_period_ = true;
						} else {
							do_end_of_frame();
						}
					} else {
						line_counter_ = (line_counter_ + 1) & 0x7f;

						// check for start of vertical sync
						if(line_counter_ == registers_[7]) {
							bus_state_.vsync = true;
							vsync_counter_ = 0;
						}

						// check for end of visible lines
						if(line_counter_ == registers_[6]) {
							line_is_visible_ = false;
						}
					}
				} else {
					bus_state_.row_address = (bus_state_.row_address + 1) & 0x1f;
				}
			}

			bus_state_.refresh_address = line_address_;
			character_counter_ = 0;
			character_is_visible_ = (registers_[1] != 0);
		}

		inline void do_end_of_frame() {
			line_counter_ = 0;
			line_is_visible_ = true;
			line_address_ = (uint16_t)((registers_[12] << 8) | registers_[13]);
			bus_state_.refresh_address = line_address_;
		}

		Personality personality_;
		T &bus_handler_;
		BusState bus_state_;

		uint8_t registers_[18];
		uint8_t dummy_register_;
		int selected_register_;

		uint8_t character_counter_;
		uint8_t line_counter_;

		bool character_is_visible_, line_is_visible_;

		int hsync_counter_;
		int vsync_counter_;
		bool is_in_adjustment_period_;

		uint16_t line_address_;
		uint16_t end_of_line_address_;
		uint8_t status_;
};

}
}

#endif /* CRTC6845_hpp */
<|endoftext|>


<commit_before>#include "catch.hpp"

#include "scene/drawRule.h"
#include "scene/sceneLayer.h"
#include "platform.h"

#include <cstdio>
#include <algorithm>

using namespace Tangram;

// Functions to initialize DrawRule instances
const int dg1 = 0;
const int dg2 = 1;

DrawRuleData instance_a() {

    std::vector<StyleParam> params = {
        { StyleParamKey::order, "value_0a" },
        { StyleParamKey::join, "value_4a" },
        { StyleParamKey::color, "value_1a" }
    };

    return { "dg1", dg1, std::move(params) };

}

DrawRuleData instance_b() {

    std::vector<StyleParam> params = {
        { StyleParamKey::order, "value_0b" },
        { StyleParamKey::width, "value_2b" },
        { StyleParamKey::color, "value_1b" },
        { StyleParamKey::cap, "value_3b" },
        { StyleParamKey::style, "value_4b" }
    };

    return { "dg1", dg1, std::move(params) };

}

DrawRuleData instance_c() {

    std::vector<StyleParam> params = {};

    // changed from dg2 - styles will not be merged otherwise
    return { "dg1", dg1, params };

}

TEST_CASE("DrawRule correctly merges with another DrawRule", "[DrawRule]") {

    const SceneLayer layer_a = { "a", Filter(), { instance_a() }, {} };
    const SceneLayer layer_b = { "b", Filter(), { instance_b() }, {} };
    const SceneLayer layer_c = { "c", Filter(), { instance_c() }, {} };

    // For parameters contained in multiple rules, the parameter from the last rule
    // (by lexicographical order) should result.
    {
        DrawRuleMergeSet ruleSet;
        ruleSet.mergeRules(layer_a);

        REQUIRE(ruleSet.matchedRules().size() == 1);
        auto& merged_ab = ruleSet.matchedRules()[0];

        for (size_t i = 0; i < StyleParamKeySize; i++) {
            auto* param = merged_ab.params[i].param;
            if (!param) {
                logMsg("param : none %d\n", i);
                continue;
            }
            logMsg("param : %s\n", param->toString().c_str());
        }

        ruleSet.mergeRules(layer_b);

        REQUIRE(ruleSet.matchedRules().size() == 1);

        // printf("rule_a:\n %s", rule_a.toString().c_str());
        // printf("rule_c:\n %s", rule_c.toString().c_str());
        // printf("merged_ac:\n %s", merged_ac.toString().c_str());
        for (size_t i = 0; i < StyleParamKeySize; i++) {
            auto* param = merged_ab.params[i].param;
            if (!param) {
                logMsg("param : none %d\n", i);
                continue;
            }
            logMsg("param : %s\n", param->toString().c_str());
        }
        REQUIRE(merged_ab.findParameter(StyleParamKey::cap).key == StyleParamKey::cap);
        REQUIRE(merged_ab.findParameter(StyleParamKey::cap).value.get<std::string>() == "value_3b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::color).key == StyleParamKey::color);
        REQUIRE(merged_ab.findParameter(StyleParamKey::color).value.get<std::string>() == "value_1b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::join).key == StyleParamKey::join);
        REQUIRE(merged_ab.findParameter(StyleParamKey::join).value.get<std::string>() == "value_4a");
        REQUIRE(merged_ab.findParameter(StyleParamKey::order).key == StyleParamKey::order);
        REQUIRE(merged_ab.findParameter(StyleParamKey::order).value.get<std::string>() == "value_0b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::style).key == StyleParamKey::style);
        REQUIRE(merged_ab.findParameter(StyleParamKey::style).value.get<std::string>() == "value_4b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::width).key == StyleParamKey::width);
        REQUIRE(merged_ab.findParameter(StyleParamKey::width).value.get<std::string>() == "value_2b");

        // explicit style wins
        REQUIRE(merged_ab.getStyleName() == "value_4b");
    }

    {
        DrawRuleMergeSet ruleSet;
        ruleSet.mergeRules(layer_b);
        ruleSet.mergeRules(layer_a);

        REQUIRE(ruleSet.matchedRules().size() == 1);

        auto& merged_ba = ruleSet.matchedRules()[0];

        REQUIRE(merged_ba.findParameter(StyleParamKey::cap).key == StyleParamKey::cap);
        REQUIRE(merged_ba.findParameter(StyleParamKey::cap).value.get<std::string>() == "value_3b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::color).key == StyleParamKey::color);
        REQUIRE(merged_ba.findParameter(StyleParamKey::color).value.get<std::string>() == "value_1b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::join).key == StyleParamKey::join);
        REQUIRE(merged_ba.findParameter(StyleParamKey::join).value.get<std::string>() == "value_4a");
        REQUIRE(merged_ba.findParameter(StyleParamKey::order).key == StyleParamKey::order);
        REQUIRE(merged_ba.findParameter(StyleParamKey::order).value.get<std::string>() == "value_0b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::style).key == StyleParamKey::style);
        REQUIRE(merged_ba.findParameter(StyleParamKey::style).value.get<std::string>() == "value_4b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::width).key == StyleParamKey::width);
        REQUIRE(merged_ba.findParameter(StyleParamKey::width).value.get<std::string>() == "value_2b");

        // explicit style wins
        REQUIRE(merged_ba.getStyleName() == "value_4b");
    }

    {
        DrawRuleMergeSet ruleSet;
        ruleSet.mergeRules(layer_c);
        ruleSet.mergeRules(layer_b);

        REQUIRE(ruleSet.matchedRules().size() == 1);

        auto& merged_bc = ruleSet.matchedRules()[0];

        // for (size_t i = 0; i < StyleParamKeySize; i++) {
        //     auto* param = merged_bc.params[i];
        //     if (!param) { continue; }
        //     REQUIRE(param->key == rule_b[0].parameters[i].key);
        //     REQUIRE(param->value.get<std::string>() ==
        //             rule_b[0].parameters[i].value.get<std::string>());
        // }

        // explicit style wins
        REQUIRE(merged_bc.getStyleName() == "value_4b");
    }

}

TEST_CASE("DrawRule locates and outputs a parameter that it contains", "[DrawRule]") {

    std::string str;

    const SceneLayer layer_a = { "a", Filter(), { instance_a() }, {} };
    const SceneLayer layer_b = { "b", Filter(), { instance_b() }, {} };

    DrawRuleMergeSet a;
    a.mergeRules(layer_a);
    auto& rule_a = a.matchedRules()[0];

    REQUIRE(rule_a.get(StyleParamKey::order, str)); REQUIRE(str == "value_0a");
    REQUIRE(rule_a.get(StyleParamKey::color, str)); REQUIRE(str == "value_1a");
    REQUIRE(rule_a.get(StyleParamKey::join, str)); REQUIRE(str == "value_4a");

    DrawRuleMergeSet b;
    b.mergeRules(layer_b);
    auto& rule_b = b.matchedRules()[0];

    REQUIRE(rule_b.get(StyleParamKey::color, str)); REQUIRE(str == "value_1b");
    REQUIRE(rule_b.get(StyleParamKey::width, str)); REQUIRE(str == "value_2b");
    REQUIRE(rule_b.get(StyleParamKey::cap, str)); REQUIRE(str == "value_3b");
    REQUIRE(rule_b.get(StyleParamKey::order, str)); REQUIRE(str == "value_0b");
}

TEST_CASE("DrawRule correctly reports that it doesn't contain a parameter", "[DrawRule]") {
    std::string str;

    const SceneLayer layer_a = { "a", Filter(), { instance_a() }, {} };
    DrawRuleMergeSet a;
    a.mergeRules(layer_a);
    REQUIRE(!a.matchedRules()[0].get(StyleParamKey::width, str)); REQUIRE(str == "");

    const SceneLayer layer_b = { "b", Filter(), { instance_b() }, {} };
    DrawRuleMergeSet b;
    b.mergeRules(layer_b);
    REQUIRE(!b.matchedRules()[0].get(StyleParamKey::join, str)); REQUIRE(str == "");

    const SceneLayer layer_c = { "c", Filter(), { instance_c() }, {} };
    DrawRuleMergeSet c;
    c.mergeRules(layer_c);
    REQUIRE(!c.matchedRules()[0].get(StyleParamKey::order, str)); REQUIRE(str == "");

}
<commit_msg>Fix a probable segfault in DrawRuleTests<commit_after>#include "catch.hpp"

#include "scene/drawRule.h"
#include "scene/sceneLayer.h"
#include "platform.h"

#include <cstdio>
#include <algorithm>

using namespace Tangram;

// Functions to initialize DrawRule instances
const int dg1 = 0;
const int dg2 = 1;

DrawRuleData instance_a() {

    std::vector<StyleParam> params = {
        { StyleParamKey::order, "value_0a" },
        { StyleParamKey::join, "value_4a" },
        { StyleParamKey::color, "value_1a" }
    };

    return { "dg1", dg1, std::move(params) };

}

DrawRuleData instance_b() {

    std::vector<StyleParam> params = {
        { StyleParamKey::order, "value_0b" },
        { StyleParamKey::width, "value_2b" },
        { StyleParamKey::color, "value_1b" },
        { StyleParamKey::cap, "value_3b" },
        { StyleParamKey::style, "value_4b" }
    };

    return { "dg1", dg1, std::move(params) };

}

DrawRuleData instance_c() {

    std::vector<StyleParam> params = {};

    // changed from dg2 - styles will not be merged otherwise
    return { "dg1", dg1, params };

}

TEST_CASE("DrawRule correctly merges with another DrawRule", "[DrawRule]") {

    const SceneLayer layer_a = { "a", Filter(), { instance_a() }, {} };
    const SceneLayer layer_b = { "b", Filter(), { instance_b() }, {} };
    const SceneLayer layer_c = { "c", Filter(), { instance_c() }, {} };

    // For parameters contained in multiple rules, the parameter from the last rule
    // (by lexicographical order) should result.
    {
        DrawRuleMergeSet ruleSet;
        ruleSet.mergeRules(layer_a);

        REQUIRE(ruleSet.matchedRules().size() == 1);
        auto& merged_ab = ruleSet.matchedRules()[0];

        for (size_t i = 0; i < StyleParamKeySize; i++) {
            if (!merged_ab.active[i]) {
                continue;
            }
            auto* param = merged_ab.params[i].param;
            if (!param) {
                logMsg("param : none %d\n", i);
                continue;
            }
            logMsg("param : %s\n", param->toString().c_str());
        }

        ruleSet.mergeRules(layer_b);

        REQUIRE(ruleSet.matchedRules().size() == 1);

        // printf("rule_a:\n %s", rule_a.toString().c_str());
        // printf("rule_c:\n %s", rule_c.toString().c_str());
        // printf("merged_ac:\n %s", merged_ac.toString().c_str());
        for (size_t i = 0; i < StyleParamKeySize; i++) {
            if (!merged_ab.active[i]) {
                continue;
            }
            auto* param = merged_ab.params[i].param;
            if (!param) {
                logMsg("param : none %d\n", i);
                continue;
            }
            logMsg("param : %s\n", param->toString().c_str());
        }
        REQUIRE(merged_ab.findParameter(StyleParamKey::cap).key == StyleParamKey::cap);
        REQUIRE(merged_ab.findParameter(StyleParamKey::cap).value.get<std::string>() == "value_3b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::color).key == StyleParamKey::color);
        REQUIRE(merged_ab.findParameter(StyleParamKey::color).value.get<std::string>() == "value_1b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::join).key == StyleParamKey::join);
        REQUIRE(merged_ab.findParameter(StyleParamKey::join).value.get<std::string>() == "value_4a");
        REQUIRE(merged_ab.findParameter(StyleParamKey::order).key == StyleParamKey::order);
        REQUIRE(merged_ab.findParameter(StyleParamKey::order).value.get<std::string>() == "value_0b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::style).key == StyleParamKey::style);
        REQUIRE(merged_ab.findParameter(StyleParamKey::style).value.get<std::string>() == "value_4b");
        REQUIRE(merged_ab.findParameter(StyleParamKey::width).key == StyleParamKey::width);
        REQUIRE(merged_ab.findParameter(StyleParamKey::width).value.get<std::string>() == "value_2b");

        // explicit style wins
        REQUIRE(merged_ab.getStyleName() == "value_4b");
    }

    {
        DrawRuleMergeSet ruleSet;
        ruleSet.mergeRules(layer_b);
        ruleSet.mergeRules(layer_a);

        REQUIRE(ruleSet.matchedRules().size() == 1);

        auto& merged_ba = ruleSet.matchedRules()[0];

        REQUIRE(merged_ba.findParameter(StyleParamKey::cap).key == StyleParamKey::cap);
        REQUIRE(merged_ba.findParameter(StyleParamKey::cap).value.get<std::string>() == "value_3b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::color).key == StyleParamKey::color);
        REQUIRE(merged_ba.findParameter(StyleParamKey::color).value.get<std::string>() == "value_1b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::join).key == StyleParamKey::join);
        REQUIRE(merged_ba.findParameter(StyleParamKey::join).value.get<std::string>() == "value_4a");
        REQUIRE(merged_ba.findParameter(StyleParamKey::order).key == StyleParamKey::order);
        REQUIRE(merged_ba.findParameter(StyleParamKey::order).value.get<std::string>() == "value_0b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::style).key == StyleParamKey::style);
        REQUIRE(merged_ba.findParameter(StyleParamKey::style).value.get<std::string>() == "value_4b");
        REQUIRE(merged_ba.findParameter(StyleParamKey::width).key == StyleParamKey::width);
        REQUIRE(merged_ba.findParameter(StyleParamKey::width).value.get<std::string>() == "value_2b");

        // explicit style wins
        REQUIRE(merged_ba.getStyleName() == "value_4b");
    }

    {
        DrawRuleMergeSet ruleSet;
        ruleSet.mergeRules(layer_c);
        ruleSet.mergeRules(layer_b);

        REQUIRE(ruleSet.matchedRules().size() == 1);

        auto& merged_bc = ruleSet.matchedRules()[0];

        // for (size_t i = 0; i < StyleParamKeySize; i++) {
        //     auto* param = merged_bc.params[i];
        //     if (!param) { continue; }
        //     REQUIRE(param->key == rule_b[0].parameters[i].key);
        //     REQUIRE(param->value.get<std::string>() ==
        //             rule_b[0].parameters[i].value.get<std::string>());
        // }

        // explicit style wins
        REQUIRE(merged_bc.getStyleName() == "value_4b");
    }

}

TEST_CASE("DrawRule locates and outputs a parameter that it contains", "[DrawRule]") {

    std::string str;

    const SceneLayer layer_a = { "a", Filter(), { instance_a() }, {} };
    const SceneLayer layer_b = { "b", Filter(), { instance_b() }, {} };

    DrawRuleMergeSet a;
    a.mergeRules(layer_a);
    auto& rule_a = a.matchedRules()[0];

    REQUIRE(rule_a.get(StyleParamKey::order, str)); REQUIRE(str == "value_0a");
    REQUIRE(rule_a.get(StyleParamKey::color, str)); REQUIRE(str == "value_1a");
    REQUIRE(rule_a.get(StyleParamKey::join, str)); REQUIRE(str == "value_4a");

    DrawRuleMergeSet b;
    b.mergeRules(layer_b);
    auto& rule_b = b.matchedRules()[0];

    REQUIRE(rule_b.get(StyleParamKey::color, str)); REQUIRE(str == "value_1b");
    REQUIRE(rule_b.get(StyleParamKey::width, str)); REQUIRE(str == "value_2b");
    REQUIRE(rule_b.get(StyleParamKey::cap, str)); REQUIRE(str == "value_3b");
    REQUIRE(rule_b.get(StyleParamKey::order, str)); REQUIRE(str == "value_0b");
}

TEST_CASE("DrawRule correctly reports that it doesn't contain a parameter", "[DrawRule]") {
    std::string str;

    const SceneLayer layer_a = { "a", Filter(), { instance_a() }, {} };
    DrawRuleMergeSet a;
    a.mergeRules(layer_a);
    REQUIRE(!a.matchedRules()[0].get(StyleParamKey::width, str)); REQUIRE(str == "");

    const SceneLayer layer_b = { "b", Filter(), { instance_b() }, {} };
    DrawRuleMergeSet b;
    b.mergeRules(layer_b);
    REQUIRE(!b.matchedRules()[0].get(StyleParamKey::join, str)); REQUIRE(str == "");

    const SceneLayer layer_c = { "c", Filter(), { instance_c() }, {} };
    DrawRuleMergeSet c;
    c.mergeRules(layer_c);
    REQUIRE(!c.matchedRules()[0].get(StyleParamKey::order, str)); REQUIRE(str == "");

}
<|endoftext|>


<commit_before>#include <tst/set.hpp>
#include <tst/check.hpp>

#include <utki/singleton.hpp>

namespace{
class test_singleton : public utki::singleton<test_singleton>{
public:
	int a = 13;
};
}

namespace{
tst::set set("singleton", [](tst::suite& suite){
	suite.add(
		"only_one_singleton_instance_can_exist_at_a_time",
		tst::flag::no_parallel,
		[]{
			test_singleton sing1;

			try{
				test_singleton sing2;
				tst::check(false, SL) << "creating second singleton object should throw";
			}catch(std::logic_error&){}
		}
	);
});
}
<commit_msg>fix msvc build<commit_after>#include <tst/set.hpp>
#include <tst/check.hpp>

#include <utki/config.hpp>
#include <utki/singleton.hpp>

#if M_COMPILER != M_COMPILER_MSVC

namespace{
class test_singleton : public utki::singleton<test_singleton>{
public:
	int a = 13;
};
}

namespace{
tst::set set("singleton", [](tst::suite& suite){
	suite.add(
		"only_one_singleton_instance_can_exist_at_a_time",
		tst::flag::no_parallel,
		[]{
			test_singleton sing1;

			try{
				test_singleton sing2;
				tst::check(false, SL) << "creating second singleton object should throw";
			}catch(std::logic_error&){}
		}
	);
});
}
#endif // ~msvc compiler
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include "oox/ole/vbamodule.hxx"
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/script/ModuleInfo.hpp>
#include <com/sun/star/script/ModuleType.hpp>
#include <com/sun/star/script/XVBAModuleInfo.hpp>
#include "oox/helper/binaryinputstream.hxx"
#include "oox/helper/storagebase.hxx"
#include "oox/helper/textinputstream.hxx"
#include "oox/ole/vbahelper.hxx"
#include "oox/ole/vbainputstream.hxx"

using ::rtl::OUString;
using ::rtl::OUStringBuffer;
using ::com::sun::star::container::XNameAccess;
using ::com::sun::star::container::XNameContainer;
using ::com::sun::star::frame::XModel;
using ::com::sun::star::script::ModuleInfo;
using ::com::sun::star::script::XVBAModuleInfo;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;

namespace ApiModuleType = ::com::sun::star::script::ModuleType;

namespace oox {
namespace ole {

// ============================================================================

VbaModule::VbaModule( const Reference< XModel >& rxDocModel, const OUString& rName, rtl_TextEncoding eTextEnc, bool bExecutable ) :
    mxDocModel( rxDocModel ),
    maName( rName ),
    meTextEnc( eTextEnc ),
    mnType( ApiModuleType::Unknown ),
    mnOffset( SAL_MAX_UINT32 ),
    mbReadOnly( false ),
    mbPrivate( false ),
    mbExecutable( bExecutable )
{
}

void VbaModule::importDirRecords( BinaryInputStream& rDirStrm )
{
    sal_uInt16 nRecId = 0;
    StreamDataSequence aRecData;
    while( VbaHelper::readDirRecord( nRecId, aRecData, rDirStrm ) && (nRecId != VBA_ID_MODULEEND) )
    {
        SequenceInputStream aRecStrm( aRecData );
        sal_Int32 nRecSize = aRecData.getLength();
        switch( nRecId )
        {
#define OOX_ENSURE_RECORDSIZE( cond ) OSL_ENSURE( cond, "VbaModule::importDirRecords - invalid record size" )
            case VBA_ID_MODULENAME:
                OSL_ENSURE( false, "VbaModule::importDirRecords - unexpected MODULENAME record" );
                maName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc );
            break;
            case VBA_ID_MODULENAMEUNICODE:
            break;
            case VBA_ID_MODULESTREAMNAME:
                maStreamName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc );
            break;
            case VBA_ID_MODULESTREAMNAMEUNICODE:
            break;
            case VBA_ID_MODULEDOCSTRING:
                maDocString = aRecStrm.readCharArrayUC( nRecSize, meTextEnc );
            break;
            case VBA_ID_MODULEDOCSTRINGUNICODE:
            break;
            case VBA_ID_MODULEOFFSET:
                OOX_ENSURE_RECORDSIZE( nRecSize == 4 );
                aRecStrm >> mnOffset;
            break;
            case VBA_ID_MODULEHELPCONTEXT:
                OOX_ENSURE_RECORDSIZE( nRecSize == 4 );
            break;
            case VBA_ID_MODULECOOKIE:
                OOX_ENSURE_RECORDSIZE( nRecSize == 2 );
            break;
            case VBA_ID_MODULETYPEPROCEDURAL:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                OSL_ENSURE( mnType == ApiModuleType::Unknown, "VbaModule::importDirRecords - multiple module type records" );
                mnType = ApiModuleType::Normal;
            break;
            case VBA_ID_MODULETYPEDOCUMENT:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                OSL_ENSURE( mnType == ApiModuleType::Unknown, "VbaModule::importDirRecords - multiple module type records" );
                mnType = ApiModuleType::Document;
            break;
            case VBA_ID_MODULEREADONLY:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                mbReadOnly = true;
            break;
            case VBA_ID_MODULEPRIVATE:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                mbPrivate = true;
            break;
            default:
                OSL_ENSURE( false, "VbaModule::importDirRecords - unknown module record" );
#undef OOX_ENSURE_RECORDSIZE
        }
    }
    OSL_ENSURE( maName.getLength() > 0, "VbaModule::importDirRecords - missing module name" );
    OSL_ENSURE( maStreamName.getLength() > 0, "VbaModule::importDirRecords - missing module stream name" );
    OSL_ENSURE( mnType != ApiModuleType::Unknown, "VbaModule::importDirRecords - missing module type" );
    OSL_ENSURE( mnOffset < SAL_MAX_UINT32, "VbaModule::importDirRecords - missing module stream offset" );
}

void VbaModule::importSourceCode( StorageBase& rVbaStrg,
        const Reference< XNameContainer >& rxBasicLib, const Reference< XNameAccess >& rxDocObjectNA ) const
{
    if( (maName.getLength() == 0) || (maStreamName.getLength() == 0) || (mnOffset == SAL_MAX_UINT32) )
        return;

    BinaryXInputStream aInStrm( rVbaStrg.openInputStream( maStreamName ), true );
    OSL_ENSURE( !aInStrm.isEof(), "VbaModule::importSourceCode - cannot open module stream" );
    // skip the 'performance cache' stored before the actual source code
    aInStrm.seek( mnOffset );
    // if stream is still valid, load the source code
    if( aInStrm.isEof() )
        return;

    // prepare the Basic module
    ModuleInfo aModuleInfo;
    aModuleInfo.ModuleType = mnType;
    OUStringBuffer aSourceCode;
    aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Rem Attribute VBA_ModuleType=" ) );
    switch( mnType )
    {
        case ApiModuleType::Normal:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAModule" ) );
        break;
        case ApiModuleType::Class:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAClassModule" ) );
        break;
        case ApiModuleType::Form:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAFormModule" ) );
            // hack from old filter, document Basic should know the XModel, but it doesn't
            aModuleInfo.ModuleObject.set( mxDocModel, UNO_QUERY );
        break;
        case ApiModuleType::Document:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBADocumentModule" ) );
            // get the VBA object associated to the document module
            if( rxDocObjectNA.is() ) try
            {
                aModuleInfo.ModuleObject.set( rxDocObjectNA->getByName( maName ), UNO_QUERY );
            }
            catch( Exception& )
            {
            }
        break;
        default:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAUnknown" ) );
    }
    aSourceCode.append( sal_Unicode( '\n' ) );
    if( mbExecutable )
    {
        aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Option VBASupport 1\n" ) );
        if( mnType == ApiModuleType::Class )
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Option ClassModule\n" ) );
    }
    else
    {
        // add a subroutine named after the module itself
        aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Sub " ) ).
            append( maName.replace( ' ', '_' ) ).append( sal_Unicode( '\n' ) );
    }

    // decompression starts at current stream position of aInStrm
    VbaInputStream aVbaStrm( aInStrm );
    // load the source code line-by-line, with some more processing
    TextInputStream aVbaTextStrm( aVbaStrm, meTextEnc );
    while( !aVbaTextStrm.isEof() )
    {
        OUString aCodeLine = aVbaTextStrm.readLine();
        // skip all 'Attribute' statements
        if( !aCodeLine.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "Attribute " ) ) )
        {
            if( !mbExecutable )
                aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Rem " ) );
            aSourceCode.append( aCodeLine ).append( sal_Unicode( '\n' ) );
        }
    }

    // close the subroutine named after the module
    if( !mbExecutable )
        aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "End Sub\n" ) );

    // insert the module into the passed Basic library
    try
    {
        rxBasicLib->insertByName( maName, Any( aSourceCode.makeStringAndClear() ) );
    }
    catch( Exception& )
    {
        OSL_ENSURE( false, "VbaModule::importSourceCode - cannot insert module into library" );
    }

    // insert extended module info
    try
    {
        Reference< XVBAModuleInfo > xVBAModuleInfo( rxBasicLib, UNO_QUERY_THROW );
        xVBAModuleInfo->insertModuleInfo( maName, aModuleInfo );
    }
    catch( Exception& )
    {
    }
}

// ============================================================================

} // namespace ole
} // namespace oox
<commit_msg>npower13_objectmodules: fix order of XModuleInfo/Module creation order<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include "oox/ole/vbamodule.hxx"
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/script/ModuleInfo.hpp>
#include <com/sun/star/script/ModuleType.hpp>
#include <com/sun/star/script/XVBAModuleInfo.hpp>
#include "oox/helper/binaryinputstream.hxx"
#include "oox/helper/storagebase.hxx"
#include "oox/helper/textinputstream.hxx"
#include "oox/ole/vbahelper.hxx"
#include "oox/ole/vbainputstream.hxx"

using ::rtl::OUString;
using ::rtl::OUStringBuffer;
using ::com::sun::star::container::XNameAccess;
using ::com::sun::star::container::XNameContainer;
using ::com::sun::star::frame::XModel;
using ::com::sun::star::script::ModuleInfo;
using ::com::sun::star::script::XVBAModuleInfo;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;

namespace ApiModuleType = ::com::sun::star::script::ModuleType;

namespace oox {
namespace ole {

// ============================================================================

VbaModule::VbaModule( const Reference< XModel >& rxDocModel, const OUString& rName, rtl_TextEncoding eTextEnc, bool bExecutable ) :
    mxDocModel( rxDocModel ),
    maName( rName ),
    meTextEnc( eTextEnc ),
    mnType( ApiModuleType::Unknown ),
    mnOffset( SAL_MAX_UINT32 ),
    mbReadOnly( false ),
    mbPrivate( false ),
    mbExecutable( bExecutable )
{
}

void VbaModule::importDirRecords( BinaryInputStream& rDirStrm )
{
    sal_uInt16 nRecId = 0;
    StreamDataSequence aRecData;
    while( VbaHelper::readDirRecord( nRecId, aRecData, rDirStrm ) && (nRecId != VBA_ID_MODULEEND) )
    {
        SequenceInputStream aRecStrm( aRecData );
        sal_Int32 nRecSize = aRecData.getLength();
        switch( nRecId )
        {
#define OOX_ENSURE_RECORDSIZE( cond ) OSL_ENSURE( cond, "VbaModule::importDirRecords - invalid record size" )
            case VBA_ID_MODULENAME:
                OSL_ENSURE( false, "VbaModule::importDirRecords - unexpected MODULENAME record" );
                maName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc );
            break;
            case VBA_ID_MODULENAMEUNICODE:
            break;
            case VBA_ID_MODULESTREAMNAME:
                maStreamName = aRecStrm.readCharArrayUC( nRecSize, meTextEnc );
            break;
            case VBA_ID_MODULESTREAMNAMEUNICODE:
            break;
            case VBA_ID_MODULEDOCSTRING:
                maDocString = aRecStrm.readCharArrayUC( nRecSize, meTextEnc );
            break;
            case VBA_ID_MODULEDOCSTRINGUNICODE:
            break;
            case VBA_ID_MODULEOFFSET:
                OOX_ENSURE_RECORDSIZE( nRecSize == 4 );
                aRecStrm >> mnOffset;
            break;
            case VBA_ID_MODULEHELPCONTEXT:
                OOX_ENSURE_RECORDSIZE( nRecSize == 4 );
            break;
            case VBA_ID_MODULECOOKIE:
                OOX_ENSURE_RECORDSIZE( nRecSize == 2 );
            break;
            case VBA_ID_MODULETYPEPROCEDURAL:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                OSL_ENSURE( mnType == ApiModuleType::Unknown, "VbaModule::importDirRecords - multiple module type records" );
                mnType = ApiModuleType::Normal;
            break;
            case VBA_ID_MODULETYPEDOCUMENT:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                OSL_ENSURE( mnType == ApiModuleType::Unknown, "VbaModule::importDirRecords - multiple module type records" );
                mnType = ApiModuleType::Document;
            break;
            case VBA_ID_MODULEREADONLY:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                mbReadOnly = true;
            break;
            case VBA_ID_MODULEPRIVATE:
                OOX_ENSURE_RECORDSIZE( nRecSize == 0 );
                mbPrivate = true;
            break;
            default:
                OSL_ENSURE( false, "VbaModule::importDirRecords - unknown module record" );
#undef OOX_ENSURE_RECORDSIZE
        }
    }
    OSL_ENSURE( maName.getLength() > 0, "VbaModule::importDirRecords - missing module name" );
    OSL_ENSURE( maStreamName.getLength() > 0, "VbaModule::importDirRecords - missing module stream name" );
    OSL_ENSURE( mnType != ApiModuleType::Unknown, "VbaModule::importDirRecords - missing module type" );
    OSL_ENSURE( mnOffset < SAL_MAX_UINT32, "VbaModule::importDirRecords - missing module stream offset" );
}

void VbaModule::importSourceCode( StorageBase& rVbaStrg,
        const Reference< XNameContainer >& rxBasicLib, const Reference< XNameAccess >& rxDocObjectNA ) const
{
    if( (maName.getLength() == 0) || (maStreamName.getLength() == 0) || (mnOffset == SAL_MAX_UINT32) )
        return;

    BinaryXInputStream aInStrm( rVbaStrg.openInputStream( maStreamName ), true );
    OSL_ENSURE( !aInStrm.isEof(), "VbaModule::importSourceCode - cannot open module stream" );
    // skip the 'performance cache' stored before the actual source code
    aInStrm.seek( mnOffset );
    // if stream is still valid, load the source code
    if( aInStrm.isEof() )
        return;

    // prepare the Basic module
    ModuleInfo aModuleInfo;
    aModuleInfo.ModuleType = mnType;
    OUStringBuffer aSourceCode;
    aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Rem Attribute VBA_ModuleType=" ) );
    switch( mnType )
    {
        case ApiModuleType::Normal:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAModule" ) );
        break;
        case ApiModuleType::Class:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAClassModule" ) );
        break;
        case ApiModuleType::Form:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAFormModule" ) );
            // hack from old filter, document Basic should know the XModel, but it doesn't
            aModuleInfo.ModuleObject.set( mxDocModel, UNO_QUERY );
        break;
        case ApiModuleType::Document:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBADocumentModule" ) );
            // get the VBA object associated to the document module
            if( rxDocObjectNA.is() ) try
            {
                aModuleInfo.ModuleObject.set( rxDocObjectNA->getByName( maName ), UNO_QUERY );
            }
            catch( Exception& )
            {
            }
        break;
        default:
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "VBAUnknown" ) );
    }
    aSourceCode.append( sal_Unicode( '\n' ) );
    if( mbExecutable )
    {
        aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Option VBASupport 1\n" ) );
        if( mnType == ApiModuleType::Class )
            aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Option ClassModule\n" ) );
    }
    else
    {
        // add a subroutine named after the module itself
        aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Sub " ) ).
            append( maName.replace( ' ', '_' ) ).append( sal_Unicode( '\n' ) );
    }

    // decompression starts at current stream position of aInStrm
    VbaInputStream aVbaStrm( aInStrm );
    // load the source code line-by-line, with some more processing
    TextInputStream aVbaTextStrm( aVbaStrm, meTextEnc );
    while( !aVbaTextStrm.isEof() )
    {
        OUString aCodeLine = aVbaTextStrm.readLine();
        // skip all 'Attribute' statements
        if( !aCodeLine.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "Attribute " ) ) )
        {
            if( !mbExecutable )
                aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "Rem " ) );
            aSourceCode.append( aCodeLine ).append( sal_Unicode( '\n' ) );
        }
    }

    // close the subroutine named after the module
    if( !mbExecutable )
        aSourceCode.appendAscii( RTL_CONSTASCII_STRINGPARAM( "End Sub\n" ) );

    // insert extended module info
    try
    {
        Reference< XVBAModuleInfo > xVBAModuleInfo( rxBasicLib, UNO_QUERY_THROW );
        xVBAModuleInfo->insertModuleInfo( maName, aModuleInfo );
    }
    catch( Exception& )
    {
    }

    // insert the module into the passed Basic library
    try
    {
        rxBasicLib->insertByName( maName, Any( aSourceCode.makeStringAndClear() ) );
    }
    catch( Exception& )
    {
        OSL_ENSURE( false, "VbaModule::importSourceCode - cannot insert module into library" );
    }
}

// ============================================================================

} // namespace ole
} // namespace oox
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2011-2012, John Haddon. All rights reserved.
//  Copyright (c) 2011-2015, 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 "ActionBinding.h"
#include "AnimationBinding.h"
#include "ApplicationRootBinding.h"
#include "ArrayPlugBinding.h"
#include "BoxPlugBinding.h"
#include "CompoundDataPlugBinding.h"
#include "CompoundNumericPlugBinding.h"
#include "ContextBinding.h"
#include "ContextProcessorBinding.h"
#include "DirtyPropagationScopeBinding.h"
#include "DotBinding.h"
#include "ExpressionBinding.h"
#include "GraphComponentBinding.h"
#include "ProcessMessageHandlerBinding.h"
#include "MetadataAlgoBinding.h"
#include "MetadataBinding.h"
#include "MonitorBinding.h"
#include "NodeAlgoBinding.h"
#include "NodeBinding.h"
#include "NumericPlugBinding.h"
#include "ParallelAlgoBinding.h"
#include "PathBinding.h"
#include "PathFilterBinding.h"
#include "PlugAlgoBinding.h"
#include "PlugBinding.h"
#include "ProcessBinding.h"
#include "RandomBinding.h"
#include "ScriptNodeBinding.h"
#include "SerialisationBinding.h"
#include "SetBinding.h"
#include "SignalsBinding.h"
#include "SplinePlugBinding.h"
#include "SpreadsheetBinding.h"
#include "StringPlugBinding.h"
#include "SubGraphBinding.h"
#include "SwitchBinding.h"
#include "Transform2DPlugBinding.h"
#include "TransformPlugBinding.h"
#include "TypedObjectPlugBinding.h"
#include "TypedPlugBinding.h"
#include "UndoScopeBinding.h"
#include "ValuePlugBinding.h"
#include "NameValuePlugBinding.h"
#include "ShufflesBinding.h"
#include "MessagesBinding.h"

#include "GafferBindings/DependencyNodeBinding.h"

#include "Gaffer/Backdrop.h"

#ifdef __linux__
#include <sys/prctl.h>
#endif

using namespace boost::python;
using namespace Gaffer;
using namespace GafferModule;
using namespace GafferBindings;

namespace
{

bool isDebug()
{
#ifdef NDEBUG
	return false;
#else
	return true;
#endif
}

int g_argc = 0;
char **g_argv = nullptr;

int storeArgcArgv( int argc, char **argv, char **env )
{
	g_argc = argc;
	g_argv = argv;
	return 0;
}

void clobberArgv()
{
	if( g_argc < 2 )
	{
		return;
	}

	// A typical command line looks like this :
	//
	// `gaffer arg1 arg2 arg3`
	//
	// But will look like this once the wrapper
	// has launched Gaffer via Python :
	//
	// `python gaffer.py arg1 arg2 arg3`
	//
	// Replace the `python` bit with `gaffer` and
	// shuffle all the arguments around so that
	// the `gaffer.py` argument disappears and we
	// get back to the original.
	char *end = g_argv[g_argc-1] + strlen( g_argv[g_argc-1] );
	strncpy( g_argv[0], "gaffer", strlen( g_argv[0] ) );
	strncpy( g_argv[1], "", strlen( g_argv[1] ) );
	char *emptyString = g_argv[1];
	for( int i = 1; i < g_argc - 1; ++i )
	{
		g_argv[i] = g_argv[i+1];
	}
	g_argv[g_argc-1] = emptyString;

	// We've just shuffled the pointers so far, but
	// in practice the original strings were contiguous
	// in the same chunk of memory, and `ps` uses that fact
	// rather than actually use the argv pointers. See
	// https://stackoverflow.com/a/23400588.
	//
	// Pack everything back down so `ps` sees what it
	// expects.
	char *c = g_argv[0];
	for( int i = 0; i < g_argc - 1; ++i )
	{
		const size_t l = strlen( g_argv[i] ) + 1;
		memmove( c, g_argv[i], l );
		g_argv[i] = c;
		c += l;
	}
	g_argv[g_argc-1] = c;
	memset( c, 0, end - c );
}

void nameProcess()
{
	// Some things (for instance, `ps` in default mode) look at `argv` to get
	// the name.
	clobberArgv();
	// Others (for instance, `top` in default mode) use other methods.
	// Cater to everyone as best we can.
#ifdef __linux__
	prctl( PR_SET_NAME, "gaffer", 0, 0, 0 );
#endif
}

} // namespace

// Arrange for `storeArgcArgv()` to be called when our module loads,
// so we can stash the original values for `argc` and `argv`.
// In Python 2 we could simply use `Py_GetArgcArgv()` instead, but
// in Python 3 that gives us a mangled copy which is of no use.
#if  defined( __APPLE__ )
__attribute__( ( section( "__DATA,__mod_init_func" ) ) ) decltype( storeArgcArgv ) *g_initArgcArgv = storeArgcArgv;
#elif defined( __linux__ )
__attribute__( ( section( ".init_array" ) ) ) decltype( storeArgcArgv ) *g_initArgcArgv = storeArgcArgv;
#endif

BOOST_PYTHON_MODULE( _Gaffer )
{

	bindSignals();
	bindGraphComponent();
	bindContext();
	bindSerialisation();
	bindNode();
	bindPlug();
	bindValuePlug();
	bindNumericPlug();
	bindTypedPlug();
	bindStringPlug();
	bindTypedObjectPlug();
	bindScriptNode();
	bindApplicationRoot();
	bindSet();
	bindDirtyPropagationScope();
	bindUndoScope();
	bindCompoundNumericPlug();
	bindSplinePlug();
	bindBoxPlug();
	bindExpression();
	bindTransformPlug();
	bindTransform2DPlug();
	bindCompoundDataPlug();
	bindRandom();
	bindSubGraph();
	bindAction();
	bindArrayPlug();
	bindMetadata();
	bindDot();
	bindPath();
	bindPathFilter();
	bindAnimation();
	bindMonitor();
	bindMetadataAlgo();
	bindSwitch();
	bindPlugAlgo();
	bindParallelAlgo();
	bindContextProcessor();
	bindProcessMessageHandler();
	bindNameValuePlug();
	bindProcess();
	bindSpreadsheet();
	bindNodeAlgo();
	bindShuffles();
	bindMessages();

	NodeClass<Backdrop>();

	def( "isDebug", &isDebug );

	def( "_nameProcess", &nameProcess );

	// Various parts of gaffer create new threads from C++, and those
	// threads may call back into Python via wrapped classes at any time.
	// We must prepare Python for this by calling PyEval_InitThreads().

	PyEval_InitThreads();

}
<commit_msg>GafferModule : Restrict process naming to Linux and Mac<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2011-2012, John Haddon. All rights reserved.
//  Copyright (c) 2011-2015, 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 "ActionBinding.h"
#include "AnimationBinding.h"
#include "ApplicationRootBinding.h"
#include "ArrayPlugBinding.h"
#include "BoxPlugBinding.h"
#include "CompoundDataPlugBinding.h"
#include "CompoundNumericPlugBinding.h"
#include "ContextBinding.h"
#include "ContextProcessorBinding.h"
#include "DirtyPropagationScopeBinding.h"
#include "DotBinding.h"
#include "ExpressionBinding.h"
#include "GraphComponentBinding.h"
#include "ProcessMessageHandlerBinding.h"
#include "MetadataAlgoBinding.h"
#include "MetadataBinding.h"
#include "MonitorBinding.h"
#include "NodeAlgoBinding.h"
#include "NodeBinding.h"
#include "NumericPlugBinding.h"
#include "ParallelAlgoBinding.h"
#include "PathBinding.h"
#include "PathFilterBinding.h"
#include "PlugAlgoBinding.h"
#include "PlugBinding.h"
#include "ProcessBinding.h"
#include "RandomBinding.h"
#include "ScriptNodeBinding.h"
#include "SerialisationBinding.h"
#include "SetBinding.h"
#include "SignalsBinding.h"
#include "SplinePlugBinding.h"
#include "SpreadsheetBinding.h"
#include "StringPlugBinding.h"
#include "SubGraphBinding.h"
#include "SwitchBinding.h"
#include "Transform2DPlugBinding.h"
#include "TransformPlugBinding.h"
#include "TypedObjectPlugBinding.h"
#include "TypedPlugBinding.h"
#include "UndoScopeBinding.h"
#include "ValuePlugBinding.h"
#include "NameValuePlugBinding.h"
#include "ShufflesBinding.h"
#include "MessagesBinding.h"

#include "GafferBindings/DependencyNodeBinding.h"

#include "Gaffer/Backdrop.h"

#ifdef __linux__
#include <sys/prctl.h>
#endif

using namespace boost::python;
using namespace Gaffer;
using namespace GafferModule;
using namespace GafferBindings;

namespace
{

bool isDebug()
{
#ifdef NDEBUG
	return false;
#else
	return true;
#endif
}

#ifndef _MSC_VER

int g_argc = 0;
char **g_argv = nullptr;

int storeArgcArgv( int argc, char **argv, char **env )
{
	g_argc = argc;
	g_argv = argv;
	return 0;
}

void clobberArgv()
{
	if( g_argc < 2 )
	{
		return;
	}

	// A typical command line looks like this :
	//
	// `gaffer arg1 arg2 arg3`
	//
	// But will look like this once the wrapper
	// has launched Gaffer via Python :
	//
	// `python gaffer.py arg1 arg2 arg3`
	//
	// Replace the `python` bit with `gaffer` and
	// shuffle all the arguments around so that
	// the `gaffer.py` argument disappears and we
	// get back to the original.
	char *end = g_argv[g_argc-1] + strlen( g_argv[g_argc-1] );
	strncpy( g_argv[0], "gaffer", strlen( g_argv[0] ) );
	strncpy( g_argv[1], "", strlen( g_argv[1] ) );
	char *emptyString = g_argv[1];
	for( int i = 1; i < g_argc - 1; ++i )
	{
		g_argv[i] = g_argv[i+1];
	}
	g_argv[g_argc-1] = emptyString;

	// We've just shuffled the pointers so far, but
	// in practice the original strings were contiguous
	// in the same chunk of memory, and `ps` uses that fact
	// rather than actually use the argv pointers. See
	// https://stackoverflow.com/a/23400588.
	//
	// Pack everything back down so `ps` sees what it
	// expects.
	char *c = g_argv[0];
	for( int i = 0; i < g_argc - 1; ++i )
	{
		const size_t l = strlen( g_argv[i] ) + 1;
		memmove( c, g_argv[i], l );
		g_argv[i] = c;
		c += l;
	}
	g_argv[g_argc-1] = c;
	memset( c, 0, end - c );
}
#endif

void nameProcess()
{
	// Some things (for instance, `ps` in default mode) look at `argv` to get
	// the name.
#ifndef _MSC_VER
	clobberArgv();
#endif
	// Others (for instance, `top` in default mode) use other methods.
	// Cater to everyone as best we can.
#ifdef __linux__
	prctl( PR_SET_NAME, "gaffer", 0, 0, 0 );
#endif
}

} // namespace

// Arrange for `storeArgcArgv()` to be called when our module loads,
// so we can stash the original values for `argc` and `argv`.
// In Python 2 we could simply use `Py_GetArgcArgv()` instead, but
// in Python 3 that gives us a mangled copy which is of no use.
#if  defined( __APPLE__ )
__attribute__( ( section( "__DATA,__mod_init_func" ) ) ) decltype( storeArgcArgv ) *g_initArgcArgv = storeArgcArgv;
#elif defined( __linux__ )
__attribute__( ( section( ".init_array" ) ) ) decltype( storeArgcArgv ) *g_initArgcArgv = storeArgcArgv;
#endif

BOOST_PYTHON_MODULE( _Gaffer )
{

	bindSignals();
	bindGraphComponent();
	bindContext();
	bindSerialisation();
	bindNode();
	bindPlug();
	bindValuePlug();
	bindNumericPlug();
	bindTypedPlug();
	bindStringPlug();
	bindTypedObjectPlug();
	bindScriptNode();
	bindApplicationRoot();
	bindSet();
	bindDirtyPropagationScope();
	bindUndoScope();
	bindCompoundNumericPlug();
	bindSplinePlug();
	bindBoxPlug();
	bindExpression();
	bindTransformPlug();
	bindTransform2DPlug();
	bindCompoundDataPlug();
	bindRandom();
	bindSubGraph();
	bindAction();
	bindArrayPlug();
	bindMetadata();
	bindDot();
	bindPath();
	bindPathFilter();
	bindAnimation();
	bindMonitor();
	bindMetadataAlgo();
	bindSwitch();
	bindPlugAlgo();
	bindParallelAlgo();
	bindContextProcessor();
	bindProcessMessageHandler();
	bindNameValuePlug();
	bindProcess();
	bindSpreadsheet();
	bindNodeAlgo();
	bindShuffles();
	bindMessages();

	NodeClass<Backdrop>();

	def( "isDebug", &isDebug );

	def( "_nameProcess", &nameProcess );

	// Various parts of gaffer create new threads from C++, and those
	// threads may call back into Python via wrapped classes at any time.
	// We must prepare Python for this by calling PyEval_InitThreads().

	PyEval_InitThreads();

}
<|endoftext|>


<commit_before>#include <set>
#include "Game/Physics/MergeBuilder.h"

#ifdef DEBUG
//#define DEBUG_MAPSPEW
#endif
#ifdef DEBUG_MAPSPEW
#include <iostream>
#include <sstream>
#endif

using namespace Wulf;
using namespace Wulf::Physics;

bool notMergable(TileType type)
{
	return type == TileType::Door || type == TileType::Pickup;
}

MergeNode::MergeNode(Map::Node const& node)
	: topLeft(node.x, node.y), type(NodeToTile(node))
	, width(0), height(0)
	, done(false)
	, mTileData(nullptr)
{
}

bool MergeNode::compatible(MergeNode const* other) const
{
	if (other == nullptr || other->done || other->type != type || notMergable(other->type))
		return false;
	return true;
}

// This returns the tiledata for the merged mass.
// It is safe to call multiple times as it only returns one TileData.
TileData* MergeNode::toTile()
{
	if (mTileData != nullptr)
		return mTileData;
	coords topLeft = this->topLeft;
	int tlx = topLeft.x;
	topLeft.x -= width;
	coords bottomRight = coords(tlx, topLeft.y + height);
	mTileData = new TileData(topLeft, bottomRight, type);
	return mTileData;
}

std::tuple<std::string, std::string, std::string> MergeNode::toString(coords tile) const
{
#ifdef DEBUG_MAPSPEW
	std::string contents;
	if (type == TileType::Empty) {
		contents = ".";
	} else if (type == TileType::Pickup) {
		contents = "x";
	} else if (type == TileType::Sprite) {
		contents = "~";
	} else if (type == TileType::Door) {
		contents = "D";
	} else {
		contents = "#";
	}

	std::stringstream a, b, c;
	coords bottomRight(topLeft.x - width, topLeft.y + height);
	if (tile == topLeft) {
		a << "/-";
		b << "|";
	} else if (tile.x == topLeft.x) {
		a << "|" << contents;
		b << "|";
	} else if (tile.y == topLeft.y) {
		a << "--";
		b << contents;
	} else {
		a << contents << contents;
		b << contents;
	}
	b << contents;
	if (tile.y == topLeft.y) {
		if (tile.x == bottomRight.x) {
			a << "\\";
			b << "|";
		} else {
			a << "-";
			b << contents;
		}
	} else if (tile.x == bottomRight.x) {
		a << "|";
		b << "|";
	} else {
		a << contents;
		b << contents;
	}
	if (tile.x == topLeft.x) {
		if (tile.y == bottomRight.y) {
			c << "\\-";
		} else {
			c << "|" << contents;
		}
	} else if (tile.y == bottomRight.y) {
		c << "--";
	} else {
		c << contents << contents;
	}
	if (tile == bottomRight) {
		c << "/";
	} else if (tile.x == bottomRight.x) {
		c << "|";
	} else if (tile.y == bottomRight.y) {
		c << "-";
	} else {
		c << contents;
	}
	return std::make_tuple(a.str(), b.str(), c.str());
#else
	return std::make_tuple("", "", "");
#endif
}

MergeBuilder::MergeBuilder(Map::Map const& map)
{
	for (auto& xnodes : nodes)
		std::fill(xnodes.begin(), xnodes.end(), nullptr);
	loadMap(map);
	performMerges();
}
MergeBuilder::~MergeBuilder()
{
	// Use a set because we have duplicate pointers
	std::set<MergeNode*> pointrs;
	for (auto& xnodes : nodes) {
		pointrs.insert(xnodes.begin(), xnodes.end());
	}
	for (MergeNode* node : pointrs) {
		delete node;
	}
}

inline
void MergeBuilder::loadMap(Map::Map const& map)
{
	const auto& mapNodes = map.nodes;
	for (int x = 0; x < xsize; x++) {
		const auto& xnodes = mapNodes[x];
		for (int y = 0; y < ysize; y++) {
			const auto& node = xnodes[y];
			// Filter out walls the player can never hit
			if (node.wall && !node.visibleWall) {
				// Make sure corners are included
				byte i = 0;
				for (Map::Node *neighbour : node.neighbours) {
					if (neighbour != nullptr && neighbour->visibleWall) {
						i++;
					}
				}
				if (i < 2)
					continue;
			}
			nodes[x][y] = new MergeNode(node);
		}
	}
}
inline
int MergeBuilder::verticalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	while (y + i < ysize) { // Ensure we stay within bounds
		MergeNode *other = nodes[x][y + i];
		// Check if the node is the same kind as us. (includes a nullptr check)
		if (!node->compatible(other))
			break;

		// Eat the node
		node->height++;
		delete other;
		nodes[x][y + i] = node;

		i++;
	}
	return i - 1;
}

inline
void MergeBuilder::horizontalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	// Consume horizontally
	while (x + i < xsize) {
		// Make sure that we can consume all x tiles for our height
		for (int j = 0; j <= node->height; j++) {
			MergeNode *other = nodes[x + i][y + j];
			if (!node->compatible(other)) {
				return;
			}
		}
		node->width++;
		// Eat all nodes for our height
		for (int j = 0; j <= node->height; j++) {
			delete nodes[x + i][y + j];
			nodes[x + i][y + j] = node;
		}

		i++;
	}
}

inline
void MergeBuilder::performMerges()
{
	for (int x = 0; x < xsize; x++) {
		for (int y = 0; y < ysize; y++) {
			MergeNode *node = nodes[x][y];
			if (node == nullptr || node->done || notMergable(node->type))
				continue;
			// Merge vertically first due to how the loop is layed out.
			int yadd = verticalMerge(x, y);
			horizontalMerge(x, y);
			node->done = true;
			// yadd lets us skip consumed nodes for the next iteration of y.
			y += yadd;
		}
	}
}

std::vector<std::pair<coords, TileData*>> MergeBuilder::getTileData() const
{
	std::vector<std::pair<coords, TileData*>> ret;
	ret.reserve(xsize * ysize);
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	// SPAM
#ifdef DEBUG_MAPSPEW
	DebugOutput();
#endif

	for (coord x = 0; x < xsize; x++) {
		for (coord y = 0; y < ysize; y++) {
			MergeNode* node = nodes[x][y];
			if (node == nullptr)
				continue;
			coord nodeX = -(x - xhalf); // I don't know why this is negative, but it is.
			coord nodeY = y - yhalf;
			ret.emplace_back(coords(nodeX, nodeY), node->toTile());
		}
	}

	return ret;
}

void MergeBuilder::DebugOutput() const
{
#ifdef DEBUG_MAPSPEW
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	coord xcoord, ycoord;

	std::cout << " vim: nowrap listchars=" << std::endl;
	std::cout.setf(std::ios_base::right);
	std::cout.fill(' ');
	std::cout.width(3);
	std::cout << "  ";
	std::ostringstream bluh;
	bluh << "   ";
	for (byte x = 0; x < xsize; ++x) {
		std::cout.width(3);
		std::cout << -(x - xhalf);
		bluh << "***";
	}
	std::cout << std::endl;
	std::cout << bluh.str() << std::endl;
	for (byte y = 0; y < ysize; ++y) {
		ycoord = (y - yhalf);
		std::stringstream a, b, c;
		a << "   *";
		b.width(3);
		b << ycoord << '*';
		c << "   *";
		for (byte x = 0; x < xsize; ++x) {
			xcoord = -(x - xhalf);
			MergeNode *node = nodes[x][y];
			if (node == nullptr) {
				a << "   ";
				b << "   ";
				c << "   ";
				continue;
			}
			auto lines = node->toString(coords(xcoord, ycoord));
			a << std::get<0>(lines);
			b << std::get<1>(lines);
			c << std::get<2>(lines);
		}
		std::cout << a.str() << std::endl;
		std::cout << b.str() << std::endl;
		std::cout << c.str() << std::endl;
	}
#endif
}
<commit_msg>Remove pickups from the collision map<commit_after>#include <set>
#include "Game/Physics/MergeBuilder.h"

#ifdef DEBUG
//#define DEBUG_MAPSPEW
#endif
#ifdef DEBUG_MAPSPEW
#include <iostream>
#include <sstream>
#endif

using namespace Wulf;
using namespace Wulf::Physics;

bool notMergable(TileType type)
{
	return type == TileType::Door || type == TileType::Pickup;
}

MergeNode::MergeNode(Map::Node const& node)
	: topLeft(node.x, node.y), type(NodeToTile(node))
	, width(0), height(0)
	, done(false)
	, mTileData(nullptr)
{
	// For us, pickups don't exist
	if (type == TileType::Pickup)
		type = TileType::Empty;
}

bool MergeNode::compatible(MergeNode const* other) const
{
	if (other == nullptr || other->done || other->type != type || notMergable(other->type))
		return false;
	return true;
}

// This returns the tiledata for the merged mass.
// It is safe to call multiple times as it only returns one TileData.
TileData* MergeNode::toTile()
{
	if (mTileData != nullptr)
		return mTileData;
	coords topLeft = this->topLeft;
	int tlx = topLeft.x;
	topLeft.x -= width;
	coords bottomRight = coords(tlx, topLeft.y + height);
	mTileData = new TileData(topLeft, bottomRight, type);
	return mTileData;
}

std::tuple<std::string, std::string, std::string> MergeNode::toString(coords tile) const
{
#ifdef DEBUG_MAPSPEW
	std::string contents;
	if (type == TileType::Empty) {
		contents = ".";
	} else if (type == TileType::Pickup) {
		contents = "x";
	} else if (type == TileType::Sprite) {
		contents = "~";
	} else if (type == TileType::Door) {
		contents = "D";
	} else {
		contents = "#";
	}

	std::stringstream a, b, c;
	coords bottomRight(topLeft.x - width, topLeft.y + height);
	if (tile == topLeft) {
		a << "/-";
		b << "|";
	} else if (tile.x == topLeft.x) {
		a << "|" << contents;
		b << "|";
	} else if (tile.y == topLeft.y) {
		a << "--";
		b << contents;
	} else {
		a << contents << contents;
		b << contents;
	}
	b << contents;
	if (tile.y == topLeft.y) {
		if (tile.x == bottomRight.x) {
			a << "\\";
			b << "|";
		} else {
			a << "-";
			b << contents;
		}
	} else if (tile.x == bottomRight.x) {
		a << "|";
		b << "|";
	} else {
		a << contents;
		b << contents;
	}
	if (tile.x == topLeft.x) {
		if (tile.y == bottomRight.y) {
			c << "\\-";
		} else {
			c << "|" << contents;
		}
	} else if (tile.y == bottomRight.y) {
		c << "--";
	} else {
		c << contents << contents;
	}
	if (tile == bottomRight) {
		c << "/";
	} else if (tile.x == bottomRight.x) {
		c << "|";
	} else if (tile.y == bottomRight.y) {
		c << "-";
	} else {
		c << contents;
	}
	return std::make_tuple(a.str(), b.str(), c.str());
#else
	return std::make_tuple("", "", "");
#endif
}

MergeBuilder::MergeBuilder(Map::Map const& map)
{
	for (auto& xnodes : nodes)
		std::fill(xnodes.begin(), xnodes.end(), nullptr);
	loadMap(map);
	performMerges();
}
MergeBuilder::~MergeBuilder()
{
	// Use a set because we have duplicate pointers
	std::set<MergeNode*> pointrs;
	for (auto& xnodes : nodes) {
		pointrs.insert(xnodes.begin(), xnodes.end());
	}
	for (MergeNode* node : pointrs) {
		delete node;
	}
}

inline
void MergeBuilder::loadMap(Map::Map const& map)
{
	const auto& mapNodes = map.nodes;
	for (int x = 0; x < xsize; x++) {
		const auto& xnodes = mapNodes[x];
		for (int y = 0; y < ysize; y++) {
			const auto& node = xnodes[y];
			// Filter out walls the player can never hit
			if (node.wall && !node.visibleWall) {
				// Make sure corners are included
				byte i = 0;
				for (Map::Node *neighbour : node.neighbours) {
					if (neighbour != nullptr && neighbour->visibleWall) {
						i++;
					}
				}
				if (i < 2)
					continue;
			}
			nodes[x][y] = new MergeNode(node);
		}
	}
}
inline
int MergeBuilder::verticalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	while (y + i < ysize) { // Ensure we stay within bounds
		MergeNode *other = nodes[x][y + i];
		// Check if the node is the same kind as us. (includes a nullptr check)
		if (!node->compatible(other))
			break;

		// Eat the node
		node->height++;
		delete other;
		nodes[x][y + i] = node;

		i++;
	}
	return i - 1;
}

inline
void MergeBuilder::horizontalMerge(int x, int y)
{
	MergeNode *node = nodes[x][y];
	int i = 1;
	// Consume horizontally
	while (x + i < xsize) {
		// Make sure that we can consume all x tiles for our height
		for (int j = 0; j <= node->height; j++) {
			MergeNode *other = nodes[x + i][y + j];
			if (!node->compatible(other)) {
				return;
			}
		}
		node->width++;
		// Eat all nodes for our height
		for (int j = 0; j <= node->height; j++) {
			delete nodes[x + i][y + j];
			nodes[x + i][y + j] = node;
		}

		i++;
	}
}

inline
void MergeBuilder::performMerges()
{
	for (int x = 0; x < xsize; x++) {
		for (int y = 0; y < ysize; y++) {
			MergeNode *node = nodes[x][y];
			if (node == nullptr || node->done || notMergable(node->type))
				continue;
			// Merge vertically first due to how the loop is layed out.
			int yadd = verticalMerge(x, y);
			horizontalMerge(x, y);
			node->done = true;
			// yadd lets us skip consumed nodes for the next iteration of y.
			y += yadd;
		}
	}
}

std::vector<std::pair<coords, TileData*>> MergeBuilder::getTileData() const
{
	std::vector<std::pair<coords, TileData*>> ret;
	ret.reserve(xsize * ysize);
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	// SPAM
#ifdef DEBUG_MAPSPEW
	DebugOutput();
#endif

	for (coord x = 0; x < xsize; x++) {
		for (coord y = 0; y < ysize; y++) {
			MergeNode* node = nodes[x][y];
			if (node == nullptr)
				continue;
			coord nodeX = -(x - xhalf); // I don't know why this is negative, but it is.
			coord nodeY = y - yhalf;
			ret.emplace_back(coords(nodeX, nodeY), node->toTile());
		}
	}

	return ret;
}

void MergeBuilder::DebugOutput() const
{
#ifdef DEBUG_MAPSPEW
	const coord xhalf = Map::Map::halfwidth;
	const coord yhalf = Map::Map::halfheight;

	coord xcoord, ycoord;

	std::cout << " vim: nowrap listchars=" << std::endl;
	std::cout.setf(std::ios_base::right);
	std::cout.fill(' ');
	std::cout.width(3);
	std::cout << "  ";
	std::ostringstream bluh;
	bluh << "   ";
	for (byte x = 0; x < xsize; ++x) {
		std::cout.width(3);
		std::cout << -(x - xhalf);
		bluh << "***";
	}
	std::cout << std::endl;
	std::cout << bluh.str() << std::endl;
	for (byte y = 0; y < ysize; ++y) {
		ycoord = (y - yhalf);
		std::stringstream a, b, c;
		a << "   *";
		b.width(3);
		b << ycoord << '*';
		c << "   *";
		for (byte x = 0; x < xsize; ++x) {
			xcoord = -(x - xhalf);
			MergeNode *node = nodes[x][y];
			if (node == nullptr) {
				a << "   ";
				b << "   ";
				c << "   ";
				continue;
			}
			auto lines = node->toString(coords(xcoord, ycoord));
			a << std::get<0>(lines);
			b << std::get<1>(lines);
			c << std::get<2>(lines);
		}
		std::cout << a.str() << std::endl;
		std::cout << b.str() << std::endl;
		std::cout << c.str() << std::endl;
	}
#endif
}
<|endoftext|>