Index: apps/Makefile
===================================================================
RCS file: /cvsroot/rockbox/apps/Makefile,v
retrieving revision 1.107
diff -u -r1.107 Makefile
--- apps/Makefile	29 Sep 2006 16:15:06 -0000	1.107
+++ apps/Makefile	18 Oct 2006 09:03:12 -0000
@@ -55,7 +55,8 @@
 endif
 
 CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(DEFINES) -DTARGET_ID=$(TARGET_ID)	\
- -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE}
+ -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} \
+ -DTARGET_NAME=\"$(ARCHOS)\"
 
 OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC))
 OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2))
Index: apps/SOURCES
===================================================================
RCS file: /cvsroot/rockbox/apps/SOURCES,v
retrieving revision 1.52
diff -u -r1.52 SOURCES
--- apps/SOURCES	12 Oct 2006 20:45:31 -0000	1.52
+++ apps/SOURCES	18 Oct 2006 09:03:12 -0000
@@ -29,6 +29,7 @@
 tree.c
 tagtree.c
 filetree.c
+scrobbler.c
 
 screen_access.c
 gui/buttonbar.c
Index: apps/main.c
===================================================================
RCS file: /cvsroot/rockbox/apps/main.c,v
retrieving revision 1.190
diff -u -r1.190 main.c
--- apps/main.c	5 Oct 2006 10:07:00 -0000	1.190
+++ apps/main.c	18 Oct 2006 09:03:12 -0000
@@ -66,6 +66,7 @@
 #include "string.h"
 #include "splash.h"
 #include "eeprom_settings.h"
+#include "scrobbler.h"
 
 #if (CONFIG_CODEC == SWCODEC)
 #include "playback.h"
@@ -455,7 +456,8 @@
     status_init();
     playlist_init();
     tree_init();
