avatar
Untitled

Guest 13 4th Nov, 2019

                                           
                         /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~(Separate library's .h file, compiled with /clr)~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/




#pragma once
#pragma warning(disable: 4002)
#pragma warning(disable: 4003)
#pragma warning(disable: 4005)
#pragma warning(disable: 4302)
#pragma warning(disable: 4311)
#pragma warning(disable: 4312)
#pragma warning(disable: 4996) //warning C4996: 'mbstowcs': This function or variable may be unsafe. Consider using mbstowcs_s instead.


#define var auto
#define null nullptr

typedef unsigned char byte;
typedef const wchar_t* ustring;



#ifdef CPPCLI



//#include <stdio.h>
//#include <windows.h>
//#include <exception>

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;

typedef char int8;
typedef short int16;
typedef int int32;
typedef long long int64;

typedef char ANSICHAR;
typedef wchar_t WIDECHAR;
typedef wchar_t TCHAR;
typedef unsigned char UTF8CHAR;
typedef unsigned short UCS2CHAR;
typedef unsigned short UTF16CHAR;
typedef unsigned int UTF32CHAR;


#define GETMGDSTR(str) ANSI_TO_TCHAR( static_cast<char*>(Marshal::StringToHGlobalAnsi(str).ToPointer()) )

static wchar_t* ANSI_TO_TCHAR(const char* text)
{
	const size_t size = strlen(text) + 1;
	wchar_t* wText = new wchar_t[size];
	mbstowcs(wText, text, size);
	System::Runtime::InteropServices::Marshal::FreeHGlobal(System::IntPtr((char*)text));
	return wText;
}


typedef unsigned int UPTRINT;
typedef unsigned int PTRINT;

#include "enums.h"

#endif



#define IMPL_MACROS 1
#include "includes.h"
#define IMPL_MACROS 0




typedef void (*fnptr_gPrintMessage)(ustring msg, int timeVisible_s);
typedef void (*fnptr_gPrintClassName)(class _AActor* actor, int timeVisible_s);




#define MACRO_FUNCTION_TYPEDEF_PARAMS(xtype, xname, xparams) typedef xtype (* fn_##xclass##_##xname )( class xclass##* actor, xparams );
#define MACRO_FUNCTION_TYPEDEF_PURE(xtype, xname) typedef xtype (* fn_##xclass##_##xname )( class xclass##* actor );

AACTOR_FUNCTION_TYPEDEF_PURE(float*, GetActorLocation);
AACTOR_FUNCTION_TYPEDEF_PARAMS(void, SetActorLocation, float*, newLocation);






class Vtable_methods
{
public:

#define MACRO_FIELD_FNPTR_IMPL(xtype, xname) get_##xclass##_##xname m_get_##xclass##_##xname; set_##xclass##_##xname m_set_##xclass##_##xname;

AACTOR_FUNCTION_FNPTR_IMPL(float*, GetActorLocation);
	AACTOR_FUNCTION_FNPTR_IMPL(void, SetActorLocation);



};











class _AActor
{

public:
	void* nativeActorObj = 0;
	void* nextActorInLinkedList = 0;
	Vtable_methods* methods;

public:
	fnptr_gPrintMessage gPrintMessage;
	fnptr_gPrintClassName gPrintClassName;


};



#ifdef CPPCLI

namespace CppCli
{
	using namespace System;
	using namespace System::Collections::Generic;
	using namespace System::IO;
	using namespace System::Runtime::InteropServices;


	ref class AActor
	{

	private:
		_AActor* native;
		void _printMsg(std::wstring msg, int time_s) { native->gPrintMessage(msg.c_str(), time_s); }

	public:
		AActor(_AActor* native) { this->native = native; }

	public:

		void PrintMessage(String^ msg)
		{
			var p = GETMGDSTR(msg);
			std::wstring str = p;
			_printMsg(str, 9);
			delete p;
		}

		void PrintClassName()
		{
			native->gPrintClassName(native, 9);
		}



	public:

#define MACRO_FUNCTION_WRAPPER_GETVEC3_PURE(xtype, xname) array<float^>^ xname() { var ptr = (native->methods->##m_fn_##xclass##_##xname##(native)); var v = gcnew array<float^>(3){ptr[0], ptr[1], ptr[2]}; delete[] ptr; return v; }

#define MACRO_FUNCTION_WRAPPER_SET_1PARAM_P1ISVEC(xtype, xname, x11, x12) void xname(array<float^>^ floats) { var ptr = new float[3]; ptr[0]=*floats[0]; ptr[1]=*floats[1]; ptr[2]=*floats[2]; (native->methods->##m_fn_##xclass##_##xname##(native, ptr)); }

AACTOR_FUNCTION_WRAPPER_GETVEC3_PURE(float*, GetActorLocation);
	AACTOR_FUNCTION_WRAPPER_SET_1PARAM_P1ISVEC(void, SetActorLocation, float*, newLocation);

		






	};


}
#endif




#define string const TCHAR*
#define var auto
#define null nullptr




namespace CppCli
{
#ifdef CPPCLI
	using namespace System;
	using namespace System::Collections::Generic;
	using namespace System::IO;
	using namespace System::Runtime::InteropServices;

	using namespace System::CodeDom;
	using namespace System::Globalization;
	using namespace System::CodeDom::Compiler;
	using namespace System::Reflection;
	using namespace System::Linq;
	using namespace Microsoft::CSharp;

	#include <msclr/gcroot.h>
	#define gcroot msclr::gcroot

	static gcroot<Assembly^> assembly;
	static gcroot<Type^> Game1;
	static gcroot<MethodInfo^> Init_Method;
	static gcroot<MethodInfo^> Update_Method;
	static gcroot<Object^> gameInst;

	ref class Mygame
	{
		List<Object^>^ actors;


	public:


		static Assembly^ CompileSourceCodeDom(List<String^>^ lines2)
		{
			CodeDomProvider^ cpd = gcnew CSharpCodeProvider();
			var cp = gcnew CompilerParameters();
			cp->ReferencedAssemblies->Add("System.dll");
			cp->ReferencedAssemblies->Add("System.Core.dll");
			cp->GenerateExecutable = false;
			cp->GenerateInMemory = true;


			var di = gcnew DirectoryInfo("D:\\dev\\MyGameCs");
			var _files = di->GetFiles("*.cs", SearchOption::AllDirectories);
			var files = gcnew List<String^>();
			for each (var f in _files)
			{
				if (!f->FullName->Contains("Program.cs"))
					files->Add(f->FullName);
			}

			CompilerResults^ cr = cpd->CompileAssemblyFromFile(cp, files->ToArray());

			if (cr->Errors->Count > 0)
				for each (var error in cr->Errors)
				{ lines2->Add(error->ToString()); }

			return cr->CompiledAssembly;
		}

		static void _Init(Assembly^ assembly, List<String^>^ lines2, List<Object^>^ sceneActors)
		{
			Game1 = assembly->GetType("game1.Game1");
			Init_Method = Game1->GetMethod("Init");
			Update_Method = Game1->GetMethod("Update");
			lines2->Add("got Init method..");
			gameInst = assembly->CreateInstance("game1.Game1");
			lines2->Add("got inst..");
			Init_Method->Invoke(gameInst, BindingFlags::InvokeMethod, null, gcnew array<Object^>(1) { sceneActors }, CultureInfo::CurrentCulture);
			lines2->Add("Init() call happened");
		}



		int Init(_AActor* root)
		{
			actors = gcnew List<Object^>();

			var _root = gcnew AActor(root);
			actors->Add(_root);
			var node = root;

			while (node->nextActorInLinkedList != null)
			{
				node = (_AActor*)node->nextActorInLinkedList;
				actors->Add(gcnew AActor(node));
			}

			List<String^>^ lines2 = gcnew List<String^>();
			lines2->Add("Run()..");

			try {
				assembly = CompileSourceCodeDom(lines2);
				_Init(assembly, lines2, actors);
			}
			catch(Exception^ e) { if(e->InnerException != null) lines2->Add(String::Concat(e->InnerException->StackTrace, e->InnerException->Message)); }
			File::WriteAllLines("-----------------\\out"+".txt", lines2->ToArray());

			return 0;
		}

		int Update()
		{
			List<String^>^ lines2 = gcnew List<String^>();
			lines2->Add("Update()..");

			try {
				Update_Method->Invoke(gameInst, BindingFlags::InvokeMethod, null, null, CultureInfo::CurrentCulture);
			}
			catch (Exception^ e) { if (e->InnerException != null) lines2->Add(String::Concat(e->InnerException->StackTrace, e->InnerException->Message)); }
			File::WriteAllLines("--------------\\out4" + ".txt", lines2->ToArray());

			return 0;
		}





	};

	static gcroot<Mygame^> mygame;

	extern "C" int __stdcall Init(_AActor * root)
	{
		mygame = gcnew Mygame;
		return mygame->Init(root);
	}
	extern "C" int __stdcall Update()
	{
		return mygame->Update();
	}
#else
	extern "C" int __stdcall Init(_AActor* root);
	extern "C" int __stdcall Update();
#endif
}

#ifndef CPPCLI


void _gPrintMessage(const TCHAR* msg, int timeVisible_s)
{
	engine->AddOnScreenDebugMessage(-1, timeVisible_s, FColorList::Green, msg);
}

void _gPrintClassName(_AActor* actor, int timeVisible_s)
{
	engine->AddOnScreenDebugMessage(-1, timeVisible_s, FColorList::Green, *GetNameSafe((UObjectBaseUtility*)actor->nativeActorObj));
}

void* _ustringlist_get_array_fname(ustringlist* v)
{
	TArray<FName>* arr = new TArray<FName>();
	var node = v;
	while (node->next)
	{
		arr->Add(node->current);
		node = node->next;
	}
	return (void*)arr;
}

ustringlist* _ustringlist_converted(TArray<FName>& sourceList)
{
	var node = new ustringlist(_ustringlist_get_array_fname);
	var rootNode = node;
	for (auto item : sourceList)
	{
		node->current = *(item.GetPlainNameString());
		node->next = new ustringlist(_ustringlist_get_array_fname);
		node = node->next;
	}
	return rootNode;
}




#define MACRO_FUNCTION_GETVEC3_PURE(xtype, xname) float* _fn_##xclass##_##xname##(##xclass##* actor) { float* ptr = new float[3]; if(!((##uclass##*)(actor->nativeActorObj))->GetRootComponent()) { ptr[0]=-1.f; ptr[1]=-1.f; ptr[2]=-1.f; return ptr; }  var v = ((##uclass##*)(actor->nativeActorObj))->##xname##(); ptr[0] = v.X; ptr[1] = v.Y; ptr[2] = v.Z; return ptr; }

#define MACRO_FUNCTION_SET_1PARAM_P1ISVEC(xtype, xname, x11, x12) void _fn_##xclass##_##xname##(##xclass##* actor, x11 x12) { /*cli -> c++ land*/ float* ptr = x12; FVector vec(ptr[0], ptr[1], ptr[2]); delete[] ptr; ((##uclass##*)(actor->nativeActorObj))->##xname##(vec); }

AACTOR_FUNCTION_GETVEC3_PURE(float*, GetActorLocation);
	AACTOR_FUNCTION_SET_1PARAM_P1ISVEC(void, SetActorLocation, float*, newLocation);





#endif

//#undef string
//#undef var
#define null 0








/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(Main game class file)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/




#include "AGame.h"
//#include <windows.h>
#include "Engine/Engine.h"
#include "UObject/ConstructorHelpers.h"
#include "Engine/Blueprint.h"
#include "D:\Epic Games\UE_4.23\Engine\Source\Runtime\Engine\Public\EngineUtils.h"
#include <exception>
#include <list>
#include <iostream>
#include <fstream>
#include <ostream>



UEngine* engine;
#include "../CppCli/CppCli.h"
_AActor* root = null;
Vtable_methods* vtable_methods = null;
std::list<_AActor*> actorRefList;




void AAGame::InitCSharp()
{

	if (vtable_methods == null)
	{
		vtable_methods = new Vtable_methods();

		#define VTABLE_MACRO_FUNCTION(xname) vtable_methods->m_fn_##xclass##_##xname = _fn_##xclass##_##xname##;

		VTABLE_AACTOR_FUNCTION(GetActorLocation);
		VTABLE_AACTOR_FUNCTION(SetActorLocation);

	}
	

	if (root == null)
	{
		root = new _AActor();
		root->methods = vtable_methods;
		root->gPrintMessage = _gPrintMessage;
		root->gPrintClassName = _gPrintClassName;
	}
	root->nativeActorObj = this;


	//delete data and get it fresh from the new BeginPlay() session
	for (auto a : actorRefList)
	{ delete a; }
	actorRefList.clear();
	

	_AActor* next = root;

	for (TActorIterator<ACharacter> Itr(GetWorld()); Itr; ++Itr)
	{
		var item = *Itr;

		_AActor* a = new _AActor();
		a->nativeActorObj = item;
		a->methods = vtable_methods;
		a->gPrintMessage = _gPrintMessage;
		a->gPrintClassName = _gPrintClassName;
		next->nextActorInLinkedList = a;
		next = (_AActor*)a;
		actorRefList.push_back((_AActor*)a);
	}

	CppCli::Init(root);

}



// Sets default values
AAGame::AAGame()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AAGame::BeginPlay()
{
	Super::BeginPlay();

	engine = GetWorld()->GetGameInstance()->GetEngine();

	InitCSharp();
}

// Called every frame
void AAGame::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	CppCli::Update();

}





