Endianess (was Re: FreeBSD port - data needed)

Marc van Woerkom (van.woerkom nospam at netcologne.de)
Sat, 13 Mar 1999 04:04:33 +0100 (CET)

Earlier I posted a cdindex-0.2.0 version with additional FreeBSD support.
Alas it produced different IDs under FreeBSD than under Linux.

By comparison I found out I got the byte order wrong.
And indeed a comment in the FreeBSD CD-ROM header <sys/cdio.h> says
that logical block addresses are kept in network order.. gnii!

The solution is to use the macro ntohl() on the lba value, it
converts an unsigned long from network to host order and is usually applied
when programming sockets (<sys/param.h>).

It works - I'm able to identify Bjoerk's Post CD with this!

The corrected source is found below.

I'll fold this into the most recent linux version (cdinfo-0.4.0?)
and into a regular FreeBSD port later this day.

Regards,
Marc

///////////////////////////////////////////////////////////////////////////////
//
// cdindex.cpp
//

// system headers

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <string.h>

// CD control

#if defined(__FreeBSD__)
#include <sys/cdio.h>
#else // don't know Linux id
#include <linux/cdrom.h>
#endif

// project headers

#include "cdindex.h"
#include "md5.h"

// stupid macros

#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif

#if defined(__FreeBSD__)
#define DEFAULT_DEVICE "/dev/cd0c"
#else // don't know Linux id
#define DEFAULT_DEVICE "/dev/cdrom"
#endif

// some types

typedef unsigned char byte;

int lbas[128];

//
// calculate index
//

void CDIndex_Calculate(CDINDEX_CDINFO* pCDInfo, // Charles Simonyi's (Hungarian) notation is ugly
char DiscId[33])
{
MD5_CTX md5;
char HexTbl[] = "0123456789ABCDEF";
unsigned char digest[16];

// go!

MD5Init(&md5);

MD5Update(&md5,
(unsigned char*) pCDInfo,
sizeof(CDINDEX_CDINFO));

MD5Final(digest, &md5);

for (int i=0; i<16; i++) {
DiscId[i << 1] = HexTbl[digest[i] / 0x10];
DiscId[(i << 1) + 1] = HexTbl[digest[i] % 0x10];
}

DiscId[32] = '\0';
}

//
// OS specific CD inquiries
//

#if defined(__FreeBSD__)

int readtochdr(int fd,
int& first,
int& last)
{
struct ioc_toc_header th;

int ret = ioctl(fd,
CDIOREADTOCHEADER,
&th);

if (!ret) {
first = th.starting_track;
last = th.ending_track;
}

return ret;
}

int readtocentry(int fd,
int track,
int& lba)
{
struct ioc_read_toc_entry te;

te.starting_track = (u_char) track;
te.address_format = CD_LBA_FORMAT; // experiment and cdio.h say
// lbas are given in network order!

struct cd_toc_entry cte;

te.data = &cte;
te.data_len = sizeof(cd_toc_entry);

int ret = ioctl(fd,
CDIOREADTOCENTRYS,
&te);

if (!ret) {
assert(te.address_format == CD_LBA_FORMAT);

lba = ntohl(te.data->addr.lba); // network to host order (long)

printf("track %02d: lba = %d (%08x)\n",
(u_char) track,
lba,
lba);
}

return ret;
}

#else // don't know Linux id

/// SHAMELESSLY RIPPED FROM LINUX KERNEL

static inline void lba_to_msf(int lba,
byte* m,
byte* s,
byte* f)
{
lba += CD_BLOCK_OFFSET;
lba &= 0xffffff; /* negative lbas use only 24 bits */
*m = lba / (CD_SECS * CD_FRAMES);
lba %= (CD_SECS * CD_FRAMES);
*s = lba / CD_FRAMES;
*f = lba % CD_FRAMES;
}

/// END OF SHAMELESS RIP

int readtochdr(int fd,
int& first, // some sign of C++ :-)
int& last)
{
struct cdrom_tochdr tochdr;

int ret = ioctl(fd, CDROMREADTOCHDR, &tochdr);

if (!ret) {
first = tochdr.cdth_trk0;
last = tochdr.cdth_trk1;
}

return ret;
}