-        
+    scrobbler_init();
+
     /* No buffer allocation (see buffer.c) may take place after the call to
        audio_init() since the mpeg thread takes the rest of the buffer space */
     mp3_init( global_settings.volume,
Index: apps/misc.c
===================================================================
RCS file: /cvsroot/rockbox/apps/misc.c,v
retrieving revision 1.66
diff -u -r1.66 misc.c
--- apps/misc.c	29 Sep 2006 06:22:43 -0000	1.66
+++ apps/misc.c	18 Oct 2006 09:03:12 -0000
@@ -45,6 +45,7 @@
 #include "font.h"
 #include "splash.h"
 #include "tagcache.h"
+#include "scrobbler.h"
 #ifdef HAVE_MMC
 #include "ata_mmc.h"
 #endif
@@ -618,6 +619,7 @@
             if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED))
 #endif
             {
+                scrobbler_flush_cache();
                 system_flush();
                 usb_screen();
                 system_restore();
Index: apps/playback.c
===================================================================
RCS file: /cvsroot/rockbox/apps/playback.c,v
retrieving revision 1.365
diff -u -r1.365 playback.c
--- apps/playback.c	17 Oct 2006 12:56:22 -0000	1.365
+++ apps/playback.c	18 Oct 2006 09:03:12 -0000
@@ -272,6 +272,9 @@
 #ifdef PLAYBACK_VOICE
 extern struct codec_api ci_voice;
 
+/* Play time of the previous track */
+unsigned long prev_track_elapsed;
+
 static volatile bool voice_thread_start;
 static volatile bool voice_is_playing;
 static volatile bool voice_codec_loaded;
@@ -1630,6 +1633,8 @@
 {
     struct event ev;
 
+    prev_track_elapsed = CUR_TI->id3.elapsed;
+
     if (ci.seek_time)
         codec_seek_complete_callback();
 
@@ -2909,6 +2914,11 @@
     track_changed_callback = handler;
 }
 
+unsigned long audio_prev_elapsed(void)
+{
+    return prev_track_elapsed;
+}
+
 static void audio_stop_codec_flush(void)
 {
     ci.stop_codec = true;
Index: apps/scrobbler.c
===================================================================
RCS file: apps/scrobbler.c
diff -N apps/scrobbler.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/scrobbler.c	18 Oct 2006 09:03:12 -0000
@@ -0,0 +1,246 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:$
+ *
+ * Copyright (C) 2006 Robert Keevil
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+/*
+Audioscrobbler spec at:
+http://www.audioscrobbler.net/wiki/Portable_Player_Logging
+*/
+
+#include "file.h"
+#include "sprintf.h"
+#include "playback.h"
+#include "logf.h"
+#include "id3.h"
+#include "kernel.h"
+#include "audio.h"
+#include "buffer.h"
+#include "settings.h"
+
+#ifndef SIMULATOR
+#include "ata.h"
+#endif
+
+#ifdef CONFIG_RTC
+#include "time.h"
+#include "timefuncs.h"
+#endif
+
+#include "scrobbler.h"
+
+#define SCROBBLER_VERSION "1.0"
+
+#ifdef CONFIG_RTC
+#define SCROBBLER_FILE "/.scrobbler.log"
+#else
+#define SCROBBLER_FILE "/.scrobbler-timeless.log"
+#endif
+
+/* increment this on any code change that effects output */
+/* replace with CVS Revision keyword? */
+#define SCROBBLER_REVISION "1.0"
+
+#define SCROBBLER_MAX_CACHE 32
+/* longest entry I've had is 323, add a safety margin */
+#define SCROBBLER_CACHE_LEN 512
+
+static char* scrobbler_cache;
+
+static int scrobbler_fd = -1;
+static int cache_pos;
+static struct mp3entry scrobbler_entry;
+static bool pending = false;
+static bool scrobbler_initialised = false;
+#ifdef CONFIG_RTC
+static time_t timestamp;
+#else
+static unsigned long timestamp;
+#endif
+
+/* Crude work-around for Archos Sims - return a set amount */
+#if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
+unsigned long audio_prev_elapsed(void)
+{
+    return 120000;
+}
+#endif
+
+static void write_cache(void)
+{
+    int i;
+
+    scrobbler_fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND);
+    if(scrobbler_fd >= 0)
+    {
+        logf("SCROBBLER: writing %d entries", cache_pos); 
+
+        for ( i=0; i < cache_pos; i++ )
+        {
+            logf("SCROBBLER: write %d", i);
+            fdprintf(scrobbler_fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i));
+        }
+        close(scrobbler_fd);
+    }
+    else
+    {
+        logf("SCROBBLER: error writing file");
+    }
+
+    cache_pos = 0;
+    scrobbler_fd = -1;
+}
+
+static void add_to_cache(void)
+{
+/* using HAVE_MMC to check for Ondios - anything better to use? */
+#if defined(SIMULATOR) || defined(IPOD_NANO) || defined(HAVE_MMC)
+    if ( cache_pos >= SCROBBLER_MAX_CACHE )
+#else
+    if ( ( cache_pos >= SCROBBLER_MAX_CACHE ) || ( ata_disk_is_active() ) )
+#endif
+        write_cache();
+
+    int ret;
+    char rating = 'S'; /* Skipped */
+
+    logf("SCROBBLER: add_to_cache[%d]", cache_pos);
+
+    if ( audio_prev_elapsed() >
+        (scrobbler_entry.length/2) )
+        rating = 'L'; /* Listened */
+
+    if (scrobbler_entry.tracknum > 0)
+    {
+        ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
+                SCROBBLER_CACHE_LEN,
+                "%s\t%s\t%s\t%d\t%d\t%c\t%ld\n",
+                scrobbler_entry.artist,
+                scrobbler_entry.album?scrobbler_entry.album:"",
+                scrobbler_entry.title,
+                scrobbler_entry.tracknum,
+                (int)scrobbler_entry.length/1000,
+                rating,
+                (long)timestamp);
+    } else {
+        ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
+                SCROBBLER_CACHE_LEN,
+                "%s\t%s\t%s\t\t%d\t%c\t%ld\n",
+                scrobbler_entry.artist,
+                scrobbler_entry.album?scrobbler_entry.album:"",
+                scrobbler_entry.title,
+                (int)scrobbler_entry.length/1000,
+                rating,
+                (long)timestamp);
+    }
+
+    if ( ret >= SCROBBLER_CACHE_LEN )
+    {
+        logf("SCROBBLER: entry too long:");
+        logf("SCROBBLER: %s", scrobbler_entry.path);
+    } else
+        cache_pos++;
+}
+
+void scrobbler_change_event(struct mp3entry *id)
+{
+    /* add entry using the previous scrobbler_entry and timestamp */
+    if (pending)
+        add_to_cache();
+
+    /*  check if track was resumed > %50 played
+        check for blank artist or track name */
+    if ((id->elapsed > (id->length/2)) ||
+        (!id->artist ) || (!id->title ) )
+    {
+        pending = false;
+        logf("SCROBBLER: skipping file %s", id->path);
+    }
+    else
+    {
+        copy_mp3entry(&scrobbler_entry, id);
+#ifdef CONFIG_RTC
+        timestamp = mktime(get_time());
+#else
+        timestamp = 0;
+#endif
+        pending = true;
+    }
+}
+
+int scrobbler_init(void)
+{
+    if(!global_settings.audioscrobbler)
+        return -1;
+
+    scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
+
+    scrobbler_fd = open(SCROBBLER_FILE, O_RDONLY);
+    if(scrobbler_fd < 0)
+    {
+        scrobbler_fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT);
+        if(scrobbler_fd >= 0)
+        {
+            fdprintf(scrobbler_fd, "#AUDIOSCROBBLER/%s\n", SCROBBLER_VERSION);
+            fdprintf(scrobbler_fd, "#TZ/UNKNOWN\n");
+#ifdef CONFIG_RTC
+            fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s\n",
+                TARGET_NAME, SCROBBLER_REVISION);
+#else
+            fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s Timeless\n",
+                TARGET_NAME, SCROBBLER_REVISION);
+#endif
+            close(scrobbler_fd);
+        }
+        else
+        {
+            logf("SCROBBLER: cannnot create log file");
+            return -1;
+        }
+    }
+    close(scrobbler_fd);
+    scrobbler_fd = -1;
+
+    audio_set_track_changed_event(&scrobbler_change_event);
+    cache_pos = 0;
+    pending = false;
+    scrobbler_initialised = true;
+
+    return 1;
+}
+
+void scrobbler_flush_cache(void)
+{
+    if (scrobbler_initialised)
+    {
+        /* check that the last add_to_cache didn't fail */
+        if (cache_pos)
+            write_cache();
+
+        pending = false;
+    }
+}
+
+void scrobbler_shutdown(void)
+{
+    scrobbler_flush_cache();
+    
+    if (scrobbler_initialised)
+    {
+        audio_set_track_changed_event(NULL);
+        scrobbler_initialised = false;
+    }
+}
Index: apps/scrobbler.h
===================================================================
RCS file: apps/scrobbler.h
diff -N apps/scrobbler.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/scrobbler.h	18 Oct 2006 09:03:12 -0000
@@ -0,0 +1,23 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id:$
+ *
+ * Copyright (C) 2006 Robert Keevil
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+ 
+void scrobbler_change_event(struct mp3entry *id);
+int scrobbler_init(void);
+void scrobbler_flush_cache(void);
+void scrobbler_shutdown(void);
Index: apps/settings.c
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.c,v
retrieving revision 1.428
diff -u -r1.428 settings.c
--- apps/settings.c	11 Oct 2006 09:12:23 -0000	1.428
+++ apps/settings.c	18 Oct 2006 09:03:13 -0000
@@ -665,6 +665,8 @@
     {2, S_O(fm_region), 0, "fm_region", "eu,us,jp,kr" },
 #endif
 
