Don't try to handle arbitrary surface formats inside your loop. If for some reason you can't just assume a fixed surface format, write wholly separate loops for each format. Write one loop that handles 24-bit source textures on a 24-bit output, a 32-bit source on a 24-bit output, etc., as needed. Libraries like liboil are meant to make this much easier if you need to handle a very large variety of formats, though I've no experience with that particular library myself.
You could also consider using SIMD instructions where appropriate. Also, you could probably split the screen into separate batches and use threading. You're massively under-utilizing your hardware with such a naive approach. Removing all the heavy casting might also help a lot; on a 32-bit machine, use nothing but 32-bit values in loops like these. Convertizing back and forth between 8-bit, 16-bit, and 32-bit values is not free. That also plays back into the "don't try to support arbitrary formats" inside your loop piece of advice. If you know your loop is handling only a particular format, you can make it much much faster. (16-bit or 32-bit is a wise choice; 24-bit does not align well).
There's also the obvious bit about how you're not using the GPU. Even if you want to avoid modern polygonal rendering, you could write this raycasting renderer as a compute shader. Which of course implicitly gives you SIMD and threading, but on a much larger and more capable scale than the CPU can provide.
As a bit of speculation: trying to use an early 90's rendering technique for a - by 90's standards - massive resolution (1,000,000 pixels is huge compared to the resolutions raycasting was used for, around 320x240 to 640x480, iirc). Also recall that these older games typically ran at a max of 16-bit color if not 8-bit paletted formats. The algorithm simply wasn't meant for today's market expectations. CPUs are significantly faster, of course, but I simply wouldn't be surprised if what you're trying to do is unfeasible; could be wrong.
So far as your note about void pointers, they're "represented" the same as any other pointer. There is no difference between a
void* and an
int* until you try to dereference the pointer or perform pointer arithmetic. Essentially,
int* p; int i = p; is roughly equivalent to
void* p; int i = *(int*)((char*)p + 20 * sizeof(int));, excepting any potential aliasing violations (casting pointers like you're doing is technically undefined behavior, though most compilers do exactly what you'd expect).