#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <vector>

int main(int argc, char** argv)
{
	if (argc != 3)
	{
		std::cout << "Usage: " << argv[0] << " <path to ppc disassembly> <start address in hex>" << std::endl;
		return 0;
	}
	
	std::ifstream inFile(argv[1]);
	
	if (!inFile.is_open())
	{
		std::cerr << "Could not open " << argv[1] << " for reading" << std::endl;
		return 1;
	}
	
	std::vector<std::string> lines;
	std::string line;
	while (std::getline(inFile, line))
	{
		lines.push_back(line);
	}
	
	unsigned int startAddress = 0;
	{
		std::stringstream ss;
		ss << std::hex << argv[2];
		ss >> startAddress;
	}
	
	unsigned int lowestAddress = startAddress;
	unsigned int highestAddress = startAddress;
	
	std::cout << "Stepping through PowerPC disassembly starting at address ";
	std::cout << "0x" << std::hex << std::setfill('0') << std::setw(8) << std::uppercase << startAddress << "... ";
	
	std::set<unsigned int> addressesTouched;
	std::set<unsigned int> addressesOfInterest;
	
	addressesOfInterest.insert(startAddress);
	
	while (!addressesOfInterest.empty())
	{
		std::set<unsigned int> nextAddressesOfInterest;
		
		for (unsigned int address : addressesOfInterest)
		{
			if (address < lowestAddress)
			{
				lowestAddress = address;
			}
			if (address > highestAddress)
			{
				highestAddress = address;
			}
			addressesTouched.insert(address);
			
			while (address / 4 < lines.size())
			{
				std::string& line = lines[address / 4];
				
				size_t insnPos = line.find("blr", 44);
				if (insnPos != std::string::npos)
				{
					break;
				}
				
				std::string addrMagic = "; 0x";
				size_t addrPos = line.find(addrMagic, 80);
				if (addrPos == std::string::npos)
				{
					address += 4;
					continue;
				}
				
				unsigned int interestingAddress = 0;
				std::stringstream ss;
				ss << std::hex << line.substr(addrPos + addrMagic.size(), 8);
				ss >> interestingAddress;
				
				if (addressesTouched.find(interestingAddress) == addressesTouched.end())
				{
					nextAddressesOfInterest.insert(interestingAddress);
				}
				
				address += 4;
			}
			if (address > highestAddress)
			{
				highestAddress = address;
			}
		}
		
		addressesOfInterest = nextAddressesOfInterest;
	}
	
	std::cout << "done." << std::endl;
	
	std::cout << "Address range is [";
	std::cout << "0x" << std::hex << std::setfill('0') << std::setw(8) << std::uppercase << lowestAddress;
	std::cout << ", ";
	std::cout << "0x" << std::hex << std::setfill('0') << std::setw(8) << std::uppercase << highestAddress;
	std::cout << "), " << std::dec << highestAddress - lowestAddress << " bytes" << std::endl;
	
	return 0;
}