+    {1, S_O(audioscrobbler), false, "Last.fm Logging", off_on},
+
     /* If values are just added to the end, no need to bump the version. */
     /* new stuff to be added at the end */
 
Index: apps/settings.h
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.h,v
retrieving revision 1.247
diff -u -r1.247 settings.h
--- apps/settings.h	9 Oct 2006 10:54:16 -0000	1.247
+++ apps/settings.h	18 Oct 2006 09:03:13 -0000
@@ -498,6 +498,7 @@
 #ifdef CONFIG_TUNER
     int fm_region;
 #endif
+    bool audioscrobbler; /* Audioscrobbler logging  */
 
 };
 
Index: apps/settings_menu.c
===================================================================
RCS file: /cvsroot/rockbox/apps/settings_menu.c,v
retrieving revision 1.278
diff -u -r1.278 settings_menu.c
--- apps/settings_menu.c	15 Oct 2006 17:50:59 -0000	1.278
+++ apps/settings_menu.c	18 Oct 2006 09:03:13 -0000
@@ -1387,6 +1387,11 @@
                       INT, names, 3, NULL );
 }
 
+static bool audioscrobbler(void)
+{
+    return set_bool( str(LANG_AUDIOSCROBBLER), &global_settings.audioscrobbler );
+}
+
 static bool codepage_setting(void)
 {
     static const struct opt_items names[] = {
@@ -1747,8 +1752,9 @@
         { ID2P(LANG_ID3_ORDER), id3_order },
         { ID2P(LANG_NEXT_FOLDER), next_folder },
 #ifdef HAVE_HEADPHONE_DETECTION
-        { ID2P(LANG_UNPLUG), unplug_menu }
+        { ID2P(LANG_UNPLUG), unplug_menu },
 #endif
+        { ID2P(LANG_AUDIOSCROBBLER), audioscrobbler}
     };
 
     bool old_shuffle = global_settings.playlist_shuffle;
Index: apps/tree.c
===================================================================
RCS file: /cvsroot/rockbox/apps/tree.c,v
retrieving revision 1.449
diff -u -r1.449 tree.c
--- apps/tree.c	5 Oct 2006 10:07:02 -0000	1.449
+++ apps/tree.c	18 Oct 2006 09:03:13 -0000
@@ -65,6 +65,7 @@
 #include "yesno.h"
 #include "gwps-common.h"
 #include "eeprom_settings.h"
+#include "scrobbler.h"
 
 /* gui api */
 #include "list.h"
@@ -1378,6 +1379,7 @@
 /* These two functions are called by the USB and shutdown handlers */
 void tree_flush(void)
 {
+    scrobbler_shutdown();
     tagcache_shutdown();
     playlist_shutdown();
 
@@ -1439,4 +1441,5 @@
     }
 #endif
     tagcache_start_scan();
+    scrobbler_init();
 }
Index: apps/lang/english.lang
===================================================================
RCS file: /cvsroot/rockbox/apps/lang/english.lang,v
retrieving revision 1.285
diff -u -r1.285 english.lang
--- apps/lang/english.lang	9 Oct 2006 11:22:42 -0000	1.285
+++ apps/lang/english.lang	18 Oct 2006 09:03:15 -0000
@@ -9940,3 +9940,17 @@
     *: "Random"
   </voice>
 </phrase>
+<phrase>
+  id: LANG_AUDIOSCROBBLER
+  desc: "Last.fm Log" in the playback menu
+  user:
+  <source>
+    *: "Last.fm Log (reboot required)"
+  </source>
+  <dest>
+    *: "Last.fm Log (reboot required)"
+  </dest>
+  <voice>
+    *: "Last.fm Log (reboot required)"
+  </voice>
+</phrase>
Index: firmware/mpeg.c
===================================================================
RCS file: /cvsroot/rockbox/firmware/mpeg.c,v
retrieving revision 1.361
diff -u -r1.361 mpeg.c
--- firmware/mpeg.c	16 Sep 2006 16:18:07 -0000	1.361
+++ firmware/mpeg.c	18 Oct 2006 09:03:16 -0000
@@ -108,6 +108,9 @@
 static unsigned int current_track_counter = 0;
 static unsigned int last_track_counter = 0;
 
+/* Play time of the previous track */
+unsigned long prev_track_elapsed;
+
 #ifndef SIMULATOR
 static int track_read_idx = 0;
 static int track_write_idx = 0;
@@ -1056,6 +1059,9 @@
 {
     DEBUGF("Track change\n");
 
+    struct trackdata *track = get_trackdata(0);
+    prev_track_elapsed = track->id3.elapsed;
+
 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
     /* Reset the AVC */
     sound_set_avc(-1);
@@ -1072,6 +1078,11 @@
     current_track_counter++;
 }
 
