public static void decompressOverMemory(Program program, List<ArtBlock> blocks,
			TaskMonitor monitor) throws Exception {
		for (ArtBlock block : blocks) {
			monitor.checkCancelled();

			Address sourceAddress = program.getMinAddress().add(block.getDataOffset());
			byte[] compressedBytes = new byte[block.getDataSize()];
			program.getMemory().getBytes(sourceAddress, compressedBytes);

			byte[] decompressedBytes = Decompressor.decompress(block.getStorageMode(),
				compressedBytes, block.getImageSize(), monitor);

			Address destinationAddress = program.getMinAddress().add(block.getImageOffset());

			//make sure block exists for bytes...
			AddressSet destinationSet = new AddressSet(destinationAddress,
				destinationAddress.add(decompressedBytes.length));
			AddressSet uncreatedSet = destinationSet.subtract(program.getMemory());
			for (AddressRange range : uncreatedSet) {
				monitor.checkCancelled();
				program.getMemory()
						.createInitializedBlock(toBlockName(range), range.getMinAddress(),
							range.getLength(), (byte) 0x00, monitor, false);
			}

			program.getMemory().setBytes(destinationAddress, decompressedBytes);
		}
	}
Decompress the block bytes and lay over program memory. 
@param program the program to overwrite
@param blocks list of ART blocks that need decompressing
@param monitor task monitor for controlling the task
@throws Exception should an error occur reading the bytes
Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/android/util/DecompressionManager.java