int readtocentry(int fd,
int track,
int& lba,
int& control,
int& adr)
{
struct cdrom_tocentry tocentry;
tocentry.cdte_track = track;
tocentry.cdte_format = CDROM_LBA;

int ret = ioctl(fd, CDROMREADTOCENTRY, &tocentry);
if (!ret) {
assert (tocentry.cdte_format == CDROM_LBA);
lba = tocentry.cdte_addr.lba;
control = tocentry.cdte_ctrl;
adr = tocentry.cdte_adr;
}

return ret;
}

#endif

void usage()
{
printf("usage: cdindex <options> [cdrom device]\n");
printf("\n");
printf("Options:\n");
printf(" -s Submit the given CD\n");
printf(" -g Get the information for this CD\n");
printf(" -l Print the URL used, but don't launch browser\n");
printf("\n");
printf("If no device is given, " DEFAULT_DEVICE " will be used.\n");

exit(0);
}

//
// start here
//

int main(int argc,
char* argv[])
{
#if defined(__FreeBSD__)
#else
unsigned char buf[CD_FRAMESIZE_RAW]; // who ordered this?
#endif

int ret;
int first;
int last;
int i;
int lba = 0;
int control;
int adr;
int opt_s = 0;
int opt_g = 0;
int opt_l = 0;

byte m;
byte s;
byte f;

char* device = DEFAULT_DEVICE;

CDINDEX_CDINFO cdinfo;

char id[33];
char url[255];

// go!

if (argc == 1 || (argc > 1 && strcasecmp(argv[1], "--help") == 0))
usage();

// command-line argument processing loop

for (i=1; i<argc; i++) {

if (strcmp(argv[i], "-s") == 0) {
opt_s = 1;
continue;
}

if (strcmp(argv[i], "-g") == 0) {
opt_g = 1;
continue;
}

if (strcmp(argv[i], "-l") == 0) {
opt_l = 1;
continue;
}

// special case the standalone optional final argument (device)
if (i != argc - 1)
usage();

device = argv[i];
}

int fd = open(device, O_RDONLY);

if (fd < 0) {
printf("Cannot open '%s'\n", device);
exit(0);
}

memset(&cdinfo, 0, sizeof(CDINDEX_CDINFO));

if (readtochdr(fd, first, last)) {
printf("Cannot read table of contents.\n");
close(fd);
exit(0);
}

cdinfo.FrameOffset[0] = 0;

for (i=first; i<=last; i++) {
#if defined(__FreeBSD__)
readtocentry(fd, i, lba);
// what use is msf here?
#else
readtocentry(fd, i, lba, control, adr);
lba_to_msf(lba, &m, &s, &f);
#endif

cdinfo.FrameOffset[i] = lba + 150;
}

cdinfo.FirstTrack = first;
cdinfo.LastTrack = last;

CDIndex_Calculate(&cdinfo, id);
close(fd);

if (opt_s)
sprintf(url,
"http://www.freeamp.org/cgi-bin/cdi/submit.pl?"
"id=%s&tracks=%d",
id,
(last - first) + 1);
else
sprintf(url,
"http://www.freeamp.org/cgi-bin/cdi/hget.pl?"
"id=%s",
id);

if (opt_l) { // just print the url
printf("%s\n", url);
exit(0);
}

printf("Opening netscape to: '%s'\n"
"(Please open it by hand if the browser doesn't "
"start automatically)\n",
url);

#if defined(__FreeBSD__)
execlp("netscape",
"netscape", // arg[0] is name
url,
NULL);
#else
execlp("netscape",
url,
NULL);
#endif

perror("Could not launch netscape browser");

return 1;
}

///////////////////////////////////////////////////////////////////////////////
//
// cdindex.h
//

#if !defined(___CDINDEX___H___)
#define ___CDINDEX___H___

#pragma pack(1) // FreeBSD: investigate

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;
typedef signed char sbyte;
typedef signed short sword;
typedef signed long sdword;

struct CDINDEX_CDINFO {
byte FirstTrack; // The first track on CD : normally 1
byte LastTrack; // The last track on CD: max number 99
dword FrameOffset[100]; // Track 2 is TrackFrameOffset[2] etc.
}; // Leadout Track will be TrackFrameOffset[0]

void CDIndex_Calculate(CDINDEX_CDINFO* pCDInfo,
char DiscId[17]);

// true = ok
// false = error

#if !defined(CD_BLOCK_OFFSET)
#define CD_BLOCK_OFFSET CD_MSF_OFFSET
#endif

#endif

// end of cdindex.h