+unsigned long audio_prev_elapsed(void)
+{
+    return prev_track_elapsed;
+}
+
 #ifdef DEBUG
 void hexdump(const unsigned char *buf, int len)
 {
Index: firmware/common/timefuncs.c
===================================================================
RCS file: /cvsroot/rockbox/firmware/common/timefuncs.c,v
retrieving revision 1.20
diff -u -r1.20 timefuncs.c
--- firmware/common/timefuncs.c	10 Mar 2006 19:17:01 -0000	1.20
+++ firmware/common/timefuncs.c	18 Oct 2006 09:03:16 -0000
@@ -130,3 +130,38 @@
     return 0;
 #endif
 }
+
+/* mktime() code taken from lynx-2.8.5 source, written
+ by Philippe De Muyter <phdm@macqel.be> */
+time_t mktime(struct tm *t)
+{
+    short month, year;
+    time_t result;
+    static int m_to_d[12] =
+        {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+    month = t->tm_mon;
+    year = t->tm_year + month / 12 + 1900;
+    month %= 12;
+    if (month < 0)
+    {
+        year -= 1;
+        month += 12;
+    }
+    result = (year - 1970) * 365 + (year - 1969) / 4 + m_to_d[month];
+    result = (year - 1970) * 365 + m_to_d[month];
+    if (month <= 1)
+        year -= 1;
+    result += (year - 1968) / 4;
+    result -= (year - 1900) / 100;
+    result += (year - 1600) / 400;
+    result += t->tm_mday;
+    result -= 1;
+    result *= 24;
+    result += t->tm_hour;
+    result *= 60;
+    result += t->tm_min;
+    result *= 60;
+    result += t->tm_sec;
+    return(result);
+}
Index: firmware/export/audio.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/export/audio.h,v
retrieving revision 1.19
diff -u -r1.19 audio.h
--- firmware/export/audio.h	12 Oct 2006 16:51:22 -0000	1.19
+++ firmware/export/audio.h	18 Oct 2006 09:03:16 -0000
@@ -141,6 +141,7 @@
 #endif
 #endif
 unsigned long audio_get_spdif_sample_rate(void);
+unsigned long audio_prev_elapsed(void);
 #if CONFIG_CODEC == SWCODEC
 /* audio encoder functions (defined in playback.c) */
 int  audio_get_encoder_id(void);
Index: firmware/include/time.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/include/time.h,v
retrieving revision 1.6
diff -u -r1.6 time.h
--- firmware/include/time.h	2 Mar 2006 13:07:11 -0000	1.6
+++ firmware/include/time.h	18 Oct 2006 09:03:16 -0000
@@ -20,8 +20,7 @@
   int	tm_isdst;
 };
 
-#if defined(SIMULATOR) && !defined(_TIME_T_DEFINED) && !defined(_TIME_T_DECLARED)
-/* for non-win32 simulators */
+#if !defined(_TIME_T_DEFINED) && !defined(_TIME_T_DECLARED)
 typedef long time_t;
 
 /* this define below is used by the mingw headers to prevent duplicate
Index: firmware/include/timefuncs.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/include/timefuncs.h,v
retrieving revision 1.3
diff -u -r1.3 timefuncs.h
--- firmware/include/timefuncs.h	17 Aug 2004 01:45:48 -0000	1.3
+++ firmware/include/timefuncs.h	18 Oct 2006 09:03:16 -0000
@@ -5,7 +5,7 @@
  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
  *                     \/            \/     \/    \/            \/
- * $Id: timefuncs.h,v 1.3 2004-08-17 01:45:48 amiconn Exp $
+ * $Id: timefuncs.h,v 1.3 2004/08/17 01:45:48 amiconn Exp $
  *
  * Copyright (C) 2002 by Linus Nielsen Feltzing
  *
@@ -27,5 +27,6 @@
 struct tm *get_time(void);
 int set_time(const struct tm *tm);
 bool valid_time(const struct tm *tm);
+time_t mktime(struct tm *t);
 
 #endif /* _TIMEFUNCS_H_ */