/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(C# class file)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/


using System;
using System.Reflection;
using System.Globalization;
using System.Linq;
using System.Collections.Generic;


namespace game1
{
	public partial class Game1
	{
	
		//TODO
		public class UActorComponent{}
		public class USceneComponent{}
		
		
		
		public class AActor
		{
			public object refclass;
			public int id;
			
			public void PrintMessage(string msg){ _PrintMessage_Method.Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{msg}, CultureInfo.CurrentCulture); }
			public void PrintClassName(){ _PrintClassName_Method.Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
			
			public static MethodInfo _PrintMessage_Method;
			public static MethodInfo _PrintClassName_Method;
			public static bool actors_methodinfo_valid = false;
			public static Dictionary<string, MethodInfo> getters;
			public static Dictionary<string, MethodInfo> setters;
			public static Dictionary<string, MethodInfo> functions;
			public static string[] fields = {
				/*~~~~~~~~~~~~~~~~~~~~~~~~~~fields~~~~~~~~~~~~~~~~~~~~~~~~~*/
				"AutoReceiveInput","bActorSeamlessTraveled","bAllowTickBeforeBeginPlay","bAlwaysRelevant","bAutoDestroyWhenFinished",
				"bBlockInput","bCanBeDamaged","bCollideWhenPlacing","bEnableAutoLODGeneration","bExchangedRoles","bFindCameraComponentWhenViewTarget","bHidden",
				"bHiddenEd","bHiddenEdLayer","bHiddenEdLevel","bIgnoresOriginShifting","bIsEditorOnlyActor","bIsEditorPreviewActor","bLockLocation",
				"BlueprintCreatedComponents","bActorLabelEditable","bAllowReceiveTickEventOnDedicatedServer","bEditable","bCanBeInCluster",
				"bListedInSceneOutliner","bNetCheckedInitialPhysicsState","bOptimizeBPComponentData","bRelevantForLevelBounds","bRelevantForNetworkReplays",
				"bReplayRewindable","bReplicateMovement","bReplicates","ControllingMatineeActors","NetDriverName","bNetLoadOnClient","bNetStartup",
				"bNetTemporary","bOnlyRelevantToOwner","bOnlyRelevantToOwner","bRunConstructionScriptOnDrag","Children","CreationTime","CurrentTransactionAnnotation",
				"CustomTimeDilation","DetachFence","GroupActor","HiddenEditorViews","InitialLifeSpan","InputComponent","InputPriority",
				"Instigator","Layers","MinNetUpdateFrequency","NetCullDistanceSquared","NetDormancy","NetPriority","NetTag","NetUpdateFrequency",
				/*--------------------------------------------------------------------------------------------------------------------------------*/
				"ReplicatedMovement","Role","SpawnCollisionHandlingMethod","SpriteScale","Tags","PivotOffset","ReplicatedComponents","RootComponent","TimerHandle_LifeSpanExpired",
				/*~~~~~~~~~~~~~~~~~~~~~~~~~~functions~~~~~~~~~~~~~~~~~~~~~~~~~*/
				/*"ActorHasTag",*/
				"GetActorForwardVector","GetActorLocation","GetActorQuat","GetActorRelativeScale3D","GetActorRightVector","GetActorRotation","GetActorScale","GetActorScale3D",
				"SetActorLocation",
				//TODO: W
			};
			
			public void writeToFile(MethodInfo[] methods)
			{
				List<string> lines = new List<string>();
				foreach(var m in methods)
					lines.Add("method: " + m.Name);
				System.IO.File.WriteAllLines(@"-----------\out2.txt", lines);
			}
			
			

			
			
			
			public AActor(object refclass, int id)
			{
				this.id = id;
				this.refclass = refclass;
				actors.Add(this);
				
				if(!actors_methodinfo_valid)
				{
					actors_methodinfo_valid = true;
					getters = new Dictionary<string, MethodInfo>();
					setters = new Dictionary<string, MethodInfo>();
					functions = new Dictionary<string, MethodInfo>();
					
					var methods = refclass.GetType().GetMethods();
					
					_PrintMessage_Method = methods.Where(x=>x.Name == "PrintMessage").FirstOrDefault();
					_PrintClassName_Method = methods.Where(x=>x.Name == "PrintClassName").FirstOrDefault();
					
					writeToFile(methods);
					
					
					foreach(var m in methods)
						foreach(var f in fields)
							if(m.Name == f){functions[f] = m; break;}
					
					foreach(var m in methods)
						foreach(var f in fields)
							if(m.Name == "get_"+f){getters[f] = m; break;}
					
					foreach(var m in methods)
						foreach(var f in fields)
							if(m.Name == "set_"+f){setters[f] = m; break;}
					
				}
				
				
				PrintClassName();
				
				//this only works if there's a RootComponent
				if(GetActorLocation().Z == -1)
					PrintMessage("GetActorLocation(): { No RootComponent }");
				else
					PrintMessage("GetActorLocation(): " + GetActorLocation());
				
				
				
			}
			
			
			
			
			
			
			
						
			public void Update()
			{
				
				if(id == 1)
				{
					var mightBeNoRootComponent = GetActorLocation();
					if(mightBeNoRootComponent.Z != -1)
						SetActorLocation(GetActorLocation() + new FVector(-1, 0, 0));
				}
				
			}
			
			
			
			
			
			
			
			
			
			
			
			/*~~~~~~~~~~~~~~~~~~~~~~~~~~functions~~~~~~~~~~~~~~~~~~~~~~~~~*/
			
			
			
			public void SetActorLocation(FVector vec)
			{ functions["SetActorLocation"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{vec.vt}, CultureInfo.CurrentCulture); }
	
			public FVector GetActorLocation()
			{ return new FVector((ValueType[])functions["GetActorLocation"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture)); }
	
			//<...>
			
			
			/*~~~~~~~~~~~~~~~~~~~~~~~~~~fields~~~~~~~~~~~~~~~~~~~~~~~~~*/
			
			
			//<...>
			
			public UInt64 HiddenEditorViews 
			{ get{ return (UInt64)getters["HiddenEditorViews"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["HiddenEditorViews"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			public float InitialLifeSpan 
			{ get{ return (float)getters["InitialLifeSpan"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["InitialLifeSpan"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			public Int32 InputPriority 
			{ get{ return (Int32)getters["InputPriority"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["InputPriority"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			public float MinNetUpdateFrequency
			{ get{ return (float)getters["MinNetUpdateFrequency"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["MinNetUpdateFrequency"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			public ENetDormancy NetDormancy 
			{ get{ return (ENetDormancy)getters["NetDormancy"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["NetDormancy"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			public ENetRole Role 
			{ get{ return (ENetRole)getters["Role"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["Role"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			public ESpawnActorCollisionHandlingMethod SpawnCollisionHandlingMethod 
			{ get{ return (ESpawnActorCollisionHandlingMethod)getters["SpawnCollisionHandlingMethod"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["SpawnCollisionHandlingMethod"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			public float SpriteScale
			{ get{ return (float)getters["SpriteScale"].Invoke(refclass, BindingFlags.InvokeMethod, null, null, CultureInfo.CurrentCulture); }
				set{ setters["SpriteScale"].Invoke(refclass, BindingFlags.InvokeMethod, null, new object[]{value}, CultureInfo.CurrentCulture); } }
			
			
			//<...>
			
			
			
			
			
			
		}
		
		
		
	
	
	}
}
                      
                                       
To share this paste please copy this url and send to your friends
RAW Paste Data