/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
//finds and creates strings that end with  '\n'
//@category Data Types

import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.data.StringDataType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;

public class CreateStringScript extends GhidraScript {
	private byte TERMINATOR = '\n';

	@Override
	public void run() throws Exception {

		Address addr = find(null, TERMINATOR);
		while (addr != null) {
			createString(addr);
			try {
				addr = addr.addNoWrap(1);
				addr = find(addr, TERMINATOR);
			}
			catch (AddressOverflowException e) {
				// must be at largest possible address - so we are done
			}
		}
	}

	private void createString(Address endAddr) {
		Address startAddr = findStartOfString(endAddr);
		int length = (int) endAddr.subtract(startAddr) + 1;
		if (length < 4) {
			println("Too small, Skipping " + startAddr);
			return;
		}
		try {
			myCreateAsciiString(startAddr, length);
			createLabelForString(startAddr, length);
		}
		catch (Exception e) {
			println("error creating string at " + startAddr + ". Reason: " + e.getMessage());
		}
	}

	private void myCreateAsciiString(Address startAddr, int length) throws Exception {
		currentProgram.getListing().createData(startAddr, new StringDataType(), length);
	}

	private Address findStartOfString(Address endAddr) {
		Address addr = endAddr;
		Address startAddr = endAddr;
		try {
			addr = addr.subtract(1);
			while (isAsciiAndNotTerminator(addr)) {
				startAddr = addr;
				addr = addr.subtractNoWrap(1);
			}
		}
		catch (AddressOverflowException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return startAddr;
	}

	private boolean isAsciiAndNotTerminator(Address addr) {
		try {
			byte b = currentProgram.getMemory().getByte(addr);
			if (b == TERMINATOR) {
				return false;
			}
			return (b >= 0x20 && b <= 0x7f) || b == '\n' || b == '\r' || b == '\t';
		}
		catch (MemoryAccessException e) {
			return false;
		}
	}

	private boolean createLabelForString(Address addr, int length) throws Exception {
		Listing listing = currentProgram.getListing();
		Memory memory = currentProgram.getMemory();
		Data data = listing.getDataAt(addr);
		String value = (String) data.getValue();
		if (value == null) {
			return false;
		}

		boolean needsUnderscore = true;
		StringBuffer buf = new StringBuffer();
		buf.append("s");
		byte[] bytes = new byte[length];
		try {
			memory.getBytes(addr, bytes);
		}
		catch (MemoryAccessException e) {
		}
		for (int i = 0; i < length; i++) {
			char c = (char) bytes[i];
			if (c > 0x20 && c <= 0x7f) {
				if (needsUnderscore) {
					buf.append('_');
					needsUnderscore = false;
				}
				buf.append(c);
			}
			else if (c != 0) {
				needsUnderscore = true;
			}
		}
		String newLabel = buf.toString();

		createLabel(addr, newLabel, true);
		return true;
	}
